在学习信号量之前先来看几个概念:
临界资源:同一时刻只允许一个进程访问的资源。
临界区:访问临界资源的代码段。
原子操作:不可被打断的操作,没有中间状态。
进程同步:进程间协同工作。
进程异步:进程间独立运行,互不干扰,需要内核机制来通知。
在多进程编程中,会存在多个进程同时访问同一个临界资源的情况。比如变量i的初始值为0,进程A和进程B同时执行i++。在执行i++之前,两个进程获得的i值都是0,再执行i++之后,i的值变为1,i++只被执行了一次。而我们想要的结果是进程A和进程B依次执行i++。这就需要信号量来维护进程间的同步。
信号量是一个特殊的变量。它是一个计数器,大于0时记录当前临界资源的数量,小于等于0时记录等待资源的进程的数量。当信号量的值大于0时,进程总是可以获取到资源并使用;小于等于0时,表示没有临界资源可用,进程必须阻塞等待其他进程释放资源。
操作方式:对信号量进行操作使用p、v操作。
p操作:获取资源,信号量的值减1。当信号量的值小于等于0时,此操作会阻塞。
v操作:释放资源,信号量的值加1。此操作一般情况下不会阻塞。
p、v操作都是原子操作,i++不是原子操作。一条单独指令的执行才是原子操作。
p操作的初始值为多少就代表几个资源可以被访问。
p、v操作力度要小,尽可能只对需要访问的临界资源执行,提高效率。
API接口:
#include <sys/sem.h>
int semget((key_t)key,int nsems,int flag);//创建或获取信号量的内核对象
int semctl(int semid,int semnum,int cmd,/*union semun arg*/);//设置信号量属性
int semop(int semid,struct sembuf buff[],int size);//完成对信号量的p操作或v操作
struct sembuf
{
short sem_num;//信号量编号
short sem_op;//信号量在一次操作中需要改变的数值,p操作为-1,v操作为+1
short sem_flg;//通常设置为SEM_UNDO
};
操作系统对进程间通讯用的信号量在内核中都是以信号量集管理的,即通过semget()函数获取到的是信号量集的标识符。其他函数操作时,必须指明操作的是哪个信号量集中的那个信号量,类似于数组的下标。一个进程创建的信号量,其他进程也可访问。
命令:
ipcs -s 查看信号量
ipcrm -s semid 删除信号量