进程间通信(IPC):
进程间通信的方式有很多,这里主要讲到进程间通信的六种方式,分别为:管道、FIFO、消息队列、共享内存、信号、信号量。
一、管道
管道的特点:
- 是一种半双工的通信方式;
- 只能在具有亲缘关系的进程间使用.进程的亲缘关系一般指的是父子关系;
- 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
二、消息队列
消息队列,是消息的链接表,存放在内核之中。一个消息队列由一个标识符(即队列ID)来标识。
用户进程可以向消息队列添加消息,也可以向消息队列读取消息。
消息队列的特点:
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级;
- 消息队列是独立于发送和接收进程的,进程终止时,消息队列及其内容并不会被删除;
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
三、共享内存
共享内存,指两个或多个进程共享一个给定的存储区。
ipcs -m 查看系统下已有的共享内存;ipcrm -m shmid可以用来删除共享内存。
共享内存的特点:
- 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
- 因为多个进程可以同时操作,所以需要进行同步。
- 信号量 + 共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
四、信号
对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。
信号的相关概述:
1、信号的名字和编号:
每个信号都有一个名字和编号,这些名字都以“SIG”开头。我们可以通过kill -l来查看信号的名字以及序号。
不存在0信号,kill对于0信号有特殊的应用。
2、信号的处理:
信号的处理有三种方法,分别是:忽略、捕捉和默认动作。
- 忽略信号,大多数信号可以使用这个方式来处理,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP);
- 捕捉信号,需要告诉内核,用户希望如何处理某一种信号,说白了就是写一个信号处理函数,然后将这个函数告诉内核。当该信号产生时,由内核来调用用户自定义的函数,以此来实现某种信号的处理。
- 系统默认动作,对于每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。具体的信号默认动作可以使用man 7 signal来查看系统的具体定义。
注意:信号发送字符串,只有在父子进程或者是共享内存下才可发送。
五、信号量
信号量与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
信号量的特点:
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
- 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
- 支持信号量组
六、Socket
前面提到的管道、消息队列、共享内存、信号量和信号都是在同一台主机上进行进程间通信,那要想跨网络与不同主机上的进程之间通信,就需要Socket通信了。
实际上,Socket通信不仅可以跨网络与不同主机的进程间通信,还可以在同主机上进程间通信。
七、进程间通信方式总结:
- 管道:速度慢,容量有限,只有父子进程能通讯;
- 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题;
- 共享内存:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题;
- 信号:有入门版和高级版两种,区别在于入门版注重动作,高级版可以传递消息。只有在父子进程或者是共享内存中,才可以发送字符串消息;
- 信号量:不能传递复杂消息,只能用来同步。用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
- Socket通信不仅可以跨网络与不同主机的进程间通信,还可以在同主机上进程间通信。