进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的程序进程,使之能在一个操作系统里同时运行。这使得一个程序能够在同一时间里处理许多用户的要求。因为即使只有一个用户发出要求,也可能导致一个操作系统中多个进程的运行,进程之间必须互相通话。IPC接口就提供了这种可能性。每个IPC方法均有它自己的优点和局限性,因此,对于单个程序而言使用所有的IPC方法是不常见的。
进程间通信主要包括系统IPC(进程间通信)(包括消息队列,信号,共享存储), 套接字(SOCKET),管道(PIPLE),信号量集
一、信号机制
1.信号机制的基本概念
信号机制主要是作为在同一用户的诸进程之间通信的简单工具。信号本身是一个1~19中的某个整数,用来代表某一种事先约定好的简单消息。每个进程在执行时,都要通过信号机制来检查是否有信号到达。若有信号到达,表示某进程已发生了某种异常事件,便立即中断正在执行的进程,转向由该信号(某整数)所指示的处理程序,去完成对所发生的事件(事先约定)的处理。处理完毕,再返回到此前的断点处继续执行。可见,信号机制是对硬中断的一种模拟,故在早期的UNIX版本中又称其为软中断。
信号机制与中断机制之间的相似之处表现为:信号和中断都同样采用异步通信方式,在检测出 有信号或有中断请求时,两者都是暂停正在执行的程序而转去执行相应的处理程序,处理完后 都再返回到原来的断点;再有就是两者对信号或中断都可加以屏蔽。
信号与中断两机制之间的差异是:中断有优先级,而信号机制则没有,即所有的信号都是平等的;再者是信号处理程序是在用户态下运行的,而中断处理程序则是在核心态下运行;还有,中断响应是及时的,而对信号的响应通常都有较长的时间延迟。
2.信号机制的功能
1) 发送信号
这是指由发送进程把信号送至指定目标进程的proc结构中信号域的某一位上。如果目标进程正在一个可被中断的优先级上睡眠,核心便将目标进程唤醒,发送过程就此结束。一个进程可能在其信号域中有多个位被置位, 代表已有多种类型的信号到达, 但对于一类信号, 进程却只能记住其中的某一个。进程可利用系统调用kill向另一进程或一组进程发送一个信号。
2) 设置对信号的处理方式
在UNIX系统中,可利用系统调用signal(sig,func)来预置对信号的处理方式。其中, 参数sig为信号名,func用于预置处理方式,可分成三种情况:
(1) func=1时,进程对sig类信号不予理睬,亦即屏蔽了该信号。
(2) func=0,即为缺省值时,进程在收到sig信号后应自我终止。
(3) func为非0、非1类整数时,就把func的值作为指向某信号处理程序的指针。
二、管道机制
1.管道的类型
1) 无名管道(Unnamed Pipes)
在早期的UNIX版本中,只提供了无名管道。这是一个临时文件,是利用系统调用pipe( )建立起来的无名文件(指无路径名)。 只用该系统调用所返回的文件描述符来标识该文件, 因而,只有调用pipe的进程及其子孙进程才能识别此文件描述符,从而才能利用该文件(管道)进行通信。当这些进程不再需要此管道时,由核心收回其索引结点。
2) 有名管道(Named Pipes)
为了克服无名管道在使用上的局限性,以便让更多的进程能够利用管道进行通信,在UNIX系统中又增加了有名管道。有名管道是利用mknod系统调用建立的、可以在文件系统中长期存在的、具有路径名的文件,因而其它进程可以感知它的存在,并能利用该路径名来访问该文件。对有名管道的访问方式像访问其它文件一样,都需先用Open系统调用将它打开。不论是有名管道、还是无名管道,对它们的读写方式是相同的。
2.对无名管道的读写
1) 对pipe文件大小的限制
为了提高进程的运行效率,pipe文件只使用索引结点中的直接地址i-addr(0)~i-addr(9)。如果每个盘块的大小为4 KB, 则pipe文件的最大长度被限制在40 KB之内。 核心将索引结点中的直接地址项作为一个循环队列来管理,为它设置一个读指针和一个写指针, 按先进先出顺序读写。
进程互斥
为使读、写进程互斥地访问pipe文件,只须使诸进程互斥地访问pipe文件索引结点中的直接地址项。因此,每逢进程在访问pipe文件前,都须先检查该索引结点是否已经上锁。 若已锁住,进程便睡眠等待;否则,将索引结点上锁,然后再执行读、写操作。操作结束后又将该索引结点解锁,并唤醒因该索引结点上锁而睡眠的进程。
进程写管道
当进程向管道写数据时,可能有以下两种情况:如果管道中有足够的空间能存放要写的数据,在每写完一(盘)块后,核心便自动增加地址项的大小。当写完i-addr(9)地址项中所指示的盘块时,便又向i-addr(0)地址项所指示的盘块中写数据,全部写完后,核心修改索引结点中的写指针,并唤醒因该管道空而睡眠等待的进程。如果管道中无足够的空间来存放要写入的数据,核心将对该索引结点做出标志,然后让写进程睡眠等待,直到读进程将数据从管道中读出后,才唤醒等待写进程
进程读管道
当进程从管道中读数据时,也同样会有两种可能:如果管道中已有足够的数据供进程读,读进程便根据读指针的初始值去读数据。每读出一块后,便增加地址项的大小。读完时,核心修改索引结点中的读指针,并唤醒所有等待的写进程。如果进程所要读的数据比管道中的数据多,则可令读进程把管道中已有数据读完后,暂时进入睡眠状态等待,直至写进程又将数据写入管道后,再将读进程唤醒。
三、消息机制
2.消息队列的建立与操作
1) 消息队列的建立
在一个进程要利用消息机制与其它进程通信之前,应利用系统调用msgget( )先建立一个指名的消息队列。对于该系统调用,核心将搜索消息队列头标表,确定是否有指定名字的消息队列。若无,核心将分配一个新的消息队列头标,并对它进行初始化,然后给用户返回一个消息队列描述符;否则,它只是检查该消息队列的许可权后便返回。
3.消息的发送和接收
1. 消息的发送
当进程要与其它进程通信时,可利用msgsnd( )系统调用来发送消息。对于msgsnd( )系统调用,核心检查消息队列描述符和许可权是否合法,消息长度是否超过系统规定的长度。 通过检查后,核心为消息分配消息数据区,并将消息从用户消息缓冲区拷贝到消息数据区。分配消息首部,将它链入消息队列的末尾;在消息首部中填写消息的类型、大小以及指向消息数据区的指针等;还要修改消息队列头标中的数据(如消息队列中的消息数、字节数等)。然后,唤醒在等待消息到来的睡眠进程。