无名管道
# 无名管道
Linux 提供了多种进程间通信(IPC)方式,其中之一就是管道。管道又可分为两种:无名管道(pipe)和有名管道(fifo)。在终端上,使用无名管道可以像这样:ls ./ | less
。
# IPC机制
Linux有多种进程间通信(IPC)方式,如果没有IPC,进程只能通过文件系统互相交换信息。
常用的IPC有如下几种:
- 管道
- 信号量
- 共享内存
- 消息队列
# 管道
管道分为两种:
无名管道(pipe):是与内存中的一个索引节点相关联的两个文件描述符,没有名字,不会在文件系统中出现。
例子:下面命令使用
|
作为无名管道,将ls
的输出传给less
命令的输入。$ ls ./ | less
1有名管道(fifo):出现于文件系统当中,遵循先入先出的原则来读取数据。
例子:下面命令使用
mkfifo fifo
,创建一个有名管道名为fifo,ls
的输出传到fifo中,less从fifo中取内容。当终端2先执行
less < ./fifo
时,没有内容输出,终端1执行ls ./ > fifo
后,终端2开始输出。$ mkfifo fifo # 终端1 $ ls ./ > fifo # 终端2 $ less < ./fifo
1
2
3
4
5
6
7
pipe、fifo的名称并非硬性规定。
# 无名管道
管道是半双工,数据只能单向传递
管道只能在相关的、有共同祖先的进程之间使用
# 创建无名管道
创建管道使用pipe调用,关闭管道使用close调用
#include<unistd.h>
int pipe(int filedes[2]);
1
2
2
如果成功建立了管道,则会打开两个文件描述符并把它们的值保存在一个整数数组中。其中,filedes[0]
用于读出数据,filedes[1]
用来写入数据 。成功返回0,否则返回-1。
# 无名管道创建示例代码
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int fd[2]; /* Array for file descriptors */
if((pipe(fd)) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
printf("descriptors are %d, %d\n", fd[0], fd[1]);
close(fd[0]);
close(fd[1]);
exit(EXIT_SUCCESS);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 读写管道
从管道读出数据和写入数据,只要简单的使用read
和write
调用即可。read
从fd[0]
中读取数据,write
向fd[1]
中写入数据。
- 如果父进程向子进程发送数据,则父进程需关闭
fd[0]
并向fd[1]
写入数据,子进程需关闭fd[1]
,并从fd[0]
读取数据。 - 反之亦然
# 读写管道示例代码
cat功能的又一实现
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#define BUFSZ PIPE_BUF
void err_quit(char *msg);
int main(int argc, char *argv[])
{
int fd[2]; /* File descriptor array for the pipe */
int fdin; /* Descriptor for input file */
char buf[BUFSZ];
int pid, len;
/* Create the pipe */
if((pipe(fd)) < 0)
err_quit("pipe");
/* Fork and close the appropriate descriptors */
if((pid = fork()) < 0)
err_quit("fork");
if (pid == 0) {
/* Child is reader, close the write descriptor */
close(fd[1]);
while((len = read(fd[0], buf, BUFSZ)) > 0)
write(STDOUT_FILENO, buf, len);
close(fd[0]);
} else {
/* Parent is writer, close the read descriptor */
close(fd[0]);
if((fdin = open(argv[1], O_RDONLY)) < 0) {
perror("open");
/* Send something */
write(fd[1], "123\n", 4);
}
else {
while((len = read(fdin, buf, BUFSZ)) > 0)
write(fd[1], buf, len);
close(fdin);
}
/* Close the write descriptor */
close(fd[1]);
}
/* Reap the exit status */
waitpid(pid, NULL, 0);
exit(EXIT_SUCCESS);
}
void err_quit(char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 输出
$ ./piperw abc.txt
adcdef
1
2
2
编辑 (opens new window)
上次更新: 2023/03/31, 22:34:04
- 01
- Linux系统移植(五)--- 制作、烧录镜像并启动Linux02-05
- 03
- Linux系统移植(三)--- Linux kernel移植02-05