基本概念
从概念上讲,管道是两个进程之间的连接,一个进程的标准输出成为另一个进程的标准输入。在UNIX操作系统中,管道用于进程间通信。
- 管道只是单向通信,即我们可以这样使用管道:一个进程向管道写入数据,另一个进程从管道读取数据。管道,是内存中被视为“虚拟文件”的一块区域。
- 管道可以被创建进程及其所有子进程读写。一个进程可以写入这个“虚拟文件”或管道,另一个相关进程可以从中读取。
- 如果在某个内容写入管道之前,某个进程试图读取该内容,则该进程将挂起,直到内容被写入。
- 管道系统调用在进程的打开文件表中找到前两个可用位置,并将其分配给管道的读取和写入端。
C语言语法:
int pipe(int fds[2]);
参数:
fd[0] 将是管道读取端的fd(文件描述符)
fd[1] 将是管道写入端的fd
返回值:0表示成功,-1表示失败。
管道表现为FIFO(先进先出),管道实现类似队列数据结构。读写大小不是必须一致。在管道中,我们可以一次写入512字节,但可以一次只读取1字节。
// C program to illustrate
// pipe system call in C
#include <stdio.h>
#include <unistd.h>
#define MSGSIZE 16
char* msg1 = "hello, world #1";
char* msg2 = "hello, world #2";
char* msg3 = "hello, world #3";
int main()
{
char inbuf[MSGSIZE];
int p[2], i;
if (pipe(p) < 0)
exit(1);
/* continued */
/* write pipe */
write(p[1], msg1, MSGSIZE);
write(p[1], msg2, MSGSIZE);
write(p[1], msg3, MSGSIZE);
for (i = 0; i < 3; i++) {
/* read pipe */
read(p[0], inbuf, MSGSIZE);
printf("% s\n", inbuf);
}
return 0;
}
输出:
hello, world #1
hello, world #2
hello, world #3
父子进程共享管道
当我们在任何进程中使用fork时,文件描述符在子进程和父进程之间保持打开状态。如果我们在创建管道后调用fork,则父级和子级可以通过管道进行通信。
// C program to illustrate
// pipe system call in C
// shared by Parent and Child
#include <stdio.h>
#include <unistd.h>
#define MSGSIZE 16
char* msg1 = "hello, world #1";
char* msg2 = "hello, world #2";
char* msg3 = "hello, world #3";
int main()
{
char inbuf[MSGSIZE];
int p[2], pid, nbytes;
if (pipe(p) < 0)
exit(1);
/* continued */
if ((pid = fork()) > 0) {
write(p[1], msg1, MSGSIZE);
write(p[1], msg2, MSGSIZE);
write(p[1], msg3, MSGSIZE);
// Adding this line will
// not hang the program
// close(p[1]);
wait(NULL);
}
else {
// Adding this line will
// not hang the program
// close(p[1]);
while ((nbytes = read(p[0], inbuf, MSGSIZE)) > 0)
printf("% s\n", inbuf);
if (nbytes != 0)
exit(2);
printf("Finished reading\n");
}
return 0;
}
输出:
hello world, #1
hello world, #2
hello world, #3
(hangs) //program does not terminate but hangs
在此代码中,在完成读/写之后,父级和子级都阻塞而不是终止进程,这就是程序挂起的原因。
- 如果管道为空,并且我们调用read系统调用,那么如果没有进程打开写入端,则管道上的读取将返回EOF(返回值0)。
- 如果其他进程打开了管道进行写入,则read将阻塞,以等待新的数据,因此代码输出挂起,因为写入端的父进程和子进程没有关闭。
参考文档
[1]Kadam Patel.pipe() System call[EB/OL].https://www.geeksforgeeks.org/pipe-system-call/,2019-06-12.
扫描二维码关注公众号,回复:
12656518 查看本文章