以UNIX为例来分析进程间通信的各种实现技术。
在UNIX中,文件(File)、信号(Signal)、无名管道(Unnamed Pipes)、有名管道(FIFOs)是传统IPC功能;新的IPC功能包括消息队列(Message queues)、共享存储段(Shared memory segment)和信号量(Semapores)。
1.信号
信号机制是UNIX为进程中断处理而设置的。它只是一组预定义的值,因此不能用于信息交换,仅用于进程中断控制。例如在发生浮点错、非法内存访问、执行无 效指令、某些按键(如ctrl-c、del等)等都会产生一个信号,操作系统就会调用有关的系统调用或用户定义的处理过程来处理。信号处理的系统调用是signal,调用形式是:
signal(signalno,action),其中,signalno是规定信号编号的值,action指明当特定的信号发生时所执行的动作。
2.无名管道
管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。无名管道是一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,并且只存在与内存中。下面是生成管道的函数原型:
#include <unistd.h> int pipe(int fd[2])
fd[0]中返回管道的读通道打开文件描述等,在fd[1]中返回管道的写通道打开文件描述符。
该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由 pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信。
向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。
3.有名管道
不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据。
下面是创建有名管道的函数原型:
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char * pathname, mode_t mode)
该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同。如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用于FIFO,如close、read、write等等。
4.消息队列
消息队列是内存中独立于生成它的进程的一段存储区,一旦创建消息队列,任何进程,只要具有正确的的访问权限,都可以访问消息队列,消息队列非常适合于在进程间交换短信息。
消息队列的每条消息由类型编号来分类,这样接收进程可以选择读取特定的消息类型——这一点与管道不同。消息队列在创建后将一直存在,直到使用msgctl系统调用或iqcrm -q命令删除它为止。
下面是关于消息队列的API:
//创建一个具有flag权限的MQ及其相应的结构,并返回一个唯一的正整数msqid(MQ的标识符); int msgget(key,flag) //向队列中发送信息; int msgsnd(msqid,msgp,msgsz,msgtyp,flag) //从队列中接收信息; int msgrcv(msqid,cmd,buf) //对MQ的控制操作; int msgctl(msqid,cmd,buf)
5.共享存储段
共享存储段是主存的一部分,它由一个或多个独立的进程共享。
下面是关于共享存储段的API
//创建大小为size的SM段,其相应的数据结构名为key,并返回共享内存区的标识符shmid; int shmget(key,size,flag) //将当前进程数据段的地址赋给shmget所返回的名为shmid的SM段; char shmat(shmid,address,flag) //从进程地址空间删除SM段; int shmdr(address) //对SM的控制操作; int shmctl (shmid,cmd,buf)
6.信号量
在UNIX中,信号灯是一组进程共享的数据结构,当几个进程竞争同一资源时(文件、共享内存或消息队列等),它们的操作便由信号灯来同步,以防止互相干扰。
信号灯保证了某一时刻只有一个进程访问某一临界资源,所有请求该资源的其它进程都将被挂起,一旦该资源得到释放,系统才允许其它进程访问该资源。信号灯通常配对使用,以便实现资源的加锁和解锁。