概述
- Linux 下的进程通信手段基本上是从
UNIX平台继承而来的。 - Linux 中进程间通信(
IPC)方式主要有以下几种:- (无名)管道(
Pipe)及有名管道(named pipe):管道(无名)可用于具有亲缘关系进程间的通信;有名管道除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。 - 信号(
Signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一样的。 - 消息队列(
Messge Queue):消息队列是消息的链接表,包括POSIX 消息队列,SystemV 消息队列。 它克服了前两种通信方式中信息量有限的缺点,具有写权限的进程可以按照一定的规则向消息队列中添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。 - 共享内存(
Shared memory):可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。 - 信号量(
Semaphore):主要作为进程之间以及同一进程的不同线程之间的同步和互斥手段。 - 套接字(
Socket):这是一种更为一般的进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。
- (无名)管道(
graph TD
K[IPC通信方式]--pipe/fifo-->A(管道)
A--pipe-->无名/匿名管道
A--fifo-->有名/命名管道
K--singal-->B(信号)
K--msg queue-->C(消息队列)
K--shm-->D(共享内存)
K--sem-->E(信号量)
K--socket-->F(套接字)
管道
无名管道
- 常说的管道一般是指
无名管道(pipe)或者亦称匿名管道,其特点有:- 其
只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间)。 - 其是一个
半双工通信模式,具有固定的读端和写端。 - 其可看成是一种特殊的文件,
范属于内核级,对于其读写可使用普通的read()和write()等函数。
- 其
- 管道读写注意:
- 只有在管道的
读端存在时,向管道写入数据才有意义。否则向管道写入数据的进程将收到内核传来的SIGPIPE信号(通常为Broken pipe错误)。 - 向管道写入数据时,Linux 将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。若读进程不读取管道缓冲区中的数据,那么写操作将会一直阻塞。
- 当一个管道共享多对文件描述符时,若将其中的一对读写文件描述符都删除,则该管道就失效。
- 只有在管道的
graph TD
A[无名管道操作]--创建管道-->B(pipe函数)-->生成fd_0和fd_1
A--读取管道数据-->B0(read函数)-->基于fd_0文件描述符
A--写入管道数据-->B1(write函数)-->基于fd_1文件描述符
A--关闭管道-->B2(close函数)-->fd_0和fd_1
有名管道
- 有名管道(
fifo)亦称命名管道,其特点如下:- 突破了无名(匿名)管道的血继界限,可以使互不相关的两个进程实现彼此通信。
- 可以通过路径名来指出,并且在文件系统中是可见的。
- 基于FIFO机制,是严格地遵循其先进先出规则的,对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾,它们
不支持如lseek()等文件定位操作。
graph TD
A[有名管道操作]--创建管道-->B(mkfifo函数)
A--打开管道-->B3(open函数)
A--读取管道数据-->B0(read函数)
A--写入管道数据-->B1(write函数)
A--关闭管道-->B2(close函数)
- 阻塞式(
BLOCK)和非阻塞式(NONE BLOCK)打开的读写:- 对于读进程
- 若该管道是阻塞打开,且当前
FIFO内没有数据,则对读进程而言将一直阻塞到有数据写入。 - 若该管道是非阻塞打开,则不论
FIFO内是否有数据,读进程都会立即执行读操作。即如果FIFO内没有数据,则读函数将立刻返回0。
- 若该管道是阻塞打开,且当前
- 对于写进程
- 若该管道是阻塞打开,则写操作将一直阻塞到数据可以被写入。
- 若该管道是非阻塞打开而不能写入全部数据,则读操作进行部分写入或者调用失败。
- 对于读进程
标准流管道
- 标准流管道是将管道一系列的操作过程合并(
封装?)到一个函数中完成操作的方式。与 Linux 的文件操作中有基于文件流的标准 I/O 操作一样,其操作也基于文件流的模式。- 其完成步骤:
- 创建一个管道。
fork()一个子进程。- 在父子进程中关闭不需要的文件描述符。
- 执行 exec 函数族调用。
- 执行函数中所指定的命令。
- 其完成步骤:
- 其特点是:极大减少了代码量,但降低了管道操作的灵活性,操作必须使用标准I/O函数操作,不能使用
read(),close(),write()一类不带缓冲的I/O函数,与之对应的则用popen()创建函数,pclose()关闭函数。
graph TD
K[标准流管道]--创建管道-->A(popen函数)
K--关闭管道-->B(pclose函数)
参考文献:《Linux嵌入式应用程序开发标准教程》