Linux间进程通信
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Linux进程间通信
1 进程间通信概述
由于进程都拥有各自的用户地址空间,彼此之间是互相独立的,所以进程间不能直接访问。
而很多情况下正需要这种进程间的通信,来实现多进程协同工作。
因此内核提供了一种机制解决了这一问题,这就是进程间通信(IPC,Inter-Process Communication)。
进程间通信主要有以下几个目的:
·数据传输:一个进程需要将它的数据发送给另一个进程。
·资源共享:多个进程之间共享同样的资源。
·通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件。
·进程控制:有些进程希望完全控制另一个进程的执行。
Linux 下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的。
A T&T的贝尔实验室对Unix早期的进程间通信手段进行了系统的改进和扩充,形成了“system V IPC”,通信进程局限在单个计算机内。
而BSD(加州大学伯克利分校的伯克利软件发布中心)则跳过了该限制,形成了基于套接口(socket)的进程间通信机制。
由于Unix版本的多样性,电子电气工程协会(IEEE)开发了一个独立的Unix标准,这个新的ANSI Unix标准被称为计算机环境的可移植性操作系统界面(PSOIX,Protable Operating System Interface),现有大部分Unix和流行版本都是遵循POSIX标准,而Linux从一开始就遵循POSIX标准,拥有POSIX IPC。
则Linux支持的IPC如下图所示:
图1 Linux所继承的进程间通信
其中,最初Unix IPC包括:管道、FIFO、信号;
System V IPC包括:System V消息队列、System V信号灯、System V共享内存区;
POSIX IPC包括:POSIX消息队列、POSIX信号灯、POSIX共享内存区。
现在在Linux下使用较多的进程间通信方式主要有以下几种:
(1)管道(Pipe):管道又分为无名管道(Pipe)与有名管道(FIFO),管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
(2)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;
(3)消息队列:消息队列是消息的链接表,包括POSIX消息队列system V消息队列。
有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。
消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
(4)共享内存:可以说这是最有用的通信方式。
它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。
这种通信方式依靠某种同步机制,如互斥锁与信号量等。
(5)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
(6)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信,应用非常广泛。
2 管道通信
什么是管道?简单地说,管道就是连接一个进程的输出和另一个进程的输人的单向通道。
管道通信是一种由文件系统实现的进程间通信方式,通过创建管道文件,并让一个进程读,一个进程写来实现进程间的通信,如下图所示。
图2 管道通信示意图
管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种:前者可用于具有亲缘关系进程间的通信,即可用于父子进程或兄弟进程间的通信;后者克服了管道没有名字的限制,因此,除具有前者所具有的功能外,它还允许无亲缘关系进程间的通信,即可用于运行于同一台机器上的任意两个进程间的通信。
2.1 无名管道(PIPE)
2.1.1 无名管道的创建
创建管道可以通过调用pipe()来实现。
管道两端可分别用描述字filedes[0]以及filedes[1]来描述,需要注意的是,管道的两端是固定了任务的,即filedes[0]只能用于读,filedes[1]只能用于写。
管道关闭时只需将filedes[0]与filedes[1]这两个文件描述符关闭即可,可使用普通的close() 函数逐个关闭各个文件描述符。
2.1.3 无名管道的读写
用pipe()函数创建的管道两端处于一个进程中,由于管道是主要用于在不同进程间通信的,因此这在实际应用中没有太大意义。
实际上,通常先是创建一个管道,再通过fork()函数创建一子进程,这时,该子进程会继承父进程所创建的管道,父子进程分别拥有自己的读写的通道,为了实现父子进程之间的读写,只需把无关的读端或写端的文件描述符关闭即可。
例如在图3中把父进程的写端filedes[1]和子进程的读端filedes[0]关闭。
父子进程之间就建立起了一条“子进程写入父进程读”的通道。
图3 无名管道通信
同样,也可以关闭父进程的filedes[0]和子进程的filedes[1],这样就可以建立一条“父进程写,子进程读”的通道。
另外,父进程还可以创建多个子进程,各个子进程都继承了相应的filedes[0]和filedes[1],这时,只需要关闭相应端口就可以建立其各子进程之间的通道。
一般文件的I/O函数都可以用于管道,则管道就可以使用普通的read()和write()函数进行读写。
2.1.4无名管道应用实例
实例一:用于shell
管道可用于输入输出重定向,它将一个命令的输出直接定向到另一个命令的输入。
比如,当在某个shell 程序(Bourne shell或C shell等)键入kill –l | grep SIGRTMIN后,相应shell程序将创建kill –l以及grep SIGRTMIN两个进程和这两个进程间的管道。
该命令的具体含义:列出所有的信号名,并在这些信号名中查找带有SIGRTMIN的信号。
前面介绍的管道是无名管道,它只能用于具有亲缘关系的进程之间,这就大大地限制了管道的使用。
有名管道的出现突破了这种限制,它可以使互不相关的两个进程实现彼此通信。
该管道可以通过路径名来指出,并且在文件系统中是可见的。
在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,使用非常方便。
不过值得注意的是,FIFO 是严格地遵循先进先出(first in first out)规则的,对管道的读总是从开始处返回数据,对它们的写则把数据添加到末尾,它们不支持如lseek()等文件定位操作。
有名管道比无名管道多了一个打开操作:open()。
FIFO的打开规则:
如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。
如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。
有名管道同样适用close()函数关闭该管道。
2.2.3 有名管道的读写
有名管道的读写跟无名管道一样适用read()读、write()写。
2.2.3 有名管道的应用实例
3 信号通信。