计算机操作系统之信号量机制
整型信号量
把整型信号量定义为一个用于表示资源数目的整型量S, 它与一般的整型量不同,除初始化外,仅通过两个标准的原子操作wait(s),和signal(s)来访问。这两个操作被称为P,V操作。
不遵循"让权等待"原则
wait(s)和signal(s)都是原子操作,不可中断
当一个进程在修改某信号量时,没有其他进程可以对信号量进行修改。
wait(s) //p操作
{
while(S<=0); //如果可用资源数目不足,则原地等待;否则占用一个资源
S--;
}
signal(S) // v操作
{
S++; //释放资源
}
因为不遵循让权等待,所以在没有资源的时候会一直的在等待,使进程处于”忙等“状态。
记录型信号量
需要一种资源且只需要一个
在整型信号量的基础之上采用让权等待的策略之后,除一个整数值s.value外,还要一个进程等待队列s.queue(阻塞在该 信号量的各个进程的标识)。
PS: 信号量作为OS的核心代码执行,不受进程调度的打断,只能通过初始化和两个标准的原语(wait和signal)来访问。初始化一个非负整数的值,表示空闲资源的总数;(如果是负值,其绝对值表示当前等待临界区的进程数)
当S-*value<0时, 表示该类资源已分配完毕,因此进程应调用block原语进行自我阻塞, 放弃处理机, 并插入到信号量链表S>list中。可见, 该机制遵循了“让权等待”准则。此时S->value的绝对值表示在该信号量链表中已阻塞进程的数目。对信号量的每次signal操作表示执行进程释放一个单位资源,使系统中可供分配的该类资源数增加一个, 故S->value++操作表示资源数目加1。若加1后仍是S→value≤0,则表示在该信号量链表中仍有等待该资源的进程被阻塞,故还应调用wakeup原语, 将S->list链表中的第一个等待进程唤醒。如果S->value的初值为1,表示只允许一个进程访问临界资源,此时的信号量转化为互斥信号量,用于进程互斥。
//记录型信号量结构体
struct s{
int value;
List queue;
}
void wait(s)
{
--value; //表示申请一个资源
if(s.value < 0) //没有空闲资源
s.queue; //调用进程进入等待队列
block; //阻塞调用进程
}
void signal(s)
{
++ s.value; //释放一个资源
if(s.value <= 0) //表示有进程处于阻塞状态,则从阻塞队列的头部取出雇一个进程p;
wakeup(); //唤醒进程p
}
AND型信号量
同时需要多种资源且每种资源占用一个
当两个进程m,n各需要资源a,b各3个时才能执行程进程,此时恰好资源a,b各恰好剩余3个,进程m有3个a,进程n有3个b,此时就会因为竞争临界资源造成一种僵持状态,无外力的情况下将无法解脱。
如果此时其中一个进程能够发挥"大爱精神",释放掉自己拥有的资源,就不会出现死锁。
AND同步机制的基本思想是:将进程在整个运行过程中需要的所有资源, 一次性全部地分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其它所有可能为之分配的资源也不分配给它。亦即,对若干个临界资源的分配采取原子操作方式:要么把它所请求的资源全部分配到进程,要么一个也不分配。由死锁理论可知,这样就可避免上述死锁情况的发生。为此, 在wait操作中增加了一个“AND”条件, 故称为AND同步, 或称为同时wait操作。
//p原语
swait(S1,S2,S3,...,Sn)
{
while(true)
{
if(S1 >= 1 && S2 >= 1 && ... && Sn >= 1) //满足全部资源要求时的处理;
{
for(i = 1; i <= n; ++i) --Si;
break;
}
else //某些资源不够时的处理;
{
//调用进程进入第一个小于1信号量的等待队列
Sj.queue;
block; //阻塞调用进程
}
}
}
//V原语S
signal(S 1, S 2,·..fSn){
while(TRUE) {
for(i=1; i<=n;++i){
++Si;//释放占用的资源;
for(each process P waitingin Si.queue) { //检查每种资源的等待队列的所有进程;从等待队列si.queue中取出进程P;
if(判断进程P是否通过S wait中的测试) {
//注:与signal不同, 这里要进行重新判断;
//通过检查(资源够用)时的处理;进程P进入就绪队列;}
else{
//未通过检查(资源不够用)时的处理;进程P进入某等待队列;
}
}
}
信号量集
用于同时需要多种资源,每种资源需要使用的数目不同,且可分配的资源存在一个临界值时的处理
在AND型信号量集的基础上进行扩充:进程对信号量Si的测试值为ti(用于信号量的判断,即Si>=ti,表示资源数量低于ti时,便不予分配)9占用值为di(用于信号量的增减,即Si=Si-di和Si=Si+di)
swait(S1,t1,d1,S2,t2,d3,S3,t3,d3,...,Sn,tn,dn)
{
while(true)
{
if(S1 >= t1 && S2 >= t2 && ... && Sn >= tn) //满足全部资源要求时的处理;
{
for(i = 1; i <= n; ++i) Si = Si - di;
break;
}
else //某些资源不够时的处理;
{
//调用进程进入第一个小于1信号量的等待队列
Sj.queue;
block; //阻塞调用进程
}
}
}
//V原语S
signal(S1,d1,S2,d2,...,Sn,dn){
while(TRUE) {
for(i = 1; i <= n;++i){
Si = Si + di;//释放占用的资源;
for(each process P waitingin Si.queue) { //检查每种资源的等待队列的所有进程;从等待队列si.queue中取出进程P;
if(判断进程P是否通过S wait中的测试) {
//注:与signal不同, 这里要进行重新判断;
//通过检查(资源够用)时的处理;进程P进入就绪队列;
}
else{
//未通过检查(资源不够用)时的处理;进程P进入某等待队列;
}
}
}
特殊情况
Swait(S,d,d) :表示每次申请d个资源,当少于d个时不分配;
Swait(S,1,1) :蜕化为记录型信号量(S>1)或互斥信号量(S=1);
Swait(S,1,0) :作为一个可控开关当S>=1时,允计许多个进程进入临界区;
}
}
}
### 特殊情况
Swait(S,d,d) :表示每次申请d个资源,当少于d个时不分配;
Swait(S,1,1) :蜕化为记录型信号量(S>1)或互斥信号量(S=1);
Swait(S,1,0) :作为一个可控开关当S>=1时,允计许多个进程进入临界区;
当S=0时,,禁止任何进程进入临界区;