由于Android系统是基于Linux系统的,所以有必要简单的介绍下Linux的跨进程通信,对大家后续了解Android的跨进程通信是有帮助的,本篇的主要内容如下:
4.7、套接字(Socket) 4.8、Linux的几种跨进程通信的方式的比较的旋转知识
说到Linux操作系统,不的不说下Unix系统
Unix因为其安全可靠,高效强大的特点在服务器领域得到了广发的应用。直到GNU/Linux流行开始前,Unix也是科学计算、大型机、超级电脑等所用操作系统的主流。
汤普逊和里奇最早是在贝尔实验室开发Unix,此后10年,Unix在学术机构和大型企业中得到了广泛的应用,当Unix拥有者AT&T公司以低廉甚至免费的许可将Unix源码授权给学术机构做研究或教学之用,许多机构在此源码的基础加以扩展和优化,形成了所谓的"Unix变种",这种变种反过来也促进了Unix的发展,其中最著名的变种就是BSD产品。BSD在后续的发展中也衍生出了3个主要分支:FresBSD、OpenBSD和NetBSD。
Unix的诞生和Multics(Multiplexed Information android Computing System) 是有一定渊源的。Multics是由麻省理工学院,AT&T贝尔实验室和通用电气合作进行的操作系统项目。由于Multics的失败,AT&T撤出了Multics投入的资源,其中一位开发者——肯.汤普逊则继续开发软件,并最终编写一个太空旅行游戏,但是他发现游戏速度很慢,并且成本高,在丹尼斯.里奇的帮助下,汤普逊用PDP-7的汇编语言重写了这个游戏,并让其再DEC PDP-7上运行起来。这次经历加上Multics项目的经验,汤普逊和里奇领导一组开发者开发了一个新的多任务操作系统,这个系统包括命令解释器和一些实用程序。1970年那部PDP-7却只能支持两个用户,所以他们就开玩笑的称他们的系统其实是"UNiplexed Information and Computing System",缩写为"UNICS",于是这个项目被称为UnICS(Uniplexed Information and Computing System)。后来,大家取其谐音这个名字就改为UNIX
UNIX is not jus an operating system , but a way of life. (UNIX 不仅仅是一个操作系统,更是一种生活方式。) 经过几十年的发展,UNIX在技术上日臻成熟的过程中,他独特的设计哲学和美学也深深地吸引了一大批技术人员,他们在维护、开发、使用UNIX的同时,UNIX也影响了他们的思考方式和看待世界的角度。这些人自然而然地形成了一个社团
GNU又称革奴计划,是由查理德.斯托曼在1983年9月27日发起的,它的目标是创建一套完全自由的操作系统。 《GNU宣言》是解释为什么发起该计划的文章,其中一个重要理由就是要"重现当年软件界合作互助的团结精神"。为了保证GNU软件可以自由地"使用、复制、修改和发布",所有GNU软件都有一份在禁止其他人添加任何限制的情况下授权所所有权利了给任何人的协议条款,GNU通用的公共许可证(GNU General Public License GPL)。即"反版权"(或者称copyleft)概念。
GNU是“GNU is Not Unix”的缩写。斯托曼宣布GNU应当发音为Guh-NOO以避免与new这个单词混淆(注:Gnu在英文中原意为非洲牛羚,发音与new相同)。UNIX是一种广泛使用的商业操作系统的名称。由于GNU将要实现UNIX系统的接口标准,因此GNU计划可以分别开发不同的操作系统部件。GNU计划采用了部分当时已经可自由使用的软件。不过GNU计划也开发了大批其他的自由软件。
1991年,在赫尔辛基,Linus Toral开始写了一个项目,目的是用来访问大学里面的大型Unix服务器的虚拟终端。他专门写了一个用于他当时正在用的硬件的,与系统操作系统无关的程序,开发是在Minix,用的编译器是GCC来完成的,这个项目后面逐渐转变为Linux内核。
Linus Torvalds本要把他的发时叫做Freax——“fread”,“free”和“x”(暗指Unix)的合成词。在开发系统的前半年里,他把文件以文件名“Freax”存储。Torvalds考虑过Linux这个名字,但是因为觉得它过于自我本位而放弃了使用它。为便于开发,后面,他把那些文件上传到了赫尔辛基工业大学(HUT)的FTP服务器。Torvalds在HUT负责管理那个服务器的同事,觉得“Freax”这个名字不是很好,就在不咨询Torvalds的情况下,把项目的名字改成了“Linux”。但是之后,Torvalds也同意“Linux”这个名字了:“经过多次讨论,他承认Linux这个名字更好。在0.01版本Linux的源代码的makefile里仍然使用‘Freax’这个名字,在之后‘Linux’这个名字才被使用。所以,Linux这个名字并不是预先想好的,只是它被广泛接受了而已”。
开源码发展实验室(Open Source Development Lab)创立于2000年。它是一个独立的非营利性组织。它的目标是优化Linux以应用于数据中心和运营商的领域。它是Linus Torvalds和Andrew Morton工作的赞助来源。2006年年中,Morton去了Google(Google也是使用Linux内核的);Torvalds全职为OSDL开发Linux内核。非商业性运营机制的资金主要来源于Red Hat,Novell,三菱,英特尔, IBM ,戴尔和惠普等几家大公司。
2007年1月22日,OSDL和自由标准组织合并为Linux基金会,把它们的工作焦点集中在改进GNU/Linux以与Windows竞争。
下面是一幅Linux kernel map:
Linux kernel map.png
这是makeLinux网站提供的一幅非常经典的Linux内核图,涵盖了内核最为核心的方法,Linux除了驱动开发外,还有很多通用子系统,比如CPU,memory,file system等核心模块,即便不做底层驱动开发,掌握这些模块对于加深理解整个系统运转还是很有帮助的。
目录 | 解释 | 部分子子目录 |
---|---|---|
kernel | 内核管理相关,进程调度等 | sched/fork等 |
fs | 文件子系统 | ext4/f2fs/fuse/debugfs/proc等 |
mm | 内存子系统 | |
drivers | 设备驱动 | staging/cpufreq/gpu 等 |
arch | 所有CPU系统结构相关的代码 | arm/x86等 |
include | 头文件 | linux/uapi/asm_generic等 |
lib | 标准通用的C库 | |
ipc | 进程通信相关 | |
init | 初始化过程(非系统引导) | |
block | 块设备驱动程序 | |
crypto | 加密、解密、校验算法 | |
Documentation | 说明文档 |
这部分是临时加进来的,是在后面的Binder驱动里面会用到,原来是打算加到"Android跨进程通信IPC之1——Linux基础"里面,不过由于简书的篇幅限制,我加到这里来了。
由于需要限制不同的程序之间的访问能力,防止他们获取别的程序的内存数据,或者获取外围设备的数据,并发送网络,CPU划分出两个权限等级 ----用户态 和 内核态。
所有用户程序都是运行在用户态的,但是有时候程序确实需要做一些内核态的事情,例如从硬盘读取数据,或者从键盘获取输入等。而唯一可以这这些事情的就是 操作系统 ,所以这时候 程序 就需要先向 操作系统 请求,以 程序 的名字来执行这些操作。这时候就需要一个这样的机制:用户态 切换到 内核态,但是不能控制内核态中执行的执行这种机制叫做** 系统调用 **,在CPU中的实现称之为 "陷阱指令(Trap Instruction)"
红黑树是60年代中期计算机科学界寻找一种算法复杂度稳定,容易实现的数据存储算法的产物。红黑树是常用的一种数据结构,它使得对数据的索引,插入和删除操作都能保持在O(lgn)的时间复杂度。在优先级队列、字典等实用领域都有广泛地应用,更是70年代提出的关系数据库模型——B树的鼻祖。当然,相比于一般的数据结构,红黑树实现的难度有所增加。** 关键是后面要讲解的Binder驱动里面用到了红黑树 **
在具体实现红黑树之前, 必须弄清楚它的基本含义。红黑树本质上是一颗二叉搜索树,它满足二叉搜索树的基本性质——即树中的任何节点的值大于它的左子节点,且小于它的右子节点。
二叉搜索树.png
按照二叉搜索树组织数据,使得对元素的查找非常快捷。比如上图的中的二叉搜索树,如果查询值为48的节点,只需要遍历4个节点即可完成。理论上,一颗平衡的二叉树搜索树的任意节点平均查找效率为树的高度h,即O(lgn)。但是如果二叉搜索树的失去平衡(元素在一侧),搜索效率就退化为O(n),因此二叉搜索树的平衡是搜索效率的关键所在。为了维护树的平衡性,数据结构内出现了各种各样的树,比如AVL树通过维持任何节点的左右子树的高度差 不大于1保持树的平衡,而红黑树使用颜色维持树的平衡,使二叉搜索树的左右子树的高度差 保持在固定的范围。相比于其他二叉搜索树,红黑树对二叉搜索树的平衡性维持着自身的优势
红黑树,顾名思义,红黑树的节点是有颜色概念的,即非红即黑,通过颜色的语速,红黑树为支持着二叉搜索树的平衡性。一个红黑树必须有下面5个特征
如下图
红黑树图1.png
这些特征强制约束了红黑树的关键性质:从跟到叶子的最长可能路径不多于最短可能路径的两倍长(特征4保证了路径最长的情况为1红1黑,最短的情况为全黑,再结合特征5,可以推导出)。结果是这个树大致上是平衡的。因为比如插入、删除和查找操作中,操作某个值的最坏情况的时间都要求与树的高度成比例,这个高度上的理论上限允许红黑树在最坏的情况都是高效的,而不同于普通的(二叉搜索树)
如下图
红河书节点.png
为了方便红黑树关键算法的实现,还定义了一些简单的操作(都是内联函数)。
//红黑树节点
template<class T>
class rb_tree_node
{
typedef rb_tree_node_color node_color;
typedef rb_tree_node<T> node_type;
public:
node_color color;//颜色
node_type*parent;//父节点
node_type*left;//左子节点
node_type*right;//右子节点
T value;//值
rb_tree_node(T&v);//构造函数
inline node_type*brother();//获取兄弟节点
inline bool on_left();//自身是左子节点
inline bool on_right();//自身是右子节点
inline void set_left(node_type*node);//设置左子节点
inline void set_right(node_type*node);//设置左子节点
};
为了表示红黑树节点的颜色,我们定义了一个简单的枚举类型。
//红黑树节点颜色
enum rb_tree_node_color
{
red=false,
black=true
};
有了节点,剩下的就是实现红黑树的构造、插入、搜索、删除等关键算法了。
//红黑树
template<class T>
class rb_tree
{
public:
typedef rb_tree_node<T> node_type;
rb_tree();
~rb_tree();
void clear();
void insert(T v);//添加节点
bool insert_unique(T v);//添加唯一节点
node_type* find(T v);//查询节点
bool remove(T v);//删除节点
inline node_type* maximum();//最大值
inline node_type* minimum();//最小值
inline node_type* next(node_type*node);//下一个节点
inline node_type* prev(node_type*node);//上一个节点
void print();//输出
int height();//高度
unsigned count();//节点数
bool validate();//验证
unsigned get_rotate_times();//获取旋转次数
private:
node_type*root;//树根
unsigned rotate_times;//旋转的次数
unsigned node_count;//节点数
void __clear(node_type*sub_root);//清除函数
void __insert(node_type*&sub_root,node_type*parent,node_type*node);//内部节点插入函数
node_type* __find(node_type*sub_root,T v);//查询
inline node_type* __maximum(node_type*sub_root);//最大值
inline node_type* __minimum(node_type*sub_root);//最小值
void __rebalance(node_type*node);//新插入节点调整平衡
void __fix(node_type*node,node_type*parent,bool direct);//删除节点调整平衡
void __rotate(node_type*node);//自动判断类型旋转
void __rotate_left(node_type*node);//左旋转
void __rotate_right(node_type*node);//右旋转
void __print(node_type*sub_root);//输出
int __height(node_type*&sub_root);//高度
bool __validate(node_type*&sub_root,int& count);//验证红黑树的合法性
};
在红黑树类中,定义了** 树根(root) ** 和** 节点数 (count) ,其中还记录了红黑树插入、删除时执行的旋转次数 rotate_times。其中核心操作有 插入操作(insert) , 搜索操作 (find) , ** 删除操作(remove) , 递减操作(prev) ** ——寻找比当前节点较小的节点, 递增操作(next) ** ——寻找比当前节点比较大的节点, ** 最大值(maximum) ** 和 ** 最小值(minimum) ** 。** 其中验证操作(__ validate) ** 通过递归操作红黑树,验证红黑树的基本颜色约束,用于操纵红黑树验证红黑树是否保持平衡。
当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质。所以为了继续保持红黑树的性质,我们可以通过对节点进行重新着色,以及对树进行相关的旋转操作,即修改树中某些节点的颜色及指针结构,来达到对红黑树进行插入和删除结点等操作后,继续保持它的性质或平衡。
树的旋转,分为左旋和右旋,借助下图来做介绍:
左旋.png
如上图所示:
右旋.png
对于树的旋转,能保持不变的只有原树和搜索性质,而原树的红黑性质则不能保持,在红黑树的数据插入和删除后可利用旋转和颜色重涂来恢复树的红黑性质。
这样大家对红黑树就有了初步了解。这里就不详细介绍了,如果大家有兴趣,可以自行去了解。
跨进程通信(IPC)的目的主要如下:
** Linux **下的跨进程通信手段基本上是从Unix平台上的进程通信手段继承而来。而对Unix发展做出大量贡献的量大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校伯克利软件发布中心)在进程间通信方面的侧重点有所不同。前者对Unix早期的进程间通信手段进行了系统的改进和扩充,形成了"system V IPC",通信进程局限在单个计算机内;而后者则跳过了这个限制,形成了基于套接字(socket)的进程间通信机制。 ** Linux **则把两者继承了下来。
所以可以把Linux中的进程间通信大体分为4类
这里说下PSOIX:
由于Unix版本的多样性,电子电器工程协会(IEEE) 开发了一个独立的Unix标准,这个心的ANSI Unix标准被称为计算机环境的可移植性操作系统。现有的大部分Unix和流行版本都是遵循POSIX标准的,而Linux从一开始就是遵循POSIX标准。
在Linux下进程通信有以下七种:
那我们就来详细的了解下相关的内容
匿名管道(pipe)是Linux支持的最初Unix IPC形式之一,具有以下特点:
匿名管道是右内核管理的一个缓冲区,相当于我们放入内存的中一个纸条。匿名管道的一端连接一个进程的输出。这个进程会向管道中放入信息。匿名管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓存区不需要很大,它被设计成为唤醒的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程就会等待,直到另一端的进程取出信息。两个进程都终结的时候,管道也会自动消失。如下图
image.png
从原理上,匿名管道利用fork机制建立,从而让两个进程可以连接到同一个PIPE上。最开始的时候,上面的两个箭头都连接到同一个进程Process 1上(连接在Process 1上的两个箭头)。当fork复制进程的时候,会将这两个连接也复制到新的进程(Process 2)。随后,每个进程关闭在自己不需要的一个连接(两个黑色的箭头被关闭;Process 1关闭从PIPE来的输入连接,Process 2关闭输出到PIPE的连接),这样,剩下的红色连接就构成了上图的PIPE。 示例图如下:
image.png
在Linux中,匿名管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构,和VFS的索引节点inode。通过将两个file结构指向同一个临时的VFS节点,而这个VFS索引节点又指向了一个物理页面而实现的。如下图
管道实现细节.png
匿名管道的实现的源代码在fs/pipe.c中,在pipe.c中有很多函数,其中有两个函数比较重要,即匿名管道pipe_read()读函数和匿名管道写函数pipe_write()。匿名管道写函数通过将字节复制到VFS索引节点指向物理内存而写入数据,而匿名管道读函数则通过复制物理内存而读出数据。当然,内核必须利用一定的 同步机制 对管道的访问,为此内核使用了 锁 、等待队列、和 信号。
当写入进程向匿名管道中写入时,它利用标准的库函数write(),系统根据库函数传递的文件描述符,可找到该文件的file结构。file结构中制定了用来进行写操作的函数(即写入函数)地址,于是,内核调用该函数完成写操作。写入函数在向内存中写入数据之前,必须首先检查VFS索引节点中的信息,同时满足如下条件时,才能进行实际的内存复制工作:
如果同时满足上述条件,写入函数首先会锁定内存,然后从写进程的地址空间中复制数据到内存。否则,写进程就休眠在VFS索引节点的等待队列中,接下来,内核将调用调度程序,而调度程序会选择其他进程运行。写进程实际处于可中断的等待状态,当内存中有足够的空间可以容纳写入数据,或内存被解锁时,读取进程会唤醒写入进程,这时,写入进程将接受到信号。当数据写入内存之后,内存被解锁,而所有休眠在索引节点的读取进程会被唤醒。
匿名管道的读取过程和写入过程类型。但是,进程可以在没有数据或者内存被锁定时立即返回错误信息,而不是阻塞该进程,这一来于文件或管道的打开模式。反之,进程可以休眠在索引节点的等待队列中等待写入进程写入数据。当所有的进程完成了管道操作之后,管道的索引节点被丢弃,而共享数据页被释放。
PS:有些同学可能不清楚VFS,我这里就简单介绍下;
VFS(virtual File System/虚拟文件系统):是Linux文件系统对外的接口。任何要使用文件系统的程序都必须经由这层接口来使用它。它是采用标准的Unix系统调用读写位于不同物理介质上的不同文件系统。VFS是一个可以让open()、read()、write()等系统调用不用关系底层的存储介质和文件系统类型就可以工作的粘合层。在Linux中,VFS采用的是面向对象的编程方法。
在上面,我们介绍了匿名管道(pipe),我们知道了如何匿名管道在进程之间传递数据,同时也是看到了这个方式的一个缺陷,就是这些就进程都是由一个共同的祖先进程启动,这给我们在不相关的进程之间交换数据带来了不方便。这里我将会介绍另一种通信方式——命名管道,来解决不相关进程之间的问题。
大家想一下,只使用一个FIFO文件,如果有多个进程同时向同一个FIFO文件写数据,而只有一个读FIFO进程在同一个FIFO文件读取数据时,会发生怎么样的情况呢,会发生数据块的相互交错是很正常的?而且个人认为多个不同进程向一个FIFO读取进程发送数据是很正常的情况。
为了解决这个问题,就是让写操作原子化,怎么才能使写操作原子化呢?答案其实很简单:系统规定,在一个以O_WRONLY(即阻塞方式)打开的FIFO中,如果写入的数据长度小于等于PIPE_BUF,那么或者写入全部字节,或者一个字节都不写入。如果所有的写请求都是发往一个阻塞的FIFO的,并且每个写请求的数据长度小于等于PIPE_BUF字节,系统就可以确保数据绝不会交错在一起。
信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;Linux除了支持Unix早期信号语义函数sigal外,还支持语义服务Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,有能够统一对外接口,用sigaction函数重新实现了signal函数)
如下图:
信号种类.png
每种信号类型都有对应的信号处理程序(也叫信号的操作),就好像每个中断都有一个中断服务例程一样。大多数信号的默认操作是结束接受信号的进程;然而一个进程通常可以请求系统采取某些代替的操作,各种代替操作是:
但是,信号和中断有所不同。中断的响应和处理都发生在内核空间,而信号的响应发生在内核空间,信号处理程序的执行却发生在用户空间。 那么什么时候检测和响应信号?通常发生在两种情况下:
信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。
信号事件的发生有两个来源:硬件来源(比如我们按下键盘或者其他硬件故障);润健来源,最常用发送信号的系统函数是kill,raise,alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。
内核给一个进程发送中断信号,是在进程所在的进程表项的信号域设置对应于该信号的位。这里要补充的是,如果信号发送给一个正在睡眠的进程,那么要看该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒正在睡眠的进程;否则仅设置进程表中信号域相应的位,而不是唤醒进程。这一点比较重要,因为进程检查是否收到信号的时机是:一个进程在即将从内核态返回到用户态时;或者,在一个进程进入或离开一个适当的低调度优先级睡眠状态时。
内核处理一个进程吸收的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。
内核处理一个进程收到的软中断信号是在该进程的上下文中。因此,进程必须处于运行状态。如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。而且执行用户定义的函数的方法很巧妙,内核是在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数出,从函数返回再弹出栈顶时,才返回原先进入内核的地方,接着原来的地方继续运行。这样做的原因是用户定义的处理函数不能切不允许在内核态下执行。
信号的生命周期.png
信号量又称为信号灯,它用来协调不用进程间的数据对象,而最主要的应用是共享内存方式的进程间通信。本质上,信号量时一个计数器,他用来记录某个资源(如共享内存)的存取状况。信号量的使用,主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。所有的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
由于信号量只能进行两种操作即"等待"和"发送",即P(sv)和V(sv),他们的行为是这样:
举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,他将得到信号量,并可以进如临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会挂起以等待第一个进程离开临界区并执行(sv)释放信号。
Linux提供两种信号量
POSIX辛信号量又分为有名信号量和无名信号量
POSIX信号量和SYSTEM V信号量的比较
消息队列也成为报文队列,消息队列是随内核持续的,只有在内核重其或者显示删除一个消息队列时,该消息队列才会真正删除系统中记录消息队列的数据结构体 struct ipc_ids_msg_ids位于内核中,系统中所有消息队列都可以在结构msg_ids中找到访问入口。
共享内存是进程间通信中最简单的方式之一。
共享内存是系统处于多个进程之间通讯的考虑,而预留的一块内存区。共享内存允许两个或更多的进程访问同一块内存,就如同malloc()函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其他进程都会觉察到这个更改。
为了简化共享数据的完整性和避免同时存取数据,内核提供了一种专门存取共享内存资源的机制。这称为互斥体或者Mutex对象。
例如,在数据被写入前不允许进程从共享内存中读取信息、不允许两个进程同时向一个共享内存地址写入数据等。
当一个基础想和两一个进程通信的时候,它将按以下顺序运行:
当一个进程从这个区域读取数据的时候,它将重复同样的步骤,只是将第二步变成读取。
要使用一块共享内存
在/proc/sys/kernel/目录下,记录着共享内存的一些限制,如一个共享内存区的最大字节数shmmax,系统范围内最大的共享内存区标志符数shmmni等。
共享内存的实现分为两个步骤:
套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。 更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是右Unix系统的BSD分之开发出来的,但是现在一般可以移植其他类Unix系统上。比如Linux和System V的变种都支持套接字。
类型 | 无连接 | 可靠 | 流控制 | 优先级 |
---|---|---|---|---|
匿名PIPE | N | Y | Y | N |
命名PIPE(FIFO) | N | Y | Y | N |
信号量 | N | Y | Y | Y |
消息队列 | N | Y | Y | Y |
共享内存 | N | Y | Y | Y |
UNIX流SOCKET | N | Y | Y | N |
UNIX数据包SOCKET | Y | Y | N | N |
PS:无连接是指无需调用某种行动是OPEN,就有发送消息的能力流控制,如果系统资源短缺或者不能接受更多的消息,则发送进程能进进行流量控制。
补充一点: 共享内存块提供了在任意数量的进程之间进行高效双向通信的机制。每个使用者都可以读取写入数据,但是所有程序之间必须达成并遵守一定的协议,以防止诸如在读取信息之前覆写内存空间等竞争状态的出现。不行的是,Linux无法严格保证提供对共享内存块的独占访问,同时,多个使用共享内存块的进程之间必须协调使用同一个键值。