前言
这一周来过的很浮躁,感觉什么也没干,时间就这么过去了。实习工作需要看协议,看的头昏脑胀,晚上还得写论文。Nginx基本没时间碰,略吐槽下。。白天工作时间趁着看别人修改的协议代码机会,顺便复习下代码中用到的管道机制,简单记录下。另外,顺利入手第一台单反,我大尼康D7000~
管道
管道是最早类型的IPC形式了,所有Unix类系统都提供管道机制。它由pipe函数提供单路(单向)数据流。原型如下:
int pipe(int fd[2]);
该函数返回两个文件描述符:fd[0]和fd[1]。前者用来读,后者用来写。
这种方式属于半双工管道,常用于shell中。
创建一个全双工IPC管道可以使用socketpair函数实现。
通常,管道由单个进程创建,却很少在单进程内使用。
管道的典型用途是用于父子进程间的通信。由一个进程(将成为父进程)创建一个管道后调用fork派生一个子进程,具体如下图:
接着,父进程关闭管道的读出端,子进程关闭写入端,即完成单向数据管道,如下图:
当需要双向数据流时,我们可创建两个管道,每个方向一个即可。如下图:
FIFO
管道的最大劣势是只能用于有一个共同祖先进程的各个进程之间。在无亲缘关系的两个进程间创建一个管道并用于IPC是不行的。FIFO就是指先进先出,也就是有名管道(named pipe)。它不同于管道的是,
每个FIFO均与一个路径名关联,从而允许无亲缘关系的进程可以同时访问一个FIFO。
原型如下:
int mkfifo(const char *pathname, mode_t mode);
mode参数类似与open()的第二个参数。
mkfifo函数已隐含指定O_CREAT | O_WXCL。它要么创建一个新的FIFO,要么返回错误。如果要打开一个已存在的FIFO或创建一个新的FIFO,应先调用mkfifo,再检查其是否返回错误,若返回错误,应该改为调用open()。
可以从shell脚本或命令行中使用mkfifo命令。具体请参考《网络编程卷二》
管道和FIFO额外属性
笔记:对一个描述符设置非阻塞的两种方法
- 调用open()函数时指定O_NONBLOCK标志;
- 描述符已被打开,调用fcntl()以启用O_NONBLOCK标志;
关于管道和FIFO的读出与写入的额外规则:
- 1,如果请求读出的数据量多于管道或FIFO中当前可用数量,那么只返回可用的数量;
- 2,如果请求写入的数据的字节数小于或等于管道的缓存大小,那么write操作能保证是原子操作。也就是说,系统不会相互混杂两个进程的数据,必须等一个进程写完所有数据,另一个进程才能开始写(重要);否则,不能保证;
- 3,O_NONBLOCK标志的设置对write操作的原子性无影响,原子性完全由所请求字节数小于等于管道缓存大小决定;但如果管道或FIFO被设置非阻塞,write的返回值会取决于待写字节数以及该管道或FIFO中当前可用空间的大小。
当待写字节数小于等于缓存大小时,有以下情况:
- a)管道空间足够,全部写入;
- b)管道空间不足,立即返回错误;
当待写字节数大于缓存大小时,有以下情况:
- a)管道空间至少有1字节空间,有多少写入多少,write返回实际写入值;
- b)管道已满,立即返回错误;
- 4,如果为一个没有为读打开的管道或FIFO写入,内核产生SIFPIPE信号;
管道和FIFO限制
系统加于管道和FIFO的唯一限制:
- OPEN_MAX: 一个进程在任意时刻能打开的最大描述符;(通过sysconf命令查询);
- PIPE_BUF:管道缓存大小,可原子性写往一个管道或FIFO的最大数据量;
总结
管道机制最重要的一个优点就是
当写入数据量小于等于管道缓存大小时,能够保证原子性的操作。
主要参考
《网络编程卷二》