Skip to main content

管道(PIPE)

1. 基本概念

Linux 管道(Pipe)是一种用于在进程之间进行进程间通信的技术。管道是一个先进先出(FIFO)的数据结构,用于将一个进程的输出作为另一个进程的输入。管道可以通过调用 pipe() 系统调用创建,并通过使用 I/O 函数(如 read()write())进行读写操作。Linux 管道可以用于实现复杂的进程间通信机制,如管道线(pipeline)、命令行管道(command line pipe)等。

常说的管道通常指的是无名管道(PIPE)和有名管道(FIFO)。

2. 无名管道(PIPE)

2.1 PIPE 的特征

  1. 没有名字,无法使用 ·open()`。
  2. 只能用于亲缘进程间(父子进程、兄弟进程、祖孙进程)通信。
  3. 半双工工作模式:读写端分开。
  4. 写入操作不具有原子性,因此只能用于一对一的简单通信。
  5. 不能使用 lseek() 来定位。

2.2 PIPE 创建

在 Linux 系统中 pipe() 函数用于创建一个管道。函数原型为:

int pipe(int pipefd[2]);

该函数接受一个参数:

  • pipefd:指向管道的整型数组的指针。

如果创建成功,则该函数返回 0。否则,返回一个非 0 值。

该函数会创建一个管道,将两个文件描述符存储在 pipefd 数组中。第一个文件描述符用于读取管道,第二个文件描述符用于写入管道。这两个文件描述符可以用来通过管道进行进程间通信。

2.3 示例代码

下面的示例代码实现了父子进程间 pipe 管道的通信:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
// 管道文件描述符
int pipefd[2];

// 创建管道
if (pipe(pipefd) < 0) {
perror("pipe() fail");
exit(EXIT_FAILURE);
}

// 创建子进程
pid_t pid = fork();
if (pid < 0) {
perror("fork() fail");
exit(EXIT_FAILURE);
}

// 子进程
if (pid == 0) {
// 关闭读取管道的文件描述符
close(pipefd[0]);

// 写入管道
const char *message = "Hello parent!\n";
write(pipefd[1], message, strlen(message));

// 关闭写入管道的文件描述符
close(pipefd[1]);
}

// 父进程
if (pid > 0) {
// 关闭写入管道的文件描述符
close(pipefd[1]);

// 读取管道
char buf[30];
bzero(buf, 30);
read(pipefd[0], buf, sizeof(buf));
printf("from child: %s", buf);
// 关闭读取管道的文件描述符
close(pipefd[0]);
}
return 0;
}

注意:父进程必须先创建PIPE,然后再创建子进程,这样子进程才能继承父进程已经产生的PIPE的文件描述符。

3. 有名管道(FIFO)

3.1 FIFO 的特征

  1. 有名字,存储于普通文件系统之中。
  2. 任何具有相应权限的进程都可以使用open()来获取FIFO的文件描述符。
  3. 跟普通文件一样:使用统一的 read( )/write() 来读写。
  4. 跟普通文件不同:不能使用lseek( )来定位。
  5. 具有写入原子性,支持多写者同时进行写操作而数据不会互相践踏。
  6. First In First Out,最先被写入FIFO的数据,最先被读出来。

3.2 FIFO 的创建

在 Linux 系统中,mkfifo() 函数用于创建一个命名管道。函数原型为:

int mkfifo(const char *pathname, mode_t mode);

该函数接受两个参数:

  • pathname:命名管道的路径。
  • mode:命名管道的权限。

如果创建成功,则该函数返回 0。否则,返回一个非 0 值。

该函数会创建一个命名管道,它与普通的管道类似,但是它有一个文件名,可以通过文件名来访问它。命名管道可以让不同进程间通过文件系统路径进行通信。

3.3 示例代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

//管道文件只能在tmp路径下或者家目录生成
#define FIFO "/tmp/myfifo"

int main(int argc, char *argv[])
{
// 创建一个FIFO文件
if (access(FIFO, F_OK)) {
if (mkfifo(FIFO, 0666) == -1) {
perror("mkfifo error");
exit(1);
}
}

// 打开FIFO文件
int fd = open(FIFO, O_WRONLY|O_RDONLY);
if (fd == -1) {
perror("open error");
exit(1);
}

// 往FIFO文件中写数据
const char *msg = "Hello from process 1\n";
int n = write(fd, msg, strlen(msg));
printf("%d bytes write fifo\n", n);

// 读取FIFO文件中的数据
char buf[20];
bzero(buf, 20);
read(fd, buf, 20);
printf("from FIFO: %s\n", msg);

sleep(1);
// 关闭FIFO文件
close(fd);

return 0;
}