11_进程间通信
owner
perms
0x0052e6a9 0
postgres 600
0x0052e6aa 32769
postgres 600
0x0052e6ab 65538
postgres 600
0x0052e6ac 98307
postgres 600
0x0052e6ad 131076
postgres 600
0x0052e6ae 163845
进程间通信
概述
实现进程间通信(IPC)的方法很多,但从历史来 看,很多都不可移植,经过多年的发展,已经有 很大的统一
IPC的方法主要包括:
管道 消息队列 信号量 共享内存 套接字
管道
管道是最古老的IPC方式,在历史上,有两方面 的限制:
只能以半双工方式工作 管道的两端必须是具有公共祖先的进程
还可以通过多次调用fork在两个子进程之间连接 IPC通道
管道的一方进程应该关闭自己不需要的管道描述 符
fd[0]
fd[1]
父进程
fd[0]
fd[1]
子进程
管道 内核
从子进程到父进程的管道
fd[0] 父进程
fd[1] 子进程
管道 内核
两条规则
当管道的一端被关闭后,下列两条规则起作用
1. 当读一个写端被关闭的管道时,所有数据被读取 后,read返回0。(只要管道的写端还有进程, 就不会产生文件结束)
通过调用pipe系统调用可以创建一个无名管道 int pipe(int [2]);
由参数返回两个文件描述符:[0]为读而打开, [1]为写而打开,[1]的输出是[0]的输入
fd[0]
fd[1]
用户进程
管道 内核
使用管道
单个进程中管道基本没有什么用处
调用pipe创建管道后,再调用fork,就创建了 从父进程到子进程(或反向)的IPC通道
0x0052e2c5 360459
postgres 600
0x0052e2c6 393228
postgres 600
0x0052e2c7 425997
postgres 600
------ Message Queues --------
key
msqid
owner
perms
bytes 38207488 38207488
但最近的Unix系统大多都取消这两方面的限制, 但为了兼容性原因,很多应用我们仍然假设管道 只能以半双工方式工作
无名管道和命名管道
管道可以分为无名管道和命名管道两类
无名管道没有名字,只能工作在具有同一个祖先 进程的若干个进程中
命名管道,又叫FIFO管道,可以在任何两个进程 之间工作
无名管道
一个例子
------ Shared Memory Segments --------
key
shmid
owner
perms
0x0052e6a9 0
postgres 600
0x0052e2c1 32769
postgres 600
------ Semaphore Arrays --------
key
semid
FIFO管道的主要用途
1. 在进程之间传输数据,但不便于使用无名管道的 地方
2. 在客户进程-服务器进程之间进行通信
XSI IPC(XSI进程间通讯)
有3种IPC被称为XSI IPC:消息队列、信号量和 共享内存
XSI IPC没有使用文件系统名字空间,而是构造 了自己的名字空间
这3种IPCBiblioteka 有类似的处理方式nattch 4 4
status
nsems 17 17 17 17 17 17 17 17 17 17 17 17 17 17
used-bytes messages
消息队列
消息队列可以实现不同进程之间的消息传递
消息队列组织成一个链接表
消息的接收不一定按照先进先出的顺序,也可以 按照消息的类型接收
FIFO管道(命名管道)
无名管道只能工作在具有同一个祖先进程的若干 个进程中,FIFO管道可以克服这个问题
FIFO管道是一种文件类型,可以被创建在文件系 统中,并设置相应的权限
多个进程可以通过分别以读/写方式打开该FIFO 文件进行读/写,从而达到进行管道通信的目的
int mkfifo(char *pathname, mode_t mode)
XSI IPC结构
IPC结构是在系统范围内起作用的,它不属于某 一个进程
IPC对象在文件系统中没有名字,只能使用专门 的命令和函数访问这些对象,不能通过文件描述 符来访问
IPC相关的几个命令
Unix系统提供了几个操纵IPC对象的命令
ipcs: 列出系统中的IPC对象
ipcrm: 删除指定的IPC对象
消息队列是可靠的、有流控的、面向对象的
操纵消息队列
先通过ftok取得键值key
打开或创建消息队列 int msgget(key_t key, int flag);
发送消息 int msgsnd(int id, void *p, size_t nbytes, int flag);
接收消息 ssize_t msgrcv(int id, void *p, size_t nbytes, long type, int flag);
2. 如果写一个读端被关闭的管道,则产生信号 SIGPIPE,如果忽略或捕获该信号并返回,则 write返回-1,errno设置为EPIPE
一段典型的代码
int main() {
int fd[2];
pipe(fd); if ((pid_t pid = fork()) > 0) {
close(fd[0]); write(fd[1], ....); ...... } else { close(fd[1]); read(fd[0], ....); } }
postgres 600
0x0052e6af 196614
postgres 600
0x0052e2c1 229383
postgres 600
0x0052e2c2 262152
postgres 600
0x0052e2c3 294921
postgres 600
0x0052e2c4 327690
postgres 600
标识符和键
每个内核中的IPC对象都用一个非负整数的标识 符加以引用,它是IPC对象的内部名
为了使多个合作进程能共享使用同一IPC对象, Unix使用了外部名:键。每个IPC对象都和一个 键相关联
可以调用ftok函数通过全局唯一字符串和项目ID 产生键 key_t ftok(char *path, int id);