Linux 中的管道

管道是 Linux 中常用的进程间通信方式。Linux 中管道并非只有一种,这里介绍一下 Linux 下的
三种管道。

(匿名)管道

1
2
3
#include <unistd.h>

int pipe(int pipefd[2]);

pipe()函数创建一个匿名管道,因为是匿名的,所以并不能通过名字来获取它,也就使得
非亲缘进程间通过它进行数据传递成为了不可能,不过还有命名管道(FIFO)可以用,下文会
提到。

pipe()接受一个长度为 2 的 int 型数组,如果调用成功,数据的第 0 和第 1 个元素会
分别放置管道的输入和输出文件描述符。在通过fork()等系统调用创建了新的进程的时候,
会自动复制父进程中的几乎所有信息,包括这个保存管道描述符的数组,此时,进程间就可
以通过这个管道进行通信了。

一般来说,管道是单向的,如果需要进行双向通信,可以申请两个管道。下面是一个例子:

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
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
int fd[2];
pid_t pid;
char *str = "data from parent";
char buf[100] = {0};
int count;

if (pipe(fd) == -1) {
perror("pipe error");
exit(1);
}

if ((pid = fork())) {
close(fd[0]);
if ((count = write(fd[1], str, strlen(str)+1)) < 0) {
perror("write error");
exit(3);
}
printf("write %d bytes to pipe\n", count);
close(fd[1]);
} else if (pid == 0) {
close(fd[1]);
count = read(fd[0], buf, 100);
printf("read %d bytes from pipe: %s\n", count, buf);
close(fd[0]);
} else {
perror("fork error");
exit(2);
}

return 0;
}

标准流管道

1
2
3
4
5
#include <stdio.h>

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);

popen()pclose()是成对出现的,由popen()创建的管道必须使用pclose()关闭。
第一个参数是用字符串指定的一个命令,由sh -c来执行,第二个参数也是一个字符串,
用来指定是读取(“r”)还是写入(“w”)。举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

#define BUFSIZE 1024

int main(int argc, char *argv[])
{
FILE *fp;
char buf[BUFSIZE];

fp = popen("ls", "r");
if (!fp) {
perror("popen error");
exit(1);
}

while (fgets(buf, BUFSIZE, fp)) {
printf("[popen]%s", buf);
}
pclose(fp);

return 0;
}

命令管道(FIFO)

1
2
3
4
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

命名管道与匿名管道不同,并不随着进程的退出而消失,可以使用一般的命令进行查看,如
ls 和 cat 等。还有一个同名的命令可以创建 FIFO。mkfifo()的参数都非常直观,不再
赘述。举例如下:

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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
int ret;
int fd;
char *str = "fifo test\n";

unlink("/tmp/my_fifo");
ret = mkfifo("/tmp/my_fifo", 0600);
if (ret < 0) {
perror("mkfifo");
exit(1);
} else {
printf("mkfifo succeeds\n");
}

fd = open("/tmp/my_fifo", O_WRONLY);
if (fd < 0) {
perror("open fifo failed");
exit(2);
} else {
printf("open fifo succeeds\n");
}

if (write(fd, str, strlen(str)) < 0) {
perror("write error");
exit(3);
} else {
printf("write to fifo succeeds\n");
}
close(fd);
return 0;
}

程序编译运行时,会阻塞在open()处,在另一个终端里对/tmp/my_fifo进行读取即可,
可以使用命令cat /tmp/my_fifo,即时程序会继续执行,cat命令会输出fifo test
符。

程序结束后,文件/tmp/my_fifo不会消失,可以使用rmunlink将其删除,当然也可
以在程序中将其删除,只需在最后加上一句:

1
unlink("/tmp/my_fifo");
0%