目录
一、进程通信的目的
数据传输:进程发送数据到另一个进程。
共享数据:多个进程想要操作共享的数据。
通知事件:当某些事件发生时,向另外的进程发通知报告事件的发生。
资源共享:要求相互操作的进程自定义的一些协议,需要访问特定的资源。
进程控制:一些特殊进程控制另一个进程的执行。
二、进程间通信机制概述
现在linux使用的进程间通信方式
(1)管道(pipe)和有名管道(FIFO)
(2)信号(signal)
(3)消息队列
(4)共享内存
(5)信号量
(6)套接字(socket)
1.1信号
信号(signal)机制是Unix系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。很多条件可以产生一个信号。
概念
1、信号(Signal):信号是在软件层次上对中断机制的一种模拟,信号的实质是软件中断。 用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一样的。
2、信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
3、信号是进程间通信机制中唯一的异步通信机制。
信号的特点
4、在一个信号的生命周期中有两个阶段:生成和传送。当一个事件发生时,需要通知一个进程,这时生成一个信号。当进程识别出信号的到来,就采取适当的动作来传送或处理信号。在信号到来和进程对信号进行处理之间,信号在进程上挂起(pending)。
5、信号没有固有的优先级。如果为一个进程同时产生了两个信号,这两个信号会以任意顺序出现在进程中并会按任意顺序被处理。另外,也没有机制用于区分同一种类的多个信号。如果进程在处理某个信号之前,又有相同的信号发出,则进程只能接收到一个信号。进程无法知道它接收了1个还是42个SIGCONT信号。
信号的局限性
6、信号的花销太大。发送信号要做系统调用;内核要中断接收进程、要管理它的堆栈、要调用处理程序、要恢复被中断的进程等。
7、信号种类有限,而且信号能传递的信息量十分有限。
8、信号没有优先级,也没有次数的概念。
9、所以,信号对于事件通知很有效,但对于复杂的交互操作却难以胜任。
1.2管道
管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。
写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。
管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。管道主要用于不同进程间通信。
概念
1、在Linux中,使用两个file数据结构来实现管道。这两个file数据结构中的f_inode(f_dentry)指针指向同一个临时创建的VFS i节点,而该VFS i节点本身又指向内存中的一个物理页。两个file数据结构中的f_op指针指向不同的文件操作进程的指针矢量:一个用于向管道中写,另一个用于从管道中读。
2、从进程的角度来看,读写管道的系统调用和读写普通文件的普通系统调用没什么不同。当写进程向管道中写时,字节被拷贝到了共享数据页,当读进程从管道中读时,字节被从共享页中拷贝出来。
3、Linux必须同步对于管道的存取,必须保证管道的写和读步调一致。Linux使用锁、等待队列和信号(locks,wait queues and signals)来实现同步。
命名管道
1、Linux也支持命名管道(也叫FIFO,因为管道工作在先入先出的原则下,第一个写入管道的数据也是第一个被读出的数据)。==与管道不同,FIFO不是临时的对象,它们是文件系统中真正的实体,可以用mkfifo命令创建。==只要有合适的访问权限,进程就可以使用FIFO。
2、FIFO的打开方式和管道稍微不同。一个管道(它的两个file数据结构、VFS i节点和共享数据页)是一次性创建的,而FIFO已经存在,可以由它的用户打开和关闭。
3、Linux必须处理在写进程打开FIFO之前读进程对它的打开,也必须处理在写进程写数据之前读进程对管道的读。除此以外,FIFO几乎和管道的处理完全一样,而且它们使用一样的数据结构和操作。
管道的局限性
1、从IPC的角度看,管道提供了从一个进程向另一个进程传输数据的有效方法。但是,管道有一些固有的局限性:
2、因为读数据的同时也将数据从管道移去,因此,管道不能用来对多个接收者广播数据。
3、管道中的数据被当作字节流,因此无法识别信息的边界。
4、如果一个管道有多个读进程,那么写进程不能发送数据到指定的读进程。同样,如果有多个写进程,那么没有办法判断是它们中哪一个发送的数据。
1.3System V IPC机制
前面讨论的信号和管道虽然可以在进程之间通信,但还有许多应用程序的IPC需求它们不能满足。因此在System V UNIX(1983)中首次引入了另外三种进程间通信机制(IPC)机制:消息队列、信号灯和共享内存 message queues,semaphores and shared memory)。
1、System V IPC机制共享通用的认证方式。进程在使用某种类型的IPC资源以前,必须首先通过系统调用创建或获得一个对该资源的引用标识符。进程只能通过系统调用,传递一个唯一的引用标识符到内核来访问这些资源。在每一种机制中,对象的引用标识符都作为它在资源表中的索引。但它不是直接的索引,需要一个简单的操作来从引用标识符产生索引。对于System V IPC对象的访问,使用访问许可权检查,这很象对文件访问时所做的检查。System V IPC对象的访问权限由对象的创建者通过系统调用设置。
2、系统中表示System V IPC对象的所有Linux数据结构中都包括一个ipc_perm数据结构,用它记录IPC资源的认证信息。
3、在ipc_perm数据结构中包括了创建者进程的用户和组标识、所有者进程的用户和组标识、对于这个对象的访问模式(属主、组和其它)以及IPC对象的键值(key)。Linux通过key 来定位System V IPC对象的引用标识符,每个IPC对象都有一个唯一的key。每种IPC机制都提供一种系统调用,用来将键值(key)转化为对象的引用标识符。
消息队列
1、消息队列就是一些消息的列表。用户可以从消息队列中添加消息和读取消息等。
2、一个或多个进程可向消息队列写入消息,而一个或多个进程可从消息队列中读取消息。
3、这种进程间通信机制通常使用在客户/服务器模型中,客户向服务器发送请求消息,服务器读取消息并执行相应请求。
4、Linux中的消息可以被描述成在内核地址空间的一个内部链表,每一个消息队列由一个IPC的标识号唯一的标识。
5、链表中的每一个元素都是一个指向msqid_ds数据结构的指针,而一个msqid_ds数据结构完整地描述了一个消息队列。
6、两个进程等待队列(wwait、rwait)分别表示等待向消息队列中写的进程(wwait)和等待从消息队列中读的进程(rwait)。如果某进程向一个消息队列发送消息而发现该队列已满,则进程挂在wwait队列中等待。从该消息队列中读取消息的进程将从队列中删除消息,从而腾出空间,再唤醒wwait队列中等待的进程。如果某进程从一个消息队列中读消息而发现该队列已空,则进程挂在rwait队列中等待。向该消息队列中发送消息的进程将消息加入队列,再唤醒rwait队列中等待的进程。
信号量
1、为了防止出现因多个程序同时访问一个共享资源而引发的问题,我们需要使执行数据更新的程序代码独占式的执行,也就是任一时刻只有一个进程访问共享资源,这时就需要信号量的帮助。
2、最简单的信号量是只能取0和1的变量,即二进制信号量,这也是信号量最常见的一种形式。可以取多个正整数值的信号量称为通用信号量。
3、所有的Linux信号量函数都是针对成组的通用信号量进行操作的,这可以在一个进程需要锁定多个资源时发挥优势。
共享内存
1、共享内存可以被描述成内存一个区域的映射,这个区域可以被更多的进程所共享。这是IPC机制中最快的一种形式,因为它不需要中间环节,而是把信息直接从一个内存段映射到调用进程的地址空间。
2、一个内存区域可以直接由一个进程创建,随后,可以有任意多的进程对其读和写。但是,一旦内存被共享之后,对共享内存的访问同步需要由其它IPC机制,例如信号量来实现。