Linux编程之pipe()函数详解
在Linux编程中,pipe()
是一个重要的函数,用于在两个进程之间创建一个管道,从而实现进程间通信。本文将详细讲解pipe()
函数的使用方法、注意事项及示例说明。
管道的创建
调用pipe()
函数可以创建一个管道,该函数的原型如下:
#include <unistd.h>
int pipe(int pipefd[2]);
其中,pipefd
是一个整型数组,用于存储管道两端的文件描述符。pipefd[0]
代表读端,pipefd[1]
代表写端。
需要注意的是,pipe()
函数必须在创建进程之前调用,这是因为管道只能在父进程和子进程之间进行通信,而且在fork函数之后,父进程和子进程拥有相同的文件描述符表,因此在子进程调用pipe()
函数创建管道已经来不及了。
管道的通信
管道的通信是通过read()
和write()
函数完成的,其中读操作和写操作分别在不同的进程中进行。如下图所示,假设有两个进程P1和P2,通过pipe()
函数创建一个管道,并且P1写入数据到管道中,P2从管道中读出数据。
+------+ +------+
| P1 | | P2 |
+------+ +------+
| -------------> |
| |
| |
| <------------- |
| |
+--------+ +--------+
| Write | | Read |
| end | | end |
+--------+ +--------+
写端
在写端进程中,我们需要取得管道的写文件描述符,并通过write()
函数将数据写入管道中,示例如下:
char *buffer = "Hello pipe!";
// 创建管道
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe failed");
exit(EXIT_FAILURE);
}
// 子进程中写入数据
if (fork() == 0) {
close(pipefd[0]); // 关闭读端
write(pipefd[1], buffer, strlen(buffer));
close(pipefd[1]);
exit(EXIT_SUCCESS);
}
上述代码中,先创建了一个包含字符串数据的缓冲区buffer
,随后通过pipe()
函数创建了一个管道,并在子进程中进行写入操作。管道的写文件描述符为pipefd[1]
,所以需要先关闭读端文件描述符pipefd[0]
,然后使用write()
函数将数据写入管道,最后关闭写文件描述符pipefd[1]
。
读端
在读端进程中,我们需要取得管道的读文件描述符,并通过read()
函数从管道中读取数据,示例如下:
char buffer[BUFSIZ];
// 创建管道
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe failed");
exit(EXIT_FAILURE);
}
// 父进程中读取数据
if (fork() > 0) {
close(pipefd[1]); // 关闭写端
int len = read(pipefd[0], buffer, BUFSIZ - 1);
buffer[len] = '\0';
printf("Received message: %s\n", buffer);
close(pipefd[0]);
exit(EXIT_SUCCESS);
}
上述代码中,先创建了一个缓冲区buffer
,随后通过pipe()
函数创建了一个管道,并在父进程中进行读取操作。管道的读文件描述符为pipefd[0]
,所以需要先关闭写端文件描述符pipefd[1]
,然后使用read()
函数从管道中读取数据,并将其保存到缓冲区中,最后打印出接收到的数据并关闭读文件描述符pipefd[0]
。
注意事项
在使用pipe()
函数时,需要注意以下几点:
- 管道是一个单向通信机制,一端写入后只能在另一端读取,不能同时读写。
- 管道的容量是有限的,写入数据超过管道容量时会阻塞,除非使用特殊标志。
- 管道的文件描述符不是永久性的,程序退出时会自动关闭。
示例说明
示例一:基本使用
上述代码中的示例即为管道的基本使用示例,实现了进程间的单向通信。
示例二:多进程通信
可使用多个子进程向同一管道中写入数据,父进程从中读取,并统计接收到的数据总和。
具体实现代码及解释如下:
// 定义进程数
#define PROCESS_NUM 3
// 主函数
int main(int argc, char *argv[]) {
char buffer[BUFSIZ];
// 创建管道
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe failed");
exit(EXIT_FAILURE);
}
// 创建多个子进程并向管道中写入数据
for (int i = 0; i < PROCESS_NUM; i++) {
if (fork() == 0) { // 子进程中
close(pipefd[0]); // 关闭读端
sprintf(buffer, "Message from child %d\n", i);
write(pipefd[1], buffer, strlen(buffer));
close(pipefd[1]);
exit(EXIT_SUCCESS);
}
}
// 父进程中读取并统计数据
close(pipefd[1]); // 关闭写端
int sum = 0;
for (int i = 0; i < PROCESS_NUM; i++) {
int len = read(pipefd[0], buffer, BUFSIZ - 1);
buffer[len] = '\0';
printf("Received message: %s", buffer);
sum += len;
}
printf("Received total %d bytes.\n", sum);
close(pipefd[0]);
wait(NULL);
return 0;
}
上述代码中,先定义了一个常量PROCESS_NUM
,表示创建的子进程数。随后创建了一个包含读写文件描述符的管道,并使用fork()
函数创建子进程,子进程中向管道中写入一条数据,并在写入完成后关闭写文件描述符。
在父进程中,读取管道中的数据并将其累加,最终打印出接收到的总数据量,并关闭读文件描述符。
通过以上代码,我们可以实现多个子进程同时向同一管道中写入数据,而父进程从中读取,并统计接收到的数据总和,从而实现了多进程之间的通信。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:linux编程之pipe()函数详解 - Python技术站