Linux内核中无名管道pipe和有名管道fifo的分析


1、管道(pipe)

管道是进程间通信的主要手段之一。一个管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端。管道是一种特殊的文件,它不属于某一种文件系统,而是一种独立的文件系统,有其自己的数据结构。根据管道的适用范围将其分为:无名管道和命名管道。

●    无名管道

主要用于父进程与子进程之间,或者两个兄弟进程之间。在linux系统中可以通过系统调用建立起一个单向的通信管道,且这种关系只能由父进程来建立。因此,每个管道都是单向的,当需要双向通信时就需要建立起两个管道。管道两端的进程均将该管道看做一个文件,一个进程负责往管道中写内容,而另一个从管道中读取。这种传输遵循“先入先出”(FIFO)的规则。

●    命名管道

命名管道是为了解决无名管道只能用于近亲进程之间通信的缺陷而设计的。命名管道是建立在实际的磁盘介质或文件系统(而不是只存在于内存中)上有自己名字的文件,任何进程可以在任何时间通过文件名或路径名与该文件建立联系。为了实现命名管道,引入了一种新的文件类型——FIFO文件(遵循先进先出的原则)。实现一个命名管道实际上就是实现一个FIFO文件。命名管道一旦建立,之后它的读、写以及关闭操作都与普通管道完全相同。虽然FIFO文件的inode节点在磁盘上,但是仅是一个节点而已,文件的数据还是存在于内存缓冲页面中,和普通管道相同。

2、环形缓冲区

每个管道只有一个页面作为缓冲区,该页面是按照环形缓冲区的方式来使用的。这种访问方式是典型的“生产者——消费者”模型。当“生产者”进程有大量的数据需要写时,而且每当写满一个页面就需要进行睡眠等待,等待“消费者”从管道中读走一些数据,为其腾出一些空间。相应的,如果管道中没有可读数据,“消费者” 进程就要睡眠等待,具体过程如下图所示。

Linux内核中无名管道pipe和有名管道fifo的分析

2.1环形缓冲区实现原理

环形缓冲区是嵌入式系统中一个常用的重要数据结构。一般采用数组形式进行存储,即在内存中申请一块连续的线性空间,可以在初始化的时候把存储空间一次性分配好。只是要模拟环形,必须在逻辑上把数组的头尾相连接。只要对数组最后一个元素进行特殊的处理——访问尾部元素的下一元素时,重新回到头部元素。对于从尾部回到头部只需模缓冲长度即可(假设maxlen为环形缓冲的长度,当读指针read指向尾部元素时,只需执行read=read%maxlen即可使read回到头部元素)。

Linux内核中无名管道pipe和有名管道fifo的分析

2.2读写操作

环形缓冲区要维护写端(write)和读端(read)两个索引。写入数据时,必须先确保缓冲区没有满,然后才能将数据写入,最后将write指针指向下一个元素;读取数据时,首先要确保缓冲区不为空,然后返回read指针对应得元素,最后使read指向下一个元素的位置。读写操作伪代码:

Linux内核中无名管道pipe和有名管道fifo的分析

2.3判断“满”和“空”

当read和write指向同一个位置时环形缓冲区为空或满。为了区别环满和空,当read和write重叠的时候环空;而当write比read快,追到距离read还有一个元素间隔的时候,就认为环已经满了。环形缓冲区原理图如图3所示。

Linux内核中无名管道pipe和有名管道fifo的分析

4.linux内核中pipe的读写实现

Linux内核中采用struct pipe_inode_info结构体来描述一个管道。

Linux内核中无名管道pipe和有名管道fifo的分析

其中,当pipe为空/满时,采用等待队列,该队列使用自旋锁进行保护。

Linux内核中无名管道pipe和有名管道fifo的分析

用struct Pipe_buffer数据结构描述pipe的缓冲(buffer)

Linux内核中无名管道pipe和有名管道fifo的分析

  • 1
  • 2
  • 3
  • 4
  • 下一页

相关内容