Linux 管道pipe的实现原理

转载html

管道

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

无名管道

主要用于父进程与子进程之间,或者两个兄弟进程之间。在linux系统中能够经过系统调用创建起一个单向的通讯管道,且这种关系只能由父进程来创建。linux

命名管道

命名管道是创建在实际的磁盘介质或文件系统(而不是只存在于内存中)上有本身名字的文件,任何进程能够在任什么时候间经过文件名或路径名与该文件创建联系。为了实现命名管道,引入了一种新的文件类型——FIFO文件(遵循先进先出的原则)。
实现一个命名管道实际上就是实现一个FIFO文件。命名管道一旦创建,以后它的读、写以及关闭操做都与普通管道彻底相同。虽然FIFO文件的inode节点在磁盘上,可是仅是一个节点而已,文件的数据仍是存在于内存缓冲页面中,和普通管道相同。数据结构

管道实现机制

管道是由内核管理的一个缓冲区,至关于咱们放入内存中的一个纸条。
管道的一端链接一个进程的输出。这个进程会向管道中放入信息。
管道的另外一端链接一个进程的输入,这个进程取出被放入管道的信息。
一个缓冲区不须要很大通常为4K大小,它被设计成为环形的数据结构,以便管道能够被循环利用。
当管道中没有信息的话,从管道中读取的进程会等待,直到另外一端的进程放入信息。
当管道被放满信息的时候,尝试放入信息的进程会等待,直到另外一端的进程取出信息。
当两个进程都终结的时候,管道也自动消失。函数

建立过程

clipboard.png

细节

在Linux中,管道的实现并无使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。
经过将两个file结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的。
以下图有两个 file 数据结构,但它们定义文件操做例程地址是不一样的,其中一个是向管道中写入数据的例程地址,
而另外一个是从管道中读出数据的例程地址。
这样,用户程序的系统调用仍然是一般的文件操做,而内核却利用这种抽象机制实现了管道这一特殊操做。
clipboard.pngspa

读写操做

管道实现的源代码在fs/pipe.c中,在pipe.c中有不少函数,其中有两个函数比较重要,
即管道读函数pipe_read()和管道写函数pipe_wrtie()。
管道写函数经过将字节复制到 VFS 索引节点指向的物理内存而写入数据,而管道读函数则经过复制物理内存中的字节而读出数据。
固然,内核必须利用必定的机制同步对管道的访问,为此,内核使用了锁、等待队列和信号。设计

当写进程向管道中写入时,它利用标准的库函数write(),系统根据库函数传递的文件描述符,可找到该文件的 file 结构。
file 结构中指定了用来进行写操做的函数(即写入函数)地址,因而,内核调用该函数完成写操做。
写入函数在向内存中写入数据以前,必须首先检查 VFS 索引节点中的信息,同时知足以下条件时,才能进行实际的内存复制工做:code

内存中有足够的空间可容纳全部要写入的数据;

内存没有被读程序锁定。

若是同时知足上述条件,写入函数首先锁定内存,而后从写进程的地址空间中复制数据到内存。
不然,写入进程就休眠在 VFS 索引节点的等待队列中,接下来,内核将调用调度程序,而调度程序会选择其余进程运行。
写入进程实际处于可中断的等待状态,当内存中有足够的空间能够容纳写入数据,
或内存被解锁时,读取进程会唤醒写入进程,这时,写入进程将接收到信号。
当数据写入内存以后,内存被解锁,而全部休眠在索引节点的读取进程会被唤醒。htm

管道的读取过程和写入过程相似。可是,进程能够在没有数据或内存被锁定时当即返回错误信息,而不是阻塞该进程,
这依赖于文件或管道的打开模式。反之,进程能够休眠在索引节点的等待队列中等待写入进程写入数据。
当全部的进程完成了管道操做以后,管道的索引节点被丢弃,而共享数据页也被释放blog

相关文章
相关标签/搜索