Linux IPC:匿名管道 与 命名管道

  进程间通信方式有多种,本文介绍的是管道,管道分为匿名管道和命名管道。

一、管道的理解

  生活中的管道用来传输资源,例如水、石油之类的资源。而进程间通信的管道传输的是数据,管道的使用场景就是数据传输

管道的特性:

  • 半双工通信
  • 先进先出

  半双工通信:数据可以沿两个方向发送,但是同一时间一个管道中只允许向一个方向发送数据,也可以称之为可以选择方向的单向通信。但实际使用管道的时候,方向一旦确定就不再更改了。就类似于家里的自来水管,自来水只会从水厂传输到家里,而不是从家里把水传输到水厂。

  先进先出:先发送的数据先被接收,数据的发送顺序就是数据的接受顺序,就像队列一样,先进先出。(注意:管道中数据不能一直无限的写入,是有大小限制的)

  如图:发送顺序是:1、2、3、4,接受顺序也是:1、2、3、4。

管道示意图

管道的本质:

  内核中的一块缓冲区,而缓冲区就是内核空间中的一块内存,这块内存被用来进行进程间通信。

管道的分类:

  • 匿名管道
  • 命名管道

二、匿名管道

理解匿名管道:

 (1)内核中的缓冲区(也就是管道)没有标识符(注意和文件标识符区分开,这两不是一个东西),只能用于具有亲缘关系的进程间通信。

 (2)因为标识符是一个管道的名字,一个管道如果没有名字,自然就是匿名管道。

匿名管道如何实现具有亲缘关系的进程间通信?

 (1)在Linux中,系统在操作管道的时候,是把管道当作文件来进行操作的,因此管道都有对应的文件描述符。父进程先创建匿名管道,然后再创建子进程,这样父子进程就可以使用这个匿名管道来通信。

 (2)因为父进程创建了匿名管道后,系统是把这个管道当作文件进行处理的,因此会有对应的文件描述符(文件描述符就是一个文件在fd_arr数组中的下标),文件描述符会占据数组中的最小未使用的下标。

 (3)父进程创建子进程后,子进程会复制父进程pcb中的大部分信息,其中就包含了fd_arr[]数组,因此子进程也会得到文件描述符的相关信息。虽然子进程会初始化页表和虚拟空间,但不会修改pcb中和IO相关的信息。

 (4)子进程复制fd_arr[]数组后,也就知道了这个管道的操作句柄,有了这个操作句柄,父子进程就可以访问同一个匿名管道了。

匿名管道原理

三、命名管道

理解命名管道:

 (1)内核中缓冲区具有标识符,也就是这个管道有名字。

 (2)标识符是一个可见于文件系统的特殊管道文件,多个进程通过打开同一个标识符文件,就可以访问同一块内核中的缓冲区(命名管道)。

  如图:管道文件标识符test.fifo(这是一个文件),进程通过这个文件来访问内核中的缓冲区(命名管道)。即便是没有亲缘关系的进程,也可以通过它来访问内核中的同一块缓冲区(命名管道)。

命名管道原理

四、管道的通信流程

  进程A要把数据通过管道发送给进程B,就首先要把数据拷贝一份放到内核空间的管道中(第一次拷贝)。然后进程B从内核空间的管道中把这个数据再拷贝一次带回来(第二次拷贝),这样才能拿到这个数据。

  因此使用管道传输数据时,需要经历两次拷贝

数据流向

五、管道的特性

  1. 管道中的数据是一次性的,读取后就没有了,空间就被腾出来了。
  2. 如果管道中没有数据,读端(read)默认会阻塞,直到读取到了数据后才会返回。
  3. 如果管道中数据写满了,写端(write)默认会阻塞,直到数据被读出,管道中有了空闲空间才可以继续写入数据。
  4. 管道的读写是一种字节传输服务,数据会在缓冲区堆积,并且遵循先进先出原则(类似于队列的先进先出)。
  5. 如果管道所有写端被关闭,读端(read)读完所有数据后,如果继续读取数据将不再是阻塞而是返回0。(管道中read返回0,最主要表示没人写数据了,你继续等着读数据没啥意义)
  6. 若管道所有读端被关闭,写端(write)继续写入数据,则触发异常,退出程序。
  7. 管道在写入数据时,如果数据大小不超过PIPE_BUF大小,保证原子操作,防止数据混乱。(原子操作:要么这个操作一次性完成无法被打断,要么不做这个操作)
  8. 自带同步与互斥,使得数据的读取和写入更加合理。
  9. 生命周期随进程,当进程结束后,管道也就会被关闭。

  注意:如果父子进程都打开了这个管道,那么只有当父子进程对于这个管道的写端全都被关闭,读端在读完缓冲区后,才会返回0。

猜你喜欢

转载自blog.csdn.net/weixin_57761086/article/details/128770828