信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量就是一个计数器。
当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,等于0,无资源可用,进程会进入睡眠状态直至资源可用。当一个进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减操作为原子操作,这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。而信号量的创建以及初始化上,不能保证操作为原子操作。
使用信号量的原因:为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,它可以通过生成并使用令牌来授权,在任意时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来协调进程对共享资源的访问的。
信号量的工作原理:
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv)
P(sv):如果sv的值大于0,就减1,如果它的值为0,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就+1
几个函数:
- 创建一个信号量集对象(得到一个信号量集标识符)
int semget(key_t key,int nsems,int semflg)
key:由ftok()函数的得到
nsems:创建信号量集中信号的个数
semflg:
IPC_CREAT:若内核中不存在键值与key相等的信号量集,则创建,否则,返回此信号量集的标识符
IPC_EXCL:单独使用无意义
IPC_CREAT | IPC_EXCL :创建一个新的信号量集并返回信号量集的标识符,否则,返回-1.
返回值:成功返回信号量集的标识符。失败返回-1.
2. 完成对信号量的P,V操作
int semop(int semid,struct sembuf* sops,unsigned nsops)
返回值:成功时,返回信号量集的标识符。否则,返回-1.
semid:信号量集标识符
nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1.
sops:指向进行操作的信号量集结构体数组的首地址
struct sembuf
{
short semnum;//信号量集合中的信号量编号,0代表第一个信号量
short val;//val>0 进行V操作信号量值+val,表示进程释放控制的资源
short flag; //设置信号量的默认操作 IPC_NOWAIT 设置信号量操作不等待
//SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进 程崩溃,则根据这个进程的UNDO记录自动恢复相应的信号量的计数
}
当操作信号量(semop)时,flg可以设置SEM_UNDO标识;SEM_UNDO用于将修改的信号量值在进程正常退出(调用exit退出或main执行完)或异常退出(如段异常、除0异常、收到KILL信号等)时归还给信号量。
如信号量初始值是20,进程以SEM_UNDO方式操作信号量减2,减5,加1;在进程未退出时,信号量变成20-2-5+1=14;在进程退出时,将修改的值归还给信号量,信号量变成14+2+5-1=20。
3.在指定的信号集或信号集内的某个信号上执行操作控制
函数原型:int semctl(int semid,int semnum,int cmd,union semun arg)
semid: 信号量集标识符
semnum:信号量集数组上的下标,表示某一个信号量
arg:
union semun {
short val; /*SETVAL用的值*/
struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构*/
unsigned short* array; /*SETALL、GETALL用的数组值*/
struct seminfo *buf; /*为控制IPC_INFO提供的缓存*/
} arg;
“comm.h”
“comm.c”
“test.c”
测试结果: