APUE第15章 进程间通讯
IPC类型
- 半双工管道、FIFO
- 全双工管道、命名全双工管道
- XSI消息队列、XSI信号量、XSI共享存储
- 消息队列(实时)、信号量、共享存储(实时)
- 套接字
管道
管道一般是半双工的,即便全双工也可以在半双工的条件下工作,一般为了移植性考虑不能假设他是全双工工作的。管道只能在具有公共祖先的进程中使用。所以一般是父进程创建管道后fork。而FIFO没有这种局限性。
1 |
|
单个进程打开管道并没有什么意义,一般是fork后,父进程关闭读/写端,子进程关闭写/读端,当然也可以不关闭,只要注意是半双工的,同一时间消息只能有一个方向。
- 当读一个写端关闭的管道时,数据读完后再read会返回0,表示到文件尾,如果读端仍有进程则不会有这种情况。
- 如果写一个读端关闭则产生SIGPIPE信号,捕获或忽略后write返回-1,errno为EPIPE
- 写管道时,常量PIPE_BUF规定了内核缓冲区大小,如果写的时候字节数比这个小,则不会和其他写这个管道的write进行交叉操作,如果超过了,那么所写数据可能会与其他进程相互交叉(即不能保证原子性)。用
1
2
3
4
5
6
7
84. 管道设置非阻塞模式的时候read和write失败都会立即返回-1
### popen和pclose
popen的作用一般是先fork然后调用exec执行cmdstring,并返回一个标准I/O文件指针,如果type是"r",则文件指针连接到cmdstring的标准输出;如果type是"w",则文件指针指向cmdstring的标准输入。
```cpp
#include <stdio.h>
FILE *popen(const char *cmdstring, const char *type);
int pclose(FILE *fp);//若成功返回cmdstring的终止状态,出错返回-1
pclose执行时,关闭标准I/O流,然后等待cmdstring的返回结果。
Notes:如果cmdstring不能执行,则pclose返回的终止状态与shell已执行exit(127)一样。
FIFO
命名管道。主要不同是让不相关的进程之间也可以交换数据。FIFO是一张文件类型。
1 |
|
- 如果path是绝对路径的时候,fd参数会被忽略
- 如果path是相对路径,则相对于fd(必须是目录)所在位置进行变化
- 如果path是一个特注释AT_FDCWD,则从当前路径开始
- 不论使用哪个函数,之后都需要通过open来打开
FIFO的设置和出错与管道基本类似,详情见《APUE》P446
XSI IPC
标识符和键
标识符是IPC对象的内部名,键(key)是IPC对象的外部名,一般创建XSI IPC的函数返回的都是key_t类型的键。
权限结构
XSI IPC为每一个IPC都关联了一个ipc_perm结构,规定了权限和所有者,至少包括以下成员(定义于1
2
3
4
5
6
7
8
9```cpp
struct{
uid_t uid; //owner's effective user id
gid_t gif; //owner's effective group id
uid_t cuid; //creator's effective user id
git_t cgid; //creator's effective group id
mode_t mode;//access mode
//......
};
优点和缺点
XSI IPC最基本的问题是没有引用计数,所以如果创建了消息队列,然后终止,那么内容不会被删除,而对于管道,如果引用计数为0就被删除了;对于命名管道,引用计数为0时内部内容销毁,仅保留命名管道的名字,直到被显式删除。
第二个问题是XSI IPC在文件系统中没有名字,无法使用文件处理的函数对他们的属性进行修改。这些形式的IPC不适用文件描述符,所以无法使用I/O多路复用。
优点是比较可靠,因为这些IPC形式被限制在一台主机上。
消息队列
1 | struct msgid_ds{ |
以上结构定义了队列的当前状态
1 |
|
信号量
本质是个计数器,对共享数据对象进行保护。要获得共享数据对象,应该先获得控制该资源的信号量,再获取,使用完,释放信号量使其加一。
1 | struct semid_ds{ |
共享存储
1 | struct shmid_ds{ |
POSIX信号量
1 |
|
一般而言sem_getvalue拿到后可能已经被修改了,所以一般只用于调试,使用这种信号量的原因是它比XSI IPC的信号量在Solaris系统中快了12%,在Linux上高了94%,因为Linux的实现将文件引射到了进程地址空间中,没有使用系统调用来操作各自的信号量。