一、什么是信号量
信号量本质是一个用于描述资源数目的计数器。主要用于同步和互斥。
相关概念:
1.临界资源:两个或多个进程看到的一份公共资源叫做临界资源。
2.临近区:各个进程访问临界资源的代码叫做临界区。
3.互斥:在临界区中,通过临界区访问临界资源,在某一个时刻只能有一个进程。(独占、排他)
4.同步:为了解决有的进程周而复始的占有资源而其他进程长时间得不到资源的问题,排队,公平访问。
5.原子性:访问不中断(要么不执行,要么执行全部)。
二、信号量工作原理
举个例子:
假设现在有一个盘子,我们就可以记作count=1,现在小明将盘子拿走,那么count就要减一,此时盘子数为0,小红也想拿盘子,但是现在盘子数count=0,那么小红现在只能排队等,过了一会小明将盘子归还,count++,现在小红就可以从队列里出来拿盘子了。
这样就比较清楚了,信号量的工作原理其实就是PV操作,P表示资源数减一,V表示资源数加一。
1.信号量结构体伪代码:
struct semaphore
{
int value;//资源数目
pointer_PCB queue;//进程等待队列
}
2.P原语:
P(s)
{
s.value = s.value--;
if (s.value < 0)
{
//该进程状态置为等待状状态
//将该进程的PCB插⼊相应的等待队列s.queue末尾
}
}
3.V原语:
V(s)
{
s.value = s.value++;
if (s.value < =0)
{
//唤醒相应等待队列s.queue中等待的⼀个进程
//改变其状态为就绪态
//并将其插⼊就绪队列
}
}
三、信号量操作函数
1.semget函数
功能:⽤来创建和访问⼀个信号量集
原型
int semget(key_t key, int nsems, int semflg);
参数
key: 信号集的名字(ftok获得)
nsems:信号集中信号量的个数
semflg: 由九个权限标志构成,它们的⽤法和创建⽂件时使⽤的mode模式标志是⼀样的
返回值:成功返回⼀个⾮负整数,即该信号集的标识码;失败返回-1
2,PV操作
功能:⽤来创建和访问⼀个信号量集
原型
int semop(int semid, struct sembuf *sops, unsigned nsops);
参数
semid:是该信号量的标识码,也就是semget函数的返回值
sops:是个指向⼀个结构数值的指针
nsops:信号量的个数
返回值:成功返回0;失败返回-1
sembuf结构体:
struct sembuf {
short sem_num;
short sem_op;
short sem_flg;
};
sem_num是信号量的编号。
sem_op是信号量⼀次PV操作时加减的数值,⼀般只会⽤到两个值:
⼀个是“-1”,也就是P操作,等待信号量变得可⽤;
另⼀个是“+1”,也就是V操作,发出信号量已经变得可⽤
sem_flag的两个取值是IPC_NOWAIT或SEM_UNDO
3.senctl函数
功能:⽤于控制信号量集
原型
int semctl(int semid, int semnum, int cmd, ...);
参数
semid:由semget返回的信号集标识码
semnum:信号集中信号量的序号
cmd:将要采取的动作(有三个可取值)
最后⼀个参数根据命令不同⽽不同
返回值:成功返回0;失败返回-1
命令:
IPC_RMID:删除信号集
SETVAL:设置信号量集中信号量的计数值
GETVAL:获取信号量集中信号量的计数值
联合体arg:
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO */
};