| IPC | 类型 | 特点 |
|---|---|---|
| 管道 | FIFOs(命名管道) 管道为单一方向流动,进程之间具有公共祖先。 | 不相关进程可以使用 |
| 流管道 | 命名流管道 | |
| 双向管道 | 单个管道就可提供父子进程的双向流动 | |
| 消息队列 | 由关键字打开或者创建 | |
| 信号量 | 用来实现对共享存储存取同步 | |
| 共享存储 | 无需进行客户机和服务器之间的复制,最快的 IPC | |
| 三个V | IPC使用关键字 key 进行通信 | |
| 套接口 | 支持不同主机间各个进程间的 IPC | |
| 流 |
管道有两种限制:
int pipe (int filedes[2]);
功能:创建管道。
由参数 filedes 返回两个文件描述符:filedes[0] 为读而打开,filedes[1] 为写而打开;filedes[1] 的输出是 filedes[0] 的输入。
标准 I/O 提供的创建管道与关闭管道的函数:
FILE *popen(const char*cmdstring,const char *type);
int pclose(FILE*fp);
函数 popen 实现的方法:函数 popen 先执行 fork,然后调用 exec 以执行 cmdstring,返回一个标准 I/O 文件指针;如果 type 是 r,则文件指针连接到 cmdstring 的标准输出;如果 type 是 w,则文件指针连接到 cmdstring 的标准输入。
举例:父子进程通过管道通信
#include 'ourhdr.h'
int main (void){
int n,fd[2];
pid_t pid;
char line[MAXLINE];
if(pipe(fd) < 0)
err_sys(“pipe error”); //创建管道
if((pid = fork()) < 0)
err_sys(“fork error”);
else if(pid > 0){
close fd[0]; //关闭父进程的读端,此时子进程无法向父进程传送数据
write(fd[1],”123456\n”,7); // 打开父进程写端,给子进程传送数据
} else {
close fd[1]; //关闭子进程写端
n = read(fd[0],line,MAXLINE);
write(STDOUT_FILENO,line,n);}
exit(0);
}
FIFO称为命名管道,是一种文件类型,所以创建 FIFO 类似于创建文件:
int mkfifo(const char*pathname , mode_t mode);
功能:创建 FIFO。Mode 与 open 函数中一样。
FIFO 的阻塞标志:O_NONBLOCK;
打开 FIFO 时,此标志存在下列影响:
FIFO有两种用途:
1、FIFO 由 shell 命令使用以便将数据从一条管道线传送到另一条,为此无需 创建中间临时文件。
-> FIFO -> prog3
输入文件 -> prog1 -> tee |
-> prog2
2、FIFO 用于客户机-服务器应用程序中,以在客户机和服务器之间传递数据
创建系统 V IPC 中的任何一种 IPC 首先要指定关键字 key,关键字由系统内核变换为标识符,此后,此标识符用于进程间的通信。下面介绍几种使客户机和服务器在IPC结构上会合的方法。
对于是否创建一个IPC或者打开一个现存的 IPC,应该遵循下列规则:
关键字 key 是 IPC_PRIVATE 或者未与特定 IPC 相结合时,创建新的 IPC,此处的 IP_PRIVATE 是一个特殊的键值,为了访问一个现存的队列,不可以指定其为关键字.
系统 V IPC 为每一个IPC结构规定了许可权和所有者的 ipc_perm 结构
struct ipc_perm {
uid_ t uid ; // 所有者的有效ID
gid_ t gid;
uid_ t cuid // 创建者的有效ID
gid_ t cgid;
mode_t mode; // 读写许可权
ulong seq;
key_t key; // 关键字
}
注意:对于任何 IPC 结构都不存在执行许可权
消息队列是消息的链接表,存放在内核中由消息队列标识符(队列ID)标识,内核为消息队列设置了结构msgid_ds,此结构包含了队列的当前状态);
int msgget(key_t key, int flag) ;
功能:打开一个现存队列或创建一个新队列。返回队列 ID;当创建一个新队列时初始化 msqid_ds;
int msgctl(int msgid, int cmd, struct msgid_ds *buf);
功能:对指定的队列进行操作。
其中,msgid是要执行的队列ID,cmd是要执行的命令,buf为指向队列当前状态结构的指针;
cmd 解释如下:
int msgsnd(int msgid, const void *ptr, size_t nbytes, int flag);
功能:向指定队列(msgid)发送类型为 ptr 的消息数据。对于接受信息的进程则可以调取消息类型来以非先进先出的顺序读取消息; flag 的值可以指定为 IPCNOWAIT。指定其为非阻塞
int msgrcv(int msgid, void*ptr,size_t nbytes,long type, int flag)
功能:从指定的队列中读消息,该函数为阻塞式调用。
从指定队列 msgid 中读取类型为 type 的消息,nbytes 说明所要接受的数据缓存的长度,参数 type 解释如下:
信号量是一个计数器,用来实现多进程对共享数据的存取。
同消息队列一样,内核也为信号量设置了结构semid_ds结构,此结构包含了信号量的当前状态;
下面介绍进程获得共享数据的步骤:
系统 V 的信号量:系统V的信号量较复杂,它的信号量并非是一个非负值,而且是一个或多个信号量值的集合.创建信号时需要指定该集合中的各个值;
semget (key_t key, int nsems, int flag);
功能:创建一个新集合或是引用一个现存的集合。由key决定;当创建一个新集合时,对semid_ds结构的相应成员赋初值, nsems 为信号量数,flag 设置许可权位;
注意: nsems是该集合中的信号量数,若创建新集合,必须设置nsems,若引用一个现存的集合,则将nsems指定为0。
int semctl(int semid, int semnum, int cmd,union semun arg);
功能:对信号量进行操作的函数。cmd指定执行的命令, semnum指定该集合中的某一个成员,区别是此函数的第三个参数是一个联合而并非指针。
semop(int semid,sruct sembuf semoparray[ ],size_t nops);
功能:此函数自动执行ID为semid的信号量集合上的操作数组。其中, nops规定了数组中操作的数量,第二个参数如下:
struct sembuf {
ushort sem_num;
short sem_op;
short sem_flag; // 可以设置 IPC_NOWAIT,或者 SEM_UNDO
}
对集合中每个成员的操作由相应的 sem_op 规定:
共享存储(信号量被用来实现对共享存储存取的同步)
共享存储允许两个或多个进程共享一给定的存储区。因为数据不需要在客户机和服务器之间复制,所以这是最快的一种IPC。
同消息队列,信号量一样,共享存储有内核分配的结构:shmid_ds;
几个基本控制函数:
int shmget(key_t key, int size, int flag);
功能:获得或者创建一个共享存储标识符。size 是该共享存储段的最小值。
int shmctl(int shmid, int cmd, struct shmid_ds buf);
功能:对共享存储段执行多种操作。指定 cmd 命令在 shmid 上执行。
区别:进程对共享字段进行操作时,需要先进行地址映射,结束之后需要对此映射地址进行脱节。
void *shmat(int shmid, void *addr, int flag) ;
功能:映射地址的函数。
flag 中指定了 SHMRDONLY 位,则以只读方式连接此段。否则以读写方式连接此段。addr 解释如下:
int shmdt(void *addr); 当对共享存储段的操作已经结束时,则调用 shmdt 脱节该段。

