进程间通信方式:管道,共享内存,信号量,信号,消息队列。
1. 管道pipe:是一种半双工通信,且只能用于有亲缘关系(即父子关系)的进程间通信。
管道是由内核管理的一个缓冲区(buffer),一个进程从管道一端输入数据,另一个进程从管道另一端读出数据。
当管道中没有信息,从管道中读取信息(read())的进程会进入阻塞状态,知道另一端的进程放入信息。当管道满时,尝试向管道中放入信息(write())的进程也会阻塞,知道另一端的进程从管道中取出了信息。
当两个进程都结束时,管道消失。
从管道中读取数据是一次性操作,数据一旦被读出,它就从管道中被抛弃,释放空间以便写更多的数据。
2. 命名管道FIFO,半双工通信,但是允许无亲缘关系的进程间通信。
3. 共享内存:
共享内存是最为高效的进程间通信方式
进程直接读写内存,不需要任何数据的拷贝
共享内存是分配一块能被其他进程访问的内存,实现是通过将内存去映射到共享它的进程的地址空间,使这些进程间的数据传送不再涉及内核,即,进程间通信不需要通过进入内核的系统调用来实现
共享内存允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取操作读出,从而实现了进程间的通信。
采用共享内存进行通信的一个主要好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝,对于像管道和消息队里等通信方式,则需要再内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次:一次从输入文件到共享内存区,另一次从共享内存到输出文件。共享内存是分配一块能被其他进程访问的内存,实现是通过将内存去映射到共享它的进程的地址空间,使这些进程间的数据传送不再涉及内核,即,进程间通信不需要通过进入内核的系统调用来实现;
内存映射 memory map机制使进程之间通过映射同一个普通文件实现共享内存,通过mmap()系统调用实现。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调read/write等文件操作函数。
或者通过shmget开辟共享内存:
•为了在多个进程间交换信息,内核专门留出了一块内存区(或者进程在自己的内存空间开辟一块共享内存)
•由需要访问的进程将其映射到自己私有地址空间
•进程直接读写这一内存区而不需要进行数据的拷贝,提高了效率
多个进程共享一段内存,需要依靠某种同步机制,如互斥锁和信号量等
共享内存编程步骤:
1). 创建共享内存
•函数shmget()
•从内存中获得一段共享内存区域
2). 映射共享内存
•把这段创建的共享内存映射到具体的进程空间中
•函数shmat()
3). 使用这段共享内存
•可以使用不带缓冲的I/O读写命令对其进行操作
4). 撤销映射操作: 函数shmdt()
5). 删除共享内存: 函数shctl()
4. 信号
进程之间通信或操作的一种机制。信号可以在任何时候发送给某一进程,而无需知道该进程的状态。如果该进程并未处于执行状态,则该信号就由内核保存起来,直到该进程恢复执行时才传递给它。如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到该信号的阻塞被取消时才传递给进程。
信号是软件层次上对中断机制的一种模拟,是一种异步通信方式。信号可以在用户空间进程和内核之间直接交互。
1)硬件来源,例如按下了cltr+C,通常产生中断信号sigint
2)软件来源,例如使用系统调用或者命令发出信号。最常用的发送信号的系统函数是kill,raise,setitimer,sigation,sigqueue函数。软件来源还包括一些非法运算等操作。
一旦有信号产生,用户进程对信号产生的相应有三种方式:
1)执行默认操作,linux对每种信号都规定了默认操作。
2)捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数。
3)忽略信号,当不希望接收到的信号对进程的执行产生影响,而让进程继续执行时,可以忽略该信号,即不对信号进程作任何处理。
有两个信号是应用进程无法捕捉和忽略的,即SIGKILL和SEGSTOP,这是为了使系统管理员能在任何时候中断或结束某一特定的进程。
5. 消息队列
就是一个消息的链表,是一系列保存在内核中消息的列表,用户进程可以向消息队列添加消息,也可以向消息队列读取消息。
消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。
6. 信号量
分为命名和匿名信号量。命名信号量通常用于不共享内存的进程之间(内核实现);匿名信号量可以用于线程通信(存放于线程共享的内存,如全局变量),或者用于进程间通信(存放于进程共享的内存,如System V/ Posix 共享内存)。
消息队列、共享内存:与System V 类似。
互斥锁mutex + 匿名信号量:线程通信
互斥锁mutex + 条件变量condition :线程通信
PV操作由P操作原语和V操作原语组成(原语是不可中断的过程)。对信号量进行操作,具体定义如下:
P(S):
①将信号量S的值减1,即S = S - 1;
②如果s >= 0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):
①将信号量S的值加1,即S = S + 1;
②如果S > 0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
PV操作的意义:我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信。
信 号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时, 表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。
让我们来看一个具体的实例: 假设现在的执行场景是有三个线程A,B,C进入一个信号量为1的临界资源,
1. 当线程A进入时,执行P操作, sem=0,线程A继续执行.
2. 当线程B进入时,线程A假设仍在占用临界资源, B执行P操作,sem = -1,B进入等待队列。
3. 当线程C进入时,线程A假设仍在占用临界资源, C执行P操作,sem = -2,C进入等待队列