点我查看操作系统秘籍连载


管道

管道是操作系统提供的一种最基本的进程间通信方式。每创建一个管道,就有两个文件描述符,一个是负责读管道的,一个是负责写管道的。所以,使用管道通信时,可以看作是两个文件描述符加一段内核空间中的内存,如图。

管道只能协调有亲缘关系的进程间通信,所谓亲缘,比如父子进程、兄弟进程。当某进程创建一个管道后,它就拥有了这个管道的两个文件描述符,它的子进程会继承这两个文件描述符,所以子进程也能读写这个管道。如图。

但为了让管道通信更安全、更方便,一般管道两端的每个进程都会各自关闭一个管道的文件描述符,例如父进程关闭读描述符,这样父进程只能向管道写数据,子进程关闭写描述符,这样子进程只能从管道读数据。或者相反。如图。

Shell也提供了管道,只需使用一根竖线连接两个命令即可。例如:

1
2
3
ps -elf | grep "sshd"

cat a.log | grep "hello world"

在shell下,这种管道称为匿名管道,即没有名称的管道。它对于编写命令行来说非常方便,且逻辑清晰易懂,shell脚本和shell命令行几乎靠它打下了半壁江山。

在shell下,还支持使用mkfifo命令创建命名管道(named pipe),即有名称的管道,它也称为FIFO,它可以协调任意进程间的数据通信。

例如,创建命名管道文件a.fifo,a.fifo就是这个命名管道的名称。虽然它以文件的方式存在于磁盘上,但它传递数据的方式不会经过磁盘IO,而是直接在内存中传递,所以速度非常快,文件名仅仅只是这个命名管道的名称而已,是引用这个管道的入口和出口。

1
2
3
$ mkfifo a.fifo
$ ls -l a.fifo
prw-r--r-- 1 root root 0 Apr 30 23:52 a.fifo # 文件类型为p

命名管道是阻塞式的双向通信管道,任意一方都可以读、写,但是只有读、写端同时打开了命名管道时,数据才会写入并被读取。例如,下图中显示了在未打开读端命名管道的时候,所有写命名管道的操作都被阻塞。如果cat a.fifo按下回车键打开读端命名管道,写和读操作都将正常执行。同理,只打开读端而未打开写端命名管道时,读操作也会被阻塞。