本文共 3251 字,大约阅读时间需要 10 分钟。
管道是UNIX系统中的一种古老的通信方式,实际上它本质上是一个文件。管道允许进程之间进行简单的数据传输,数据可以是文本或二进制数据。例如,命令行工具who的标准输出原本会显示在屏幕上,但可以通过管道将输出重定向到一个文件中。这种重定向称为匿名管道,常见的例子是who | wc -l,其中who将数据写入管道,wc -l则从管道读取数据并显示结果。
从who | wc -l的例子可以看出,who进程使用管道的写端将数据写入管道,而wc进程使用管道的读端读取数据。两个进程通过管道进行通信时,一个进程需要使用写端,另一个进程需要使用读端。因此,管道文件需要两个文件描述符来控制读写操作。
在UNIX系统中,匿名管道可以通过pipe()函数创建。该函数的头文件是<unistd.h>,函数原型为int pipe(int fd[2])。传入的fd数组包含两个元素,fd[0]表示管道的读端文件描述符,fd[1]表示写端文件描述符。默认情况下,fd[0]和fd[1]分别打开管道文件进行读写操作。
pipe()函数创建管道。fork()创建子进程。fd[1],子进程关闭读端fd[0]。以下是一个简单的示例:
#include#include #include #include int main() { int pipefd[2] = {0}; pipe(pipefd); pid_t child_id = fork(); if (child_id == 0) { close(pipefd[0]); const char *msg = "This is the data that the child process wrote"; while (1) { write(pipefd[1], msg, strlen(msg)); sleep(1); } } else { close(pipefd[1]); char buffer[64]; while (1) { ssize_t ret = read(pipefd[0], buffer, sizeof(buffer) - 1); if (ret > 0) { buffer[ret] = '\0'; printf("The father process got the information: %s\n", buffer); } } } return 0;}
在上述代码中,子进程每秒写入一段数据,父进程则持续读取并输出数据。当子进程退出时,父进程也随之退出。这种简单的通信机制展示了管道的基本特性。
如果写端没有关闭文件描述符且不进行写入操作,读端可能会长时间阻塞(即读端慢)。例如,当子进程的睡眠时间从1秒增加到5秒时,父进程仍然可以及时读取数据。
当写端试图向已满的管道中写入数据时,写端会被阻塞,直到读端读取数据并释放管道空间。
如果写端关闭,读端在读到管道内容结束(返回值为0)时会停止读取。
如果读端关闭,操作系统会发送SIGPIPE信号终止写端进程。
匿名管道只能在具有共同祖先的进程间通信,而命名管道可以在任何进程间通信。
命名管道可以通过mkfifo命令或函数mkfifo()创建。例如:
mkfifo my_pipe
函数原型为int mkfifo(const char *filename, mode_t mode)。
以下是一个使用命名管道的简单示例:
#include#include #include #include int main() { const char *fifo_path = "my_fifo"; umask(0); if (mkfifo(fifo_path, 0666) == -1) { perror("创建管道失败"); return 1; } int fd = open(fifo_path, O_WRONLY); if (fd == -1) { perror("打开管道失败"); return 1; } char *msg = "Hello, world!"; while (1) { write(fd, msg, strlen(msg)); sleep(1); } return 0;}
在另一进程中打开同一管道文件并进行读写操作:
#include#include #include #include int main() { const char *fifo_path = "my_fifo"; int fd = open(fifo_path, O_RDONLY); if (fd == -1) { perror("打开管道失败"); return 1; } char *buffer = "Hello, parent!"; while (1) { ssize_t ret = read(fd, buffer, sizeof(buffer) - 1); if (ret > 0) { buffer[ret] = '\0'; printf("Parent received: %s\n", buffer); } } return 0;}
在Linux内核中,所有文件都以文件结构体file表示。管道文件的file结构体的type成员为S_IFIFO,表明这是一个FIFO文件。文件的路径和目录结构体dentry关联,目录的inode存储文件的信息。
特性:
优势:
通过对管道和命名管道的理解,我们可以更好地利用这些机制进行进程间通信。例如,who | wc -l的实现正是基于匿名管道的通信机制,而mkfifo命令则用于创建命名管道以支持跨进程通信。
转载地址:http://yisi.baihongyu.com/