简介

最近想梳理下进程相关的知识点,第一篇先谈下进程间通信的知识吧。

为什么要进程间通信

  • 数据传输,如A进程需要传一些参数到B进程
  • 事件通知,如A进程告知B进程,你需要停止运行
  • 进程间的分工协作,如A进程告诉B进程,你可以做xx了
  • 数据的共享,如AB进程同时对一块数据处理

进程间通信的方式

  • 管道
  • 消息队列
  • 信号
  • 锁与信号灯
  • 共享内存
  • 套接字

进程间通信的各种方法,叫法、分类可能不同,但是实际上大致也就是这几种,统称IPC技术。

管道

大致分为匿名管道(pipe)和命名管道(FIFO)

  • 匿名管道(pipe)作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。

  • 命名管道(FIFO)不同于pipe之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。

管道总体上有如下特质:
1. 其本质是一个(伪)文件(实为内核缓冲区)
2. 由两个文件描述符引用,一个表示读端,一个表示写端。
3. 规定数据从管道的写端流入管道,从读端流出

消息队列(message queues)

消息队列实际上就是一个链表,而消息就是链表中具有特定格式和优先级的记录,对消息队列有写权限的进程可以根据一定规则在消息链表中添加消息,对消息队列有读权限的进程则可以从消息队列中获得所需的信息。

在某个进程往一个消息队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。这跟命名管道是不同的,对后者来说,除非读端已经存在,否则写端的打开管道操作会一直阻塞。此外,管道和命名管道都是随进程持续的,而消息队列还有后面的信号量、共享内存都是随内核持续的。也就是说当一个管道或FIFO的最后一次关闭发生时,仍在该管道或FIFO上的数据将被丢弃。而对于消息队列来说,除非内核自举或显式删除,否则其一直存在。

消息队列、共享内存、信号量是一套比较标准的规范,可以通过ipcs、ipcrm等系列命令进行管理

信号

软中断信号(signal,又简称为信号)是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

进程之间可以互相通过系统调用kill发送软中断信号,内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

信号的3种处理方式:
- 默认处理 SIG_DFL
- 忽略 SIG_IGN
- 用户自定义 自定义函数

详细使用说明可以看kill -l 看所有支持的信号

信号量

信号量又叫信号灯,也有人把它叫做信号集,遵循《UNIX环境高级编程》的叫法,仍称其为信号量。它的英文是semaphores,本意是“旗语”“信号”的意思。Linux环境下主要实现的信号量有两种。根据标准的不同,它们跟共享内存类似,一套XSI的信号量,一套POSIX的信号量。

信号量是用来协调进程对共享资源的访问的。程序对其访问都是原子操作,且只允许对它进行等待和发送操作,也即P(sv)和V(sv)

man semaphores 查看详细信息

共享内存

共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种最有效的方式,不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

注意共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以通常需要用其他的机制来同步对共享内存的访问,例如前面说到的信号量。

free命令,shaore量就是共享内存的值。

linux操作系统提供的相关函数都是c的,像java、php、python虽然有类似实现,但是底层都还是用c封装的,可能功能上和系统默认的差别比较大,性能可能也会大打折扣。

其他

套接字这里我们不做重点描述,其核心用途还是网络编程。此外如果深入了解IPC技术的话,POSIX和XSI标准上的差异,是需要重点了解的。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐