进程间通信(IPC Interprocess Communication)
进程和进程之间的通信只能通过内核,在内核中提供一块缓冲区进行通信。内核提供的这种机制叫做IPC
在进程间完成数据传输需要借助操作系统提供的特殊方法,如:文件(开销较大,稳定性低)、管道(使用简单)、信号(开销最小)、共享内存(共享映射区,可以完成没有血缘关系的进程之间的 通信)、消息队列、本地套接字(复杂,较难,最稳定)、命名管道等。
管道
进程的内存在虚拟内存上互不相关,但是有血缘关系的进程在物理内存上,进程的内核区在同一片区域。离得很近。
管道是一种最基本的IPC机制。作用于有血缘关系的进程之间,完成数据传递。
特征:
- 本质是一个伪文件(实际上是内核缓冲区)
Linux操作系统中实际上有七中数据类型(-,d,l,s,b,p,c),其中只有文件、目录和符号链接占用磁盘空间,符号链接当路径不是非常长的时候保存在inode
中,其他类型都是伪文件,不占用实际存储空间 - 由两个文件描述符引用,一个一个表示读端,一个表示写端
- 规定数据从管道的写端流入管道,从读端读出数据。
原理:管道实际上是内核使用环形队列机制,借助内核缓冲区(4K)实现,即一般是八个扇区
圆形的磁盘被划分为多个扇区,每个扇区512字节。一个文件最少占用一个扇区。
因为是由队列实现的,所以我们不能进行多次读取,而且只能是固定方向传送文件。
局限性:
- 数据不能同时读写
- 数据一旦读走便不在管道中存在,不可重读读取数据
- 数据采用半双工通信方式(数据只能在一个方向流动的通信方式,还有双向全双工、双向半双工、单工通信)
- 只能在有公共祖先的进程(有血缘关系的进程)间使用管道
pipe函数
头文件:#include<unistd.h>
函数声明:int pipe(int pipefd[2]);
规定fd[0]
读,fd[1]
写
返回值:成功返回0,失败返回-1,同时设置erron
为了保证管道的单向流动性需要在进程中关闭一部分文件描述符。
从管道中读取数据时当管道中没有数据将会自动阻塞,直到有数据写入。
读管道:
- 如果管道中有数据,则read返回实际读到的字节数
- 如果管道中没有数据,若写端关闭,read返回0,如果仍有写端打开,则阻塞等待写入。
写管道:
- 如果读端全关闭,则进程异常终止(SIGPIPE信号)
- 如果读端打开,若管道未满,则写入数据,返回写入字节数。若管道已满,则阻塞(这种情况很少出现)。
当写进程向管道中写入时,它利用标准的库函数write(),系统根据库函数传递的文件描述符,可找到该文件的 file 结构。file 结构中指定了用来进行写操作的函数(即写入函数)地址,于是,内核调用该函数完成写操作。写入函数在向内存中写入数据之前,必须首先检查 VFS 索引节点中的信息,同时满足如下条件时,才能进行实际的内存复制工作:
1.内存中有足够的空间可容纳所有要写入的数据;
2.内存没有被读程序锁定。
如果同时满足上述条件,写入函数首先锁定内存,然后从写进程的地址空间中复制数据到内存。否则,写入进程就休眠在 VFS 索 引节点的等待队列中,接下来,内核将调用调度程序,而调度程序会选择其他进程运行。写入进程实际处于可中断的等待状态,当内存中有足够的空间可以容纳写入 数据,或内存被解锁时,读取进程会唤醒写入进程,这时,写入进程将接收到信号。当数据写入内存之后,内存被解锁,而所有休眠在索引节点的读取进程会被唤醒。
管道的读取过程和写入过程类似。但是,进程可以在没有数据或内存被锁定时立即返回错误信息,而不是阻塞该进程,这依赖于文件或管道的打开模式。反之,进程可 以休眠在索引节点的等待队列中等待写入进程写入数据。当所有的进程完成了管道操作之后,管道的索引节点被丢弃,而共享数据页也被释放。
参考博客 linux管道详解
获取管道缓冲区的大小:ulimit -a
优点:实现手段简单
缺点:单向通信、只有血缘关系进程间使用