一、管道
- 局限性
(1)半双工。
(2)只能在具有公共祖先的两个进程间通信。 - 创建
#include <unistd.h>
int pip(int fd[2]);
参数fd返回两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。fd[1]写入,fd[0]读出。
3. 经由父进程向子进程传送数据的例子
#include "apue.h"
int main()
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];
if(pipe(fd)<0)
printf("pipe error");
if((pipe(fd)==fork())<0)
printf("fork error");
else if(pid>0)//parent
{
close(fd[0]);
write(fd[1],"hello world\n",12);
}
else//child
{
close(fd[1]);
n=read(fd[0],line,MAXLINE);
write(STDOUT_FILENO,line,n);
printf("%s",line);
}
exit(0);
}
- 函数popen和pclose
#include <stdio.h>
FILE *popen(const char *cmdstring,const char *type);
int pclose(FILE *fp);
创建一个管道,fork一个子进程,关闭未使用的管道,执行一个shell命令,然后等待命令终止。
二、FIFO
- 不相关的进程间交换数据(命名的管道)
- 创建
#include <sys/stat.h>
int mkfifo(const char *path,mode_t mode);
int mkfifoat(int fd,const char *path,mode_t mode);
path为绝对路径,fd参数被忽略,mkfifo与mkfifoat相似;path相对路径,fd是一个打开目录的有效文件描述符;path相对路径,且fp有特殊值AT_FDCWD,则路径名以当前目录开始,mkfifo与mkfifoat相似。
3. 用途
shell命令使用FIFO将数据从一条管道传送到另一条时,无需创建中间临时文件;客户进程-服务器进程应用程序中,FIFO用作汇聚点,在客户进程和服务器进程二者之间传递数据。
三、 消息队列
- 消息的链接表,存储在内核中,由消息队列标识符标识。
- 创建
#include <sys/msg.h>
int msgget(key_t key,int flag);//打开一个现有队列或是创建一个新队列
int msgsnd(int msqid,const void*ptr,size_t nbytes,int flag);//将数据放入到消息队列中,消息总是放在队列尾端
ssize_t msgrcv(int msqid,void *ptr,size_t nbytes,long type,int flag);//从消息队列中取用消息
ptr参数指向一个长整型数(其中存储的是返回的消息类型),其后面是存储实际消息数据的缓冲区。nbytes指定了数据缓冲区的长度。type=0,返回队列中的第一个消息;type>0,返回队列中消息类型为type的第一个消息;type<0,=,返回队列中消息类型值小于等于type绝对值的消息,如果这种消息有多个,则取类型值最小的。
四、信号量
信号量与前面的IPC机构(管道、FIFO以及消息队列)不同,它是一个计数器,用于为多个进程提供对共享数据对象的访问。
1. 为获得资源进程的操作:测试控制所需资源的信号量;若该资源的信号量的值为正,则可以使用该资源,获得资源使用权后,信号量的值会减1; 若信号量的值为0,则进程进入休眠状态,等到信号量的值大于0时才被唤醒。
2. 内核为信号量维护的semid_ds结构
struct semid_ds{
struct ipc_perm sem_perm;//权限结构
unsigned short sem_nsems;//of semaphores in set
time_t sem_otime;//last-semop() time
time_t sem_ctime;//last-change time
};
- 每一个信号量有一个无名结构表示
struct{
unsigned short semval;//semaphore value,always>=0
pid_t sempid;//pid for last operator
unsigned short semncnt;//processes awaiting semval>curval;
unsigned short semzcnt;//processes awaiting semval==0
};
- 创建
#include <sys/sem.h>
int semget(key_t key,int nsems,int flag);
若成功返回一个信号量的ID,key为信号量对象的外部名,其中nsems表示集合中的信号量数。
五、共享存储
允许两个或是多个进程共享一个给定的存储区。数据不需要在客户和服务器进程间复制,所以这是最快的一种IPC。使用共享存储需要知道,在多个进程之间同步的访问一个给定的存储区。
1. 共享存储的结构
struct{
struct ipc_perm shm_perm;//规定权限和所有者
size_t shm_segsz;//size of segment in bytes
pid_t shm_lpid;//pid of last shmop()
pid_t shm_cpid;//pid of creator
shmatt_t shm_nattch;//number of current attaches
time_t shm_atime;//last-attach time
time_t shm_dtime;//last-detach time
time_t shm_ctime;//last-change time
};
- 创建
#include<sys/shm.h>
int shmget(key_t key,size_t size,int flag);
获得一个存储标识符,在创建新的IPC结构时,如果key是IPC_PRIVATE或者和当前某种类型的IPC结构无关,则需要指明flag的IPC_CREAT标志位。为了引用一个现有的队列,key必须等于队列创建时指明的key的值,且IPC_CREAT必须不被指明。msgget和semget也是这样的。
3. 位置:共享存储段紧靠在栈之下
六、信号量、记录锁和互斥量的比较
- 信号量:先创建一个包含该信号量的集合,然后再将该信号量初始化为1。分配资源,以sem_op为-1调用semop;释放资源,以sem_op为1调用semop。对每个操作都指定SEM_UNDO,以处理在为释放资源条件下进程终止的情况。
- 记录锁:先创建一个空文件,且用该文件的第一个字节作为锁。分配资源,先对该字节获得一个写锁;释放资源,则对该字节解锁。记录锁的性质确保了当一个锁的持有者进程终止时,内核会自动释放该锁。
- 互斥信号量:需要所有的进程将相同的文件映射到他们的地址空间里,且使用PTHREAD_PROCESS_SHARED互斥量属性在文件的相同偏移处初始化互斥量。分配资源,对互斥量加锁;释放资源,解锁互斥量。如果一个进程没有释放互斥量而终止,恢复很困难,除非是用鲁棒性的互斥量。