
什么是管道

#include <unistd.h>
功能:创建⼀⽆名管道
原型
int pipe(int fd[2]);
参数
fd:⽂件描述符数组,其中fd[0]表⽰读端, fd[1]表⽰写端
返回值:成功返回0,失败返回错误代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int fds[2];
char buf[100];
int len;
//使用pipe(fds),如果成功,则会将fds[0].fds[1]分别写入R,W文件描述符
if( pipe(fds) == -1)
perror("make pipe"), exit(1);
while(fgets(buf,100,stdin))
{
len = strlen(buf);
if(write(fds[1], buf, len) != len)
{
perror("write to pipe");
break;
}
memset(buf, 0x00, sizeof(buf));
//将buf数组中的所有100个字节全部初始化为0x00,也就是10进制的0,目的是为了清空内存区域。
//read from pipe
if(((len = read(fds[0],buf,100)) == -1))
{
perror("read from pipe");
break;
}
//write to stdout
if(write(1, buf, len) != len)
{
perror("write to stdout");
break;
}
}
return 0;
}

在 Unix/Linux 系统中,我们可以使用 fork() 系统调用来创建子进程,并通过共享管道(pipe)进行进程间通信。下面是使用 fork() 来共享管道的原理:
首先,父进程创建一个管道:
int fds[2];
pipe(fds);这将创建一个管道,其读端文件描述符存储在 fds[0],写端文件描述符存储在 fds[1]。
接着,父进程调用 fork() 创建一个子进程:
pid_t pid = fork();此时,父进程和子进程都有一个指向同一个管道的文件描述符。
在父进程中:
fds[0]。fds[1] 写入数据。在子进程中:
fds[1]。fds[0] 读取数据。通过这种方式,父进程和子进程可以通过共享的管道进行通信。父进程将数据写入管道,子进程从管道中读取数据。
这个过程中,父进程和子进程各自持有管道的一端文件描述符,确保了数据的正确流向。当父进程关闭读端,子进程关闭写端时,双方就建立了一个单向的数据流通道。
使用 fork() 创建的子进程与父进程共享相同的内存空间,这意味着它们可以共享同一个管道。这种机制使得进程间通信变得简单高效,是 Unix/Linux 系统中常见的进程间通信方式之一。
从文件描述符的角度来深入理解管道(pipe)的工作原理是非常有帮助的。让我们一步步探讨这个过程:
pipe(fds) 系统调用创建一个管道。fds[0] 是读端, fds[1] 是写端。


stdin)是文件描述符 0, 标准输出(stdout)是文件描述符 1, 标准错误(stderr)是文件描述符 2。pipe(fds) 会在进程的文件描述符表中添加两个新的文件描述符: fds[0] 和 fds[1]。fds[0] 是管道的读端, fds[1] 是管道的写端。fds[1] 写入数据时,数据会被写入内核缓冲区。fds[0] 从内核缓冲区中读取数据。fork() 创建子进程时,子进程会继承父进程的所有文件描述符,包括管道的文件描述符。fds[0] 表示不再从管道读取数据,关闭写端 fds[1] 表示不再向管道写入数据。看待管道,就如同看待⽂件⼀样!管道的使⽤和⽂件⼀致,迎合了“Linux⼀切皆⽂件思想”。

从内核的角度来理解管道(pipe)的本质是很有帮助的。让我们深入探讨一下管道在内核中的实现:
read()、write()、close() 等系统调用。总的来说,从内核的角度看,管道本质上是一个由内核维护的环形缓冲区,用于在进程间传递数据。