进程之间的通信方式主要分为六种:
管道、信号、消息队列、共享内存、信号量、socket。其中前五种主要用于一台主机之中的各个进程之间的通信,socket套接字通信主要用于网络之中不同主机之间的通信。
管道:
管道的本质是从内核中划出一片空间用作缓存,一个进程往这片缓存中写入数据,另一个进程读取这个缓存中的数据。管道的数据是无格式的流且大小首先
管道分为匿名管道和命名管道, 匿名管道是一种半双工通信,数据只能在管道中单向流动,如果想要进行双向通信的话,则需要建立两条管道。且一般只用于有亲缘关系的进程之间的通信,一般是父子进程;匿名管道是借助磁盘来实现的,可以用于内存中所有进程之间的通信。
缺点是:
1、是一个半双工通信。
2、一个进程写完之后,另外一个进程可以读取数据。并且如果管道内的数据如果没有被读完的话,则写进程会堵塞,直到管道内部没有数据。
管道的创建:
一个进程采用pipe( )函数,创建管道,该函数返回两个文件描述符,一个是写描述符,一个是读描述符,由于匿名管道只能用于有亲缘关系的两个进程之间的通信,所以此时通过fork()创建一个子进程,然后实现父子进程之间的通信。
而命名管道则是借助磁盘,创建一个类型为管道的设备文件,所以可以用于任意两个进程之间的通信。
信号:
信号是一种软件中断
信号的产生方式主要有两种,一种为硬件产生,即采用终端设备比如键盘来产生信号,另外一种是通过调用系统函数给一个进程发送信号。
对于信号的处理方式一般有三种:
1、不进行处理
2、根据默认的信号处理程序进行处理
3、可以改变task_struct中信号处理函数对应的指针,自定义相应的信号处理程序
进程收到信号之后并不会立刻执行,而是在CPU由内核态转变为用户态之前会检查是否有未处理的信号,如果有,再进行处理。
消息队列:
管道的发送消息的效率很低,不适合两个频繁需要交换数据的进程,为此我们引入了消息队列。
进程A给进程B发送数据时,只需要即将数据放在消息队列之中便可以立即返回而不需要阻塞。
消息队列是保存在内核中的消息链表,通信双方会约定好收发数据的格式,而不是无格式的字节流。同时消息队列也支持随机读取消息块。
缺点:
1、对所发送数据的大小有限制
2、由于消息队列是存贮于内核中的消息链表,所以收发数据时,会进行CPU变态,伴随着CPU上下文切换。
共享内存:
共享内存可以理解操作系统内核所划分出的一段物理地址空间,由于每一个进程都有着自己的虚拟地址空间,且通过页表完成地址的映射,所以我们可以通过借助页表完成进程中的一段虚拟地址空间和共享内存的映射。
从而通过虚拟地址借助MMU就能够实现对共享内存的访问。
但是多个进程又能够访问共享内存,所以我们引入了信号量。
信号量:
当使用共享内存的通信方式,如果有多个进程同时往共享内存写入数据,有可能先写的进程的内容被其他进程覆盖了。
因此需要一种保护机制,信号量本质上是一个整型的计数器,用于实现进程间的互斥和同步。
信号量代表着资源的数量,操作信号量的方式有两种:
P操作:这个操作会将信号量减一,相减后信号量如果小于0,则表示资源已经被占用了,进程需要阻塞等待;如果大于等于0,则说明还有资源可用,进程可以正常执行。
V操作:这个操作会将信号量加一,相加后信号量如果小于等于0,则表明当前有进程阻塞,于是会将该进程唤醒;如果大于0,则表示当前没有阻塞的进程。