linux编程之pipe函数详解
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()函数创建子进程,子进程中向管道中写入一条数据,并在写入完成后关闭写文件描述符。
在父进程中,读取管道中的数据并将其累加,最终打印出接收到的总数据量,并关闭读文件描述符。
通过以上代码,我们可以实现多个子进程同时向同一管道中写入数据,而父进程从中读取,并统计接收到的数据总和,从而实现了多进程之间的通信。