进程间通讯之管道
node
进程间通讯:数据结构
每一个进程各自有不一样的用户地址空间,任何一个进程的全局变量在另外一个进程中都看不到,因此进程之间要交换数据必须经过内核(操做系统),在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通讯。ide
本质:让不一样的进程能够访问同一块系统资源。函数
实现方式:测试
1、最基本的进程间通讯机制--管道(由pipe函数建立)spa
管道的实现机制 操作系统
在Linux中,管道是一种使用很是频繁的通讯机制。从本质上说,管道也是一种文件,但它又和通常的文件有所不一样,管道能够克服使用文件进行通讯的两个问题,具体表现为:3d
(1)限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不像文件那样不加检验地增加。使用单个固定缓冲区也会带来问题,好比在写管道时可能变满,当这种状况发生时,随后再对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。blog
(2)读取进程时也可能工做得比写进程快。当全部当前进程数据已被读取时,管道变空。当这种状况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。索引
注意:从管道读数据是一次性操做,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。
一、管道的结构:
在Linux中,管道的实现并无使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。经过将两个file结构指向同一个临时的VFS索引节点,而这个VFS索引节点又指向一个物理页面而实现的。如下图所示:
上图中两个 file 数据结构,但它们定义文件操做例程地址是不一样的,其中一个是向管道中写入数据的例程地址,而另外一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是一般的文件操做,而内核却利用这种抽象机制实现了管道这一特殊操做。
二、管道的读写
管道实现的源代码在fs/pipe.c中,在pipe.c中有不少函数,其中有两个函数比较重要,即管道读函数pipe_read()和管道写函数pipe_wrtie()。管道写函数经过将字节复制到VFS索引节点指向的物理内存而写入数据,而管道读函数则经过复制物理内存中的字节而读出数据。固然,内核必须利用必定的机制同步对管道的访问,为此,内核使用了锁、等待队列和信号。
当写进程向管道中写入时,它利用标准的库函数write(),系统根据库函数传递的文件描述符,可找到该文件的file结构。file结构中指定了用来进行写操做的函数(即写入函数)地址,因而,内核调用该函数完成写操做。写入函数在向内存中写入数据以前,必须首先检查VFS索引节点中的信息,同时知足以下条件时,才能进行实际的内存复制工做:
· 内存中有足够的空间可容纳全部要写入的数据;
· 内存没有被读程序锁定。
若是同时知足上述条件,写入函数首先锁定内存,而后从写进程的地址空间中复制数据到内存。不然,写入进程就休眠在VFS索引节点的等待队列中,接下来,内核将调用调度程序,而调度程序会选择其余进程运行。写入进程实际处于可中断的等待状态,当内存中有足够的空间能够容纳写入数据,或内存被解锁时,读取进程会唤醒写入进程,这时,写入进程将接收到信号。当数据写入内存以后,内存被解锁,而全部休眠在索引节点的读取进程会被唤醒。
管道的读取过程和写入过程相似。可是,进程能够在没有数据或内存被锁定时当即返回错误信息,而不是阻塞该进程,这依赖于文件或管道的打开模式。反之,进程能够休眠在索引节点的等待队列中等待写入进程写入数据。当全部的进程完成了管道操做以后,管道的索引节点被丢弃,而共享数据页也被释放。
所以管道的实现涉及不少文件的操做。
获取Linux 内存页(基页)大小的命令:getconf PAGE_SIZE ,通常的输出是4096,即 4KB。
管道建立后如何实现两个进程间的通讯(好比以下实现步骤)
实现步骤:
一、父进程调用pipe开辟管道,获得两个文件描述符指向管道的两端。以下图:
二、父进程调用fork建立子进程,那么子进程也有两个文件描述符指向同一管道。以下图:
三、父进程关闭管道读端,子进程关闭管道写端。父进程能够往管道里写,子进程能够从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通讯。以下图:
演示代码:
管道的特色:
1、管道只容许具备血缘关系的进程间通讯,如父子进程间的通讯。
2、管道只容许单向通讯。
3、依赖文件系统
4、面向字节流
5、管道随进程,进程在管道在,进程消失管道对应的端口也关闭,两个进程都消失管道也消失。
管道的容量
测试代码以下图:
运行结果以下:
由运行结果可得,管道容量约为64KB。
注意管道如下的四种特殊状况:
一、写端关闭,读端继续,当把管道中剩余的数据读完,再次read将返回0,就像读到文件末尾同样。
二、写端不关,且写端不写,读端继续,当管道中剩余的数据都被读取后,再次read将被阻塞,直到管道中有数据可读了才读取数据并返回。
三、读端关闭,进程向管道的写端write,那么进程会收到信号SIGPIPE,致使进程异常终止。
四、读端不关且读端不读,写端继续,管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。