当前位置: 首页 > Linux

Linux流水线pipe

时间:2023-04-06 04:52:22 Linux

转载pipeline的实现原理Pipeline是进程间通信的主要手段之一。管道实际上是一个只存在于内存中的文件。对这个文件的操作是通过两个打开的文件来执行的,这两个文件代表了管道的两端。管道是一种特殊的文件,不属于某个文件系统,而是一个独立的文件系统,有自己的数据结构。根据管道的适用范围分为:无名管道和有名管道。无名管道主要用于父进程和子进程之间,或者两个兄弟进程之间。在Linux系统中,可以通过系统调用建立单向通信通道,而这种关系只能由父进程建立。命名管道命名管道是建立在实际磁盘介质或文件系统上(而不是仅仅存在于内存中)的具有自己名称的文件,任何进程都可以通过文件名或路径名随时与该文件建立联系。为了实现命名管道,引入了一种新的文件类型——FIFO文件(遵循先进先出的原则)。实现命名管道实际上就是实现一个FIFO文件。命名管道一旦建立,其读、写、关闭操作与普通管道完全相同。FIFO文件的inode节点虽然在磁盘上,但只是一个节点,文件的数据仍然存在于内存缓冲页中,这和普通的管道是一样的。流水线实现机制流水线是由内核管理的缓冲区,相当于我们放在内存中的一张纸条。管道的一端连接到进程的输出。此过程将消息放入管道。管道的另一端连接到一个进程的输入,该进程获取放入管道的信息。一个buffer不需要很大,一般4K大小,设计成环形数据结构,以便pipeline可以循环使用。当管道中没有消息时,从管道中读取的进程会一直等待,直到另一端的进程放入消息。当管道充满消息时,试图放入消息的进程会一直等待,直到另一端的进程收到消息。当两个进程都终止时,管道自动消失。创建过程详解在Linux中,管道的实现并没有使用特殊的数据结构,而是使用了文件系统的文件结构和VFS索引节点的inode。这是通过将两个文件结构指向同一个临时VFSinode来实现的,该临时VFSinode又指向一个物理页面。如下图,有两种文件数据结构,但是它们定义了不同的文件操作例程地址,其中一个是将数据写入管道的例程地址,一个是读取数据的例程地址来自管道的数据。这样,用户程序的系统调用仍然是普通的文件操作,只是内核利用这种抽象机制实现了管道的特殊操作。读写操作流水线源码在fs/pipe.c。pipe.c中有很多函数,其中比较重要的有两个,分别是管道读函数pipe_read()和管道写函数pipe_wrtie()。管道写入函数通过将字节复制到VFSinode指向的物理内存来写入数据,而管道读取函数通过复制物理内存中的字节来读取数据。当然,内核必须使用一定的机制来同步对管道的访问。为此,内核使用锁、等待队列和信号。写进程向管道写入时,使用标准库函数write(),系统可以根据库函数传递的文件描述符找到文件的文件结构。用于执行写操作的函数(即write函数)的地址在文件结构中指定,因此内核调用这个函数来完成写操作。write函数在将数据写入内存之前,首先要检查VFS索引节点中的信息,只有满足以下条件才能进行真正的内存拷贝:内存中有足够的空间容纳所有数据待写;存储器未被读取器锁定。如果同时满足以上条件,write函数首先锁住内存,然后将数据从写进程的地址空间复制到内存中。否则,写入进程在VFSinode的等待队列中休眠,然后内核调用调度器,调度器选择另一个进程运行。写入过程实际上处于可中断的等待状态。当内存中有足够的空间容纳写入的数据,或者内存解锁时,读进程会唤醒写进程。这时候写进程会收到一个信号。数据写入内存后,内存解锁,所有休眠在索引节点上的读进程都会被唤醒。管道的读取过程与写入过程类似。但是,当没有数据或内存被锁定时,进程可以立即返回错误消息,而不是阻塞进程,具体取决于文件或管道打开方式。反之,进程可以休眠在索引节点的等待队列中,等待写入进程写入数据。当所有进程都完成管道操作后,管道的索引节点被丢弃,共享数据页也被释放