Linux下进程间通信概述
Linux下的进程通信基本上是从UNIX平台上的进程通信继承来的。而对UNIX发展做出最大贡献的俩大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程的通信方面的侧重点有所不同。前者是对UNIX早期的进程间通信手段进行了系统的改进和扩充,形成了“System V IPC”,其通信主要局限在单个计算机内;后者跳出了该限制,形成了基于套接字(Socket)的进程间通信机制。而Linux则把两者的优势都继承下来。
LINUX 进程间通信方式:
传统: /* 通用级 */
有名管道:mkfifo write read close无名管道:pipe write read close信号: signal kill raise pause alarm
POSIX: /* 新的、轻量级 */
信号量: man sem_overview
有名:sem_open sem_close sem_unlink无名:sem_init sem_destroysem_post sem_wait sem_getvalue
共享内存: man shm_overview
shm_open shm_unlink mmap munmap
消息队列: man mq_overview
mq_open mq_close mq_unlink mq_send mq_receive
system V:
/* 古老的、重量级 */
信号量集:
semget semctl semop
共享内存:
shmget shmctl shmat shmop
消息队列:
msgget msgop msgctl msgsnd msgrcv
BSD:
Socket通信
连接:
SERVER: socket bind listen accept recv sendCLIENT: socket bind connect send recv
无连接:
SERVER: socket bind recvfrom sendtoCLIENT: socket bind sendto recvfrom
在Linux中使用的进程间通信(IPC)方式
- 管道(Pipe)及有名管道(命名管道)(Named Pipe):管道可以用于具有亲缘关系的进程进行通信。有名管道除了具有管道的所有功能外,还允许无情缘关系的进程间通信。
- 信号(Signal):信号实在软件层次上的对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理机收到一个中断请求效果上可以说是一样的。信号也是唯一一种进程间异步通信的方式,双方在通信前不用先做好准备。
- 消息队列(Message Queue):消息队列是消息的链接表,包括POSIX消息队列和System V消息队列,它克服了前两种消息量有限的缺点,并按照权限进行操作消息队列。
- 共享内存(Shared Memory):共享内存是最高效的进程间通信方式。它使得多个进程可以使用同一块内存空间。但这种通信方式需要依靠某种同步机制,如互斥锁、信号量来保证安全性。
- 信号量(Semaphore):主要用于进程(线程)间的同步和互斥通信。
- 套接字(Socket):这是一种应用范围更广的进程间通信方式。它不仅可以在本地进程间通信,还可以用于不同主机内进程通信。
管道通信
无名管道
有名管道(命名管道)它只能用于有亲缘关系(父子,兄弟等)的进程间的通信。它是一个半双工的通信模式,具有固定的读端和写端。管道可以看成特殊的文件,对它的操作可以使用read和write,它不属于任何文件系统,存在于内存中。
它可以实现不相关的两个进程间彼此通信。命令管道FIFO严格按照先进先出的规则。不支持lseek操作。命名管道在文件系统中是可见的,使用mkfifo可以创建该类型文件
常用函数
pipe,read,write,mkfifo
注意事项
int pipe(int fd[2]),下标为0的为读端,下标为1的为写端
只有在读端存在的时候写入才有意义,否则,向管道写入数据的进程将收到SIGPIPE信号,进程被杀死。
信号
信号是在软件层次上对中断机制的一种模拟,信号是异步的,一个进程不必通过任何操作来等待信号到来,即进程处于未执行的状态,内核会保存其信号,待进程执行时在传给它。最常用的发送信号的系统函数有kill(),raise(),alarm(),setitimer()和sigqueue()等。
进程可以通过三种方式响应一个信号
忽略信号
忽略信号即对信号不做任何处理,其中 SIGKILL和SIGSTOP不能被忽略 。
执行默认操作
Linux对每种信号都有默认的处理,使用该方式进行处理信号。
捕捉信号
使用signal()函数注册信号处理函数,在信号来时执行预定的处理函数。
常用函数
发送信号的函数:kill(),raise()
捕捉信号的函数:alarm(),pause()
注册信号处理的函数:signal(),sigaction()
消息队列
消息队列就是一些消息的列表,用户可以在消息队列中添加小写和读取消息等。从这点上看,消息队列具有FIFO的特性,但是它可实现消息的随机查询,比FIFO具有更大的优势,同时这些消息存在于内核中,有队列ID来标识。使用ipcs -q查看当前系统的消息队列状态。
常用函数
ftok() 获取key
msgget() 获取消息队列id
msgsnd() 向消息队列发送消息
msgrcv() 从消息队列接收消息
msgctl() msg通用控制函数
共享内存
为了在多个进程间交换信息,内核专门流出了一块内存区,这块内存可以由需要访问的进程映射到自己的私有地址空间,从而进程可以直接读写这一段内存,不需要复制数据。因此共享内存是最为高效的进程间通信方式。使用ipcs -m查看当前系统中共享内存使用状态。
常用函数
ftok() 获得key
shmget() 获得共享内存id
shmat() 映射共享内存
shmdt() 取消映射共享内存
shmctl() 共享内存控制函数
信号量
背景:
在多任务的操作系统下,进程间可能存在一定的制约关系。例如间接制约和直接制约。
互斥关系
间接制约指进程间相互争夺共享资源的关系,例如进程争夺CPU时间片、I/O设备。我们把进程间争夺共享资源的关系称为互斥关系。
同步关系
直接制约指进程间相互合作的关系,即需要按条件有固定顺序的访问某资源。例如读者与写者问题,进程A的输出结果,进程B需要用到,所以进程B必须先等进程A完成。我们把进程间有固定顺序的操作某些资源的合作关系称为同步关系。
对于同步与互斥的关系我们可以理解为:
同步关系包含互斥关系,互斥关系是一种特殊的同步关系。
同步与互斥的根本原因在于资源不足,共享资源。这些共享的资源被称为临界资源,这些操作临界资源的代码称为临界区。
信号量:
信号量是用来解决进程间同步与互斥问题的一种进程间通信机制,包括一个称为信号量的变量和在该信号量下等待的资源的进程等待队列,以及信号量进行的两个原子操作(PV操作),其中信号量对应某一种资源,取一个非负整数。信号量的值指当前可用该资源的数量,若为0表示该资源当前没有可用资源。
多个信号量又被称为信号灯集。
PV原子操作的具体定义如下
P操作
如果有可用资源(信号量值>0),则占用一个资源(给当前信号量值减一,进入临界区代码);如果当前没有可用资源(信号量值=0),则进程被阻塞直到系统将资源分配给该进程(进入等待队列,直到资源轮到该进程使用)。
V操作
如果在该信号的等待队列中有进程在等待资源,则唤醒一个阻塞进程;如果没有进程等待该资源,则释放一个资源(给当前信号量值加一)。
常用函数
ftok() 获取key
semget() 获取信号灯集ID
semop() 对信号灯集操作
semctl() 信号灯集的控制函数
sem_wait() 对信号量进行P操作
sem_post() 对信号量进行V操作
使用ipcs -s查看当前系统信号量使用情况。