知识问答

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()函数创建子进程,子进程中向管道中写入一条数据,并在写入完成后关闭写文件描述符。

在父进程中,读取管道中的数据并将其累加,最终打印出接收到的总数据量,并关闭读文件描述符。

通过以上代码,我们可以实现多个子进程同时向同一管道中写入数据,而父进程从中读取,并统计接收到的数据总和,从而实现了多进程之间的通信。