16-信号量机制
信号量机制
引例
- 进程互斥的四种软件实现方法:
- 单标志法
- 双标志先检查法
- 进入区“检查”,上锁指令无法一气呵成,从而导致了所有进程都有可能进入临界区的问题
- 双标志后检查法
- Peterson算法
- 进程互斥的三种硬件实现方法:
- 中断屏蔽方法
- TS/TSL指令
- Swap/XCHG指令
- 所有的解决方法都无法实现“让权等待”
- 1965年,荷兰学者Dijsktra提出了一种桌有成效的进程互斥、同步方法–信号量机制。
信号量机制
用户进程可以通过操作系统提供的一对原语来对信号量进程操作,从而很方便的实现了进程互斥、进程共享。
信号量其实就是一个变量(可以是一个整数,也可以是记录型变量可以一个信号量表示系统中某种资源的梳理。
软件解决方法的主要问题"是进入区的各个操作无法一气呵成",因此如果能把进入区、退出区的操作都用原语实现,使得操作能“一气呵成”就能避免问题。
信号量原语
一对原语:wait(S)和signal(S)原语,可以把原语理解为我们自己写的函数,函数名分别为wait和signal,括号里的信号量S其实就是函数调用时传入的一个参数。
wait,signal原语简称P,V操作,因此做题时候常把wait(S),signal(S)两个操作分别写为P(S),V(S)
0
整型信号量
用一个整型的变量作为信号量,用来表示系统中某种资源的数量
示例代码
初始化代码
int S=1;
void wait(int S){ //wait原语,相当于进入区
while(S<=0); //如果资源数量不够,就一直循环等待
S=S-1; //如果资源数量够,则占用一个资源
}
void signal(int S){//Signal原语,相当于退出区
S=S+1; //使用完资源后,在退出区释放资源
}
进程代码
wait(S); //进入区,申请资源
使用打印机资源;//临界区,访问资源
signal(S); //退出区,释放资源
存在的问题
- 与普通整数变量的区别,即对信号量的操作只有三种,即初始化,P操作,V操作。
- “检查”和“上锁”一气呵成,避免了并发,异步导致的问题。
- 存在的问题:不满足“让权等待”,会发生忙等。
记录型信号量
整型信号量存在的缺陷是忙等问题,因此人们又提出“记录型信号量”,即用数据结构表示信号量
示例代码
初始化代码
/*记录型信号量的定义*/
typedef struct{
int value; //剩余资源数
struct process *L;//等待队列
}sempahore;
原语操作代码
void wait(semaphore S){
S.value--;
if(S.value <0){
block(S.L);
//如果剩余资源数不够,就会使用Block原语,使得进程从运行态进入阻塞态,并把挂到信号量S的等待队列中.
}
}
void signal(semaphore S){
S.value++;
if(S.value<=0){
wakeup(S.L);
//释放资源后,若还有别的进程在等待这种资源,则使用wakeup原语唤醒队列中的一个进程,改进程从阻塞态变为就绪态。
}
}
- 对信号量S的一次P操作意味着请求一次资源,所以Value减一如果value<0时候表示资源已经分配完毕,应该用block原语进行自我阻塞(当前进程的状态从运行态到阻塞态),主动放弃处理机,并插入该类资源的等待队列S.L中,该机制遵循了“让权等待”原则。
- 对信号量S的一次V操作意味着释放一次资源,所以Value加1,如果加1后仍然S<=0,表示仍有进程等待该资源,因此应该用wakeup原语唤醒等待队列中的第一个进程。