阅读 244

管道通信总结

前言

面试的时候常常被问道进程间的通信和线程间的通信,静态做个小总结

概括

进程通信主要可以分成以下几种

  • 管道

  • 消息队列

  • 共享内存

  • 信号量

  • 信号

  • socket

管道

匿名管道

ps auxf | grep mysql
复制代码

上面命令行里的「|」竖线就是一个管道,它的功能是将前一个命令(ps auxf)的输出,作为后一个命令(grep mysql)的输入,从这功能描述,可以看出管道传输数据是单向的,如果想相互通信,我们需要创建两个管道才行。

命名管道

  • 命名管道是文件
$ mkfifo myPipe
复制代码
  • myPipe 就是这个管道的名称,基于 Linux 一切皆文件的理念,所以管道也是以文件的方式存在,我们可以用 ls 看一下,这个文件的类型是 p,也就是 pipe(管道) 的意思
$ ls -l\
prw-r--r--. 1 root    root         0 Jul 17 02:45 myPipe
复制代码
  • 我们往 myPipe 这个管道写入数据:
$ echo "hello" > myPipe  // 将数据写进管道\
                         // 停住了 ...
复制代码

在开启另外一个线程读取数据

$ cat < myPipe  // 读取管道里的数据\
hello
复制代码

实现原理

  • 匿名管道的创建,需要通过下面这个系统调用:
int pipe(int fd[2])
复制代码
  • 这里表示创建一个匿名管道,并返回了两个描述符,一个是管道的读取端描述符 fd[0],另一个是管道的写入端描述符 fd[1]。注意,这个匿名管道是特殊的文件,只存在于内存,不存于文件系统中。

  • 所谓的管道,就是内核里面的一串缓存。从管道的一段写入的数据,实际上是缓存在内核中的,另一端读取,也就是从内核中读取这段数据。另外,管道传输的数据是无格式的流且大小受限。

image.png

  • 你可能会有疑问了,这两个描述符都是在一个进程里面,并没有起到进程间通信的作用,怎么样才能使得管道是跨过两个进程的呢?

  • 我们可以使用 fork 创建子进程,创建的子进程会复制父进程的文件描述符,这样就做到了两个进程各有两个「 fd[0] 与 fd[1]」,两个进程就可以通过各自的 fd 写入和读取同一个管道文件实现跨进程通信了。

image.png

  • 在 shell 里面执行 A | B命令的时候,A 进程和 B 进程都是 shell 创建出来的子进程,A 和 B 之间不存在父子关系,它俩的父进程都是 shell。

image.png

  • 所以说,在 shell 里通过「|」匿名管道将多个命令连接在一起,实际上也就是创建了多个子进程,那么在我们编写 shell 脚本时,能使用一个管道搞定的事情,就不要多用一个管道,这样可以减少创建子进程的系统开销。

  • 我们可以得知,对于匿名管道,它的通信范围是存在父子关系的进程。因为管道没有实体,也就是没有管道文件,只能通过 fork 来复制父进程 fd 文件描述符,来达到通信的目的。

  • 另外,对于命名管道,它可以在不相关的进程间也能相互通信。因为命令管道,提前创建了一个类型为管道的设备文件,在进程里只要使用这个设备文件,就可以相互通信。

参考

文章分类
后端
文章标签