概述
在Linux系统中,无名管道是一种简单的进程间通信机制。它允许一个进程创建一对文件描述符,其中一个用于读取,另一个用于写入。当一个进程通过系统调用创建了一个无名管道后,便可以将这两个文件描述符传递给它的子进程,使得父子进程之间可以进行单向的数据交换。
无名管道的特点
无名管道的使用相对比较简单,具有以下一些显著的特点。
1、半双工。无名管道是单向的,意味着我们不能同时从同一个管道两端既读又写。
2、有限容量。无名管道有一个固定的缓冲区大小,默认情况下通常是64KB左右。如果写入的数据超过了这个限制,写操作将会阻塞直到有足够的空间可用。
3、本地通信。无名管道只能在同一台机器上的进程间使用,不能跨越网络边界。
4、有亲缘关系的进程。无名管道主要用于父子进程或者兄弟进程之间,因为只有这些进程可以共享由共同祖先创建的文件描述符。
5、非持久化。无名管道是临时性的,当所有引用它的文件描述符都被关闭时,它就会被销毁。
无名管道的创建
要创建一个无名管道,我们可以使用pipe函数。其函数原型如下。
int pipe(int pipefd[2]);
各个参数和返回值的含义如下。
pipefd:一个整型数组,用来存储两个文件描述符。数组的第一个元素是读端的文件描述符,第二个元素是写端的文件描述符。这两个文件描述符可以像普通文件一样,使用read和write函数来操作。
返回值:成功时返回0,并且会将两个新的文件描述符填充到传入的pipefd数组中。失败时返回-1,并设置全局变量errno来表示具体的错误原因。
无名管道的使用
一旦创建了无名管道,我们便可以利用它来进行进程间的通信。通常的做法是在父进程中创建管道,然后通过fork函数来创建子进程。接着,根据需要关闭不必要的文件描述符(比如:父进程中可能不需要读端,子进程中可能不需要写端),以确保数据流只朝一个方向进行流动。最后,父进程和子进程分别从各自的端点读取或写入数据。
在下面的示例代码中,父进程创建了一个管道,然后调用fork来创建子进程。子进程继承了父进程的文件描述符,但随后关闭了不需要的写端。父进程则关闭了不需要的读端,并向管道中写入了一条消息。子进程从管道中读取消息,并打印到标准输出。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int pipefd[2];
if (pipe(pipefd) == -1)
{
printf("pipe failed\n");
return -1;
}
pid_t cpid = fork();
if (cpid == -1)
{
printf("fork failed\n");
return -1;
}
if (cpid == 0)
{
// 子进程,关闭写端
close(pipefd[1]);
char buf = 0;
while (read(pipefd[0], &buf, 1) > 0)
{
write(STDOUT_FILENO, &buf, 1);
}
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
}
else
{
// 父进程,关闭读端
close(pipefd[0]);
const char *pszMsg = "Hello, Hope_Wisdom";
write(pipefd[1], pszMsg, strlen(pszMsg));
close(pipefd[1]);
// 等待子进程结束
wait(NULL);
}
return 0;
}