Inter-Process Communication
定义
首先什么是进程间通信,有些人会认为是进程间传递数据,其实并非仅仅如此,communication翻译成通信可能误导了这些人,communication是进程间交流,不仅包括传递数据,还包括一些执行先后顺序的沟通。
《现代操作系统》中说,进程间通信包括三个问题。第一个问题是一个进程如何把信息传递给另一个进程。第二个问题是,确保两个或更多的进程在关键活动中不会出现交叉,例如飞机订票系统中的两个进程胃不痛的客户视图争夺飞机上的最后一个座位。第三个问题与正确的顺序有关(如果顺序是关联的话)比如进程A产生数据而进程B打印数据,那么B在打印之前必须等待,知道A产生了一些数据。
而且实际上,以上三个问题对于线程间通信同样适用。而且事实上线程间通信和进程间通信并没有什么不同,除了线程间交换数据的时候因为有进程共享内存的存在更方便一些。并不像很多八股文说的似乎进程间通信和线程间通信完全是两个话题,而八股文中一般进程间通信的方法更侧重第一个问题,线程间通讯更侧重第二三个问题,我觉得原因应该是在实际开发中,不同进程之间更多的使用场景是传递数据,而线程间通信的应用场景则更多的是进行同步,后两个问题就是同步问题。
本篇文章主要说的是数据传递的方式,所以就不说信号量了,他本来就不是传递数据的,而是用来同步的。
传递数据的方式
传递数据就是上面说的第一个问题。
单纯的八股文的贻害之一就是他似乎以一种结构化的方式宣誓着绝对正确的知识点。
八股我是会的,进程间通信:管道(匿名、命名)、共享内存、信号、信号量、套接字、消息队列
但首先,进程间通信只是一个很现实的问题而已,他没有什么标准的答案,以上只是一些常用的手段,你能想出其他的方式,只要实现了进程间传递数据,就都是IPC。比如说使用文件,多个线程读写同一个文件也能实现线程通信。其次,以上方式被并列在这里似乎做着一样的事情,但事实上信号量就不是进程间传递数据的方式,而是解决同步问题的。
1 管道
linux中 | 就是管道实现的。
匿名管道
匿名管道是一种父子或者兄弟进程之间半双工FIFO通信通道。系统调动是int pipe(int filedes[2]);
在linux的实现上复用了io子系统,也就是相当于在内存中创建了一个文件,一个进程写,一个进程读。
标准流管道
实现了全双工通信
FILE* popen(char* command ,char* type);
命名管道
可以在没有父子关系的进程间使用
int mkfifo( const char *pathname, mode_t mode );
如果想用管道实现全双工,用两个管道即可
2 共享内存
共享内存比较简单,就是在内存中开辟一块区域,让两个进程可以同时访问。
shm_open
功能:
打开或创建一个共享内存区
头文件:
#include
函数原形:
int shm_open(const char *name,int oflag,mode_t mode);
参数:
name 共享内存区的名字
cflag 标志位
mode 权限位
返回值:
成功返回0,出错返回-1
oflag参数必须含有O_RDONLY和O_RDWR标志,还可以指定如下标志:O_CREAT,O_EXCL或O_TRUNC.
mode参数指定权限位,它指定O_CREAT标志的前提下使用。
shm_open的返回值是一个整数描述字,它随后用作mmap的第五个参数。
同时mmap也可以实现共享内存
使用共享内存的时候可能会发生内存泄漏和同步问题。
同时还有内存可见性问题,这些问题其实在线程同步是一样的,线程同步解决这些问题的方式都可以用来解决这个问题。
3 信号
linux中 kill -9 pid使用的就是信号
信号其实就是为了方便进程间传递一些很短小的消息,由操作系统提供的一种高效机制。
信号是发送到进程或同一进程内的特定线程以通知其事件的异步通知。
信号起源于 1970 年代的贝尔实验室Unix,后来在POSIX标准中指定。当发送信号时,操作系统会中断目标进程的正常执行流程以传递信号。执行可以在任何非原子指令期间中断。如果进程先前已注册信号处理程序,则执行该例程。否则,将执行默认信号处理程序。
嵌入式程序可能会发现信号对进程间通信有用,因为信号以其算法效率而著称。
信号类似于中断,不同之处在于中断由处理器调解并由内核处理,而信号由内核调解(可能通过系统调用)并由进程处理。内核可以将中断作为信号传递给引起它的进程(典型的例子是SIGSEGV、SIGBUS、SIGILL和SIGFPE)。
4 套接字socket
socket就是利用网络协议进行通信的方式。具体来说就是一对(ip+端口号)
socket并不局限于大家熟知的TCPsocket,也包括UDPsocket,甚至还有直接Socket
另外,其实套接字和其他几种方式的一个显著区别就是socket可以用于不同操作系统之间通信,这里我们应该意识到IPC概念本身并未限制是在一个操作系统内的。所以其实RPC远程过程调用也是IPC的一种,这里就先不展开了。
5 消息队列
其实消息队列的逻辑有点类似于管道,就是一个生产者消费者模式的队列。但是mq是第三方解耦的,作为第三方在进程A、B之间传递消息,但是又不依赖于A,B。mq还提供了更多的功能,各种转发方式。著名的mq中间件比如RabbitMQ、Kafka。RocketMQ。