0%

Linux程序设计-管道 [总结概况版][复习专用][速通]

导航

当前章节:管道
原文链接:管道
上一章节:进程
下一章节:线程
目录:Linux程序设计

管道

介绍

管道是Linux中进程间通信的一种形式。它们允许一个过程的输出变成另一个过程中的输入。

在此之前需要先理解一个概念。linux将进程的通信管道,文件是一个实体,使用文件描述符指向对应文件,文件描述符是一个整型数据,从0开始递增。

如当创建一个进程时默认存在stdin标准输入0(dev/stdin),stdout标准输出1,stderror标准错误2。创建子进程会继承文件描述符。
创建管道时会返回两个文件描述符,一个写一个读,通过write和read可以实现进程通信。

管道有几个关键点:

  • 管道是使用pipe()系统调用创建的。这返回两个文件描述符,一个用于读取(fd[0]),另一个用于写入(fd[1])。
  • pipe()返回的文件描述符在默认情况下是不可继承的。我们需要使用dup2()将它们传递给子进程。
  • 如果没有要读取的数据(管道为空),则从管道读取将被阻止。对完整管道的写入也将被阻止。
  • 管道的容量有限,通常在4kB到64kB之间。如果达到此容量,将阻止进一步的写入操作。
  • 关闭管道的读取端会导致写入端接收EOF(文件结束)。对管道的进一步写入将导致SIGPIPE信号。当所有数据都已被读取时,关闭写入端会导致读取端接收EOF。
  • 管道允许相关进程以简单的方式进行通信。进程可以是父进程和子进程,也可以是共享管道的不相关进程。

父子进程通信

1
2
3
4
5
6
7
8
9
10
11
int fd[2]; 
pipe(fd);

pid_t pid = fork();
if (pid == 0) { // Child
close(fd[1]); // Close write end
read(fd[0], buf, sizeof(buf));
} else { // Parent
close(fd[0]); // Close read end
write(fd[1], "Hello", 6);
}

不相关两个进程通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int fd[2];
pipe(fd);

pid_t pid1 = fork();
if (pid1 == 0) { // Child 1
close(fd[1]);
read(fd[0], buf, sizeof(buf));
}

pid_t pid2 = fork();
if (pid2 == 0) { // Child 2
close(fd[0]);
write(fd[1], "Hello", 6);
}

dup函数实现重定向

#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);

dup会将返回一个新文件描述符,这个描述符是当前可用的最小描述符,并且为传入的旧文件描述符的值,即该文件描述符指向的文件是原文件
dup2实现对输入输出等文件描述符重定向,例如父子进程将标准输入输出赋值为管道的输入输出,就能实现通过标准输入输出通信。
要注意的是,stdin 是一个指向标准输入的流对象,在底层实现中使用了文件描述符 STDIN_FILENO 来表示标准输入。
例如 dup2(fd[0], STDIN_FILENO) 就是将管道的读取端复制到了 stdin 所代表的标准输入上,使得之后对 stdin 的读取实际上是从管道中读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
int fd[2];
pipe(fd);
pid_t pid = fork();
if (pid == 0) {
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
execlp("cat", "cat", NULL);
} else {
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
execlp("ls", "ls", NULL);
}

dup2()执行以下操作:

  • 如果newfd已经存在,则首先关闭它。

  • oldfd与newfd重复。

  • newfd与oldfd共享相同的文件位置指针和文件状态标志。

  • 这两个文件描述符可以互换使用。

  • 对任何一个文件描述符的读取和写入都将对同一个文件进行操作。

  • dup2()用于将一个文件描述符复制到另一个,同时共享底层资源。

  • 它通常用于重定向进程中的标准I/O流。

  • dup2()到一个关闭的文件描述符将打开它,然后复制到它。

  • dup2()导致原始fd关闭,而新fd引用相同的文件。

  • 重复的fds共享文件位置指针和文件状态标志。