- UNIX IPC 摘要
UDS(Unix Domain Socket)
管道
- 管道的限制
- 历史上是半双工的,处于兼容性考虑,不应该作出支持全双工的假设
- 只能在具有公共祖先的两个进程间使用
- 管道的操作
- 创建管道
int pipe(int fd[2]);
由参数返回两个 fdfd[0]
用于读fd[1]
用于写fd[1]
的输出是fd[0]
的输入
- fstat 测试管道
- 创建管道
- 进程与管道的示意图
- 单进程
- 父、子进程,先调用 pipe 再调用 fork,然后关闭部分 fd 达到想要的数据流向
- 单进程
- 可以用两个管道做父、子进程同步,实现
TELL_WAIT
/TELL_PARENT
/TELL_CHILD
/WAIT_PRENT
/WAIT_CHILD
,read 操作阻塞,write 唤醒
协同进程
- 协同进程:通常在 shell 后台运行,其标准输入和标准输出通过管道连接到另一个程序
命名管道 FIFO
- 为什么需要命名管道:因为匿名管道只能在两个相关的进程间使用,并且这两个相关的进程还必须有一个公共祖先进程,而FIFO 可以使不同的进程之间交互数据
- 如何使用
- 创建:
int mkfifo(const char *path, mode_t mode)
:mode 与 open 的 mode 相同,创建成功返回 0,否则返回 -1 - 创建之后需要用
open
打开O_NONBLOCK
标志的影响- 没有指定
O_NONBLOCK
的时候,只读 open 要阻塞到某个进程为写打开一个 FIFO 为止,只写 open 阻塞到某个进程为读打开一个 FIFO 为止 - 如果指定了
O_NONBLOCK
,并且此时没有进程为写打开 FIFO,只读 open 会立即返回 -1,并将 errno 设置为ENXIO
- 没有指定
- 创建:
- 用途
- shell 命令使用 FIFO 将数据从一条管道传递到另一条时,无需创建中间临时文件
- 客户进程-服务器进程应用程序中,FIFO 用作汇聚点,在客户进程和服务器进程之间传递数据
- shell 命令使用 FIFO 将数据从一条管道传递到另一条时,无需创建中间临时文件
XSI IPC
- 内核 IPC 结构:消息队列、信号量、共性存储器
- 每个内核中的 IPC 结构用一个非负正数 id 引用
- 如向一个消息队列发消息,只用知道其队列 id
- 消息队列创建再删除,相关 id 连续加 1,直至 int 最大值,然后 reset 到 0
- IPC 结构外部命名:使多个合作进程能在同一个 IPC 结构上汇聚,即需要提供一个 key
- 实现方式
- 服务器进程指定 key IPC_PRIVATE 创建一个新 IPC 结构,所返回的标识符可供 fork 出的子进程使用,子进程又可将此标识符作为 exec 的一个参数传给新程序
- 在公共头文件中定义 client/server 公认的 key,然后 server 以此 key 创建一个 IPC 结构用以通信
- client 和 server 认同一个路径名和项目 id(0-255 间的字符),然后使用 ftok 然后将这两个值变为一个 key,然后用 2 中的方法使用该 key
- 实现方式
- IPC 的问题
- 创建 IPC 的进程退出后,IPC 结构(如消息队列)不会删除、IPC 中的数据也不会删除
- IPC 不用 fd,因此不能使用 IO 多路复用
- 消息队列:存储在内核中的消息链表,支持先进先出和按消息的类型字段取消息
- 每个队列都有一个 msqid_ds 结构,包含了权限、消息数、队列最大长度(byte)
- 操作
msgsnd
发消息msgrcv
接收消息msgctl
获取 msqid_ds 结构/设置权限或队列祖达长度/删除队列及队列中的消息
- 信号量:计数器,用于为多个进提供对共享数据对象的访问
- 访问共享数据前需要获得信号量,如果获取失败,则被阻塞;使用完需要释放信号量
- 操作
semget
使用 XSI 信号量前需要先获得一个信号量 ID,如果是新创建需要指定信号量数,否则指定为 0semctl
操作信号量,包括获取信号量状态、设置权限、删除信号量、获取 semval、设置 semval、获取 pid、获取 semncnt、获取 senzcnt、获取所有信号量的值、设置所有信号量的值semop
自动执行信号量集合上的操作数组,提供原子性保
- 共享存储:允许两个或多个进程共享(同步访问)一个给定的存储区
- 内核为每个共享存储段维护一个数据结构,包括访问权限、段大小(byte)、创建进程的 pid、上次操作共享存储的 pid、当前共享存储段 attach 技术、last-attach time、last-detach time、last-change time
- 操作
shmget
创建新的或引用现有的共享存储段shmctl
操作共享存储段,包括获取 shmid_ds 结构、设置权限或 uid/gid、删除共享存储段- SHM_LOCK/SHM_UNLOCK 对共享存储段加锁/解锁,只有特权用户可以,不是 Single UNIX Specification 定义的
shmat
将共享存储段连接(attach)到调用进程的某个地址上,可以通过参数指定由内核指定地址还是用户进程指定地址shmdt
讲共享存储段与调用进程的地址分离(detach),并不会删除共享存储段
- 使用 mmap + /dev/zero 的方式可以在相关联的进程之间实现共产存储段类似的效果
- POSIX 信号量:简化了 XSI 信号量的接口,优化了删除时的行为,POSIX 信号量删除后仍然可以正常工作,直到该信号量的最后一次引用被释放
- 匿名信号量:只在内存中存在,要求使用信号量的进程必须可以访问内存