进程间通信(2):管道
管道
管道是操作系统提供的一种最基本的进程间通信方式。每创建一个管道,就有两个文件描述符,一个是负责读管道的,一个是负责写管道的。所以,使用管道通信时,可以看作是两个文件描述符加一段内核空间中的内存,如图。
管道只能协调有亲缘关系的进程间通信,所谓亲缘,比如父子进程、兄弟进程。当某进程创建一个管道后,它就拥有了这个管道的两个文件描述符,它的子进程会继承这两个文件描述符,所以子进程也能读写这个管道。如图。
但为了让管道通信更安全、更方便,一般管道两端的每个进程都会各自关闭一个管道的文件描述符,例如父进程关闭读描述符,这样父进程只能向管道写数据,子进程关闭写描述符,这样子进程只能从管道读数据。或者相反。如图。
Shell也提供了管道,只需使用一根竖线连接两个命令即可。例如:
[root@docker-03 ~]# ps -elf | grep "sshd" 4 S root 939 1 0 80 0 - 26519 poll_s 18:15 ? 00:00:00 /usr/sbin/sshd -D 4 S root 1306 939 0 80 0 - 37099 poll_s 18:16 ? 00:00:00 sshd: root@pts/0 0 S root 1417 1308 0 80 0 - 28182 pipe_w 19:23 pts/0 00:00:00 grep --color=auto sshd [root@docker-03 ~]# cat a.log | grep "hello world"
在shell下,这种管道称为匿名管道,即没有名称的管道。它对于编写命令行来说非常方便,且逻辑清晰易懂,shell脚本和shell命令行几乎靠它打下了半壁江山。
在shell下,还支持使用mkfifo
命令创建命名管道(named pipe),即有名称的管道,它也称为FIFO,它可以协调任意进程间的数据通信。
例如,创建命名管道文件a.fifo,a.fifo就是这个命名管道的名称。虽然它以文件的方式存在于磁盘上,但它传递数据的方式不会经过磁盘IO,而是直接在内存中传递,所以速度非常快,文件名仅仅只是这个命名管道的名称而已,是引用这个管道的入口和出口。
1
|
|
命名管道是阻塞式的双向通信管道,任意一方都可以读、写,但是只有读、写端同时打开了命名管道时,数据才会写入并被读取。例如,下图中显示了在未打开读端命名管道的时候,所有写命名管道的操作都被阻塞。如果cat a.fifo按下回车键打开读端命名管道,写和读操作都将正常执行。同理,只打开读端而未打开写端命名管道时,读操作也会被阻塞。