文章目录
- 一、知识预览
- 二、前篇文章知识点回顾
- 三、信号量机制
- 四、信号量机制—整形信号量
- 五、信号量机制—记录型信号量
- 六、总结
一、知识预览
二、前篇文章知识点回顾
进程互斥的四种软件实现方式:单标志法、双标志先检查、双标志后检查、Peterson算法。( 具体知识点见(十八)操作系统-进程互斥的软件实现方法 )
进程互斥的三种硬件实现方法:中断屏蔽方法、TS/TSL指令、Swap/XCHG指令。(具体知识点见(十九)操作系统-进程互斥的硬件实现方法 )
在双标志先检查法中,进入区的“检查”、“上锁”操作无法一气呵成,从而导致了两个进程有可能同时进入临界区的问题。
所有的解决方案都无法实现“让权等待”。
三、信号量机制
用户进程可以通过使用操作系统的一对原语来对信号量进行操作,从而很方便的实现了进程互斥、进程同步。
信号量就是一个变量(可以是一个整数,也可以是更复杂的记录型变量),可以用一个信号量来表示系统中某种资源的数量。比如:系统中只有一台打印机,就可以设置一个初值为1的信号量。
原语是一种特殊的程序段,其执行只能一气呵成,不能被中断。原语是由关中断/开中断指令实现的。软甲解决方案的只要问题是由“进入区的各种操作无法一气呵成”,因此如果能把进入区、退出区的操作都用“原语”实现,使这些操作能“一气呵成”就能避免问题。
一对原语:wait(S)原语和signal(S)原语,可以把原语理解成为我们自己写的函数,函数名分别为wait和signal,信号量S其实是函数调用时传入的一个参数。
wait、signal原语常简称为P、V操作。
四、信号量机制—整形信号量
用一个整数型的变量作为信号量,用来表示系统中某种资源的数量。与普通整数变量的区别在于,对信号量的操作只有三种:初始化、P操作、V操作。
举例:某计算机系统中有一台打印机
int s = 1; //初始化整型信号量s,表示当前系统中可用的打印机资源数
void wait (int S) { // wait原语,相当于"进入区"
while (s <= 0); //如果资源数不够,就一直循环等待
S=S-1; //如果资源数够,则占用一个资源
}
void signal (int S) { // signal原语,相当于"退出区”
S=S+1; //使用完资源后,在退出区释放资源
}
//进程p0操作
进程PO:
...
wait(S); //进入区,申请资源
使用打印机资源... //临界区,访问资源
signal(S); //退出区,释放资源
...
//p1进程进入申请资源擦欧总
进程P1:
...
wait(S); //进入区,申请资源
使用打印机资源... //临界区,访问资源
signal(S); //退出区,释放资源
...
进程p0想要使用打印机资源,由于资源有限只有一个,并且要互斥访问这个打印机,所以在使用打印机资源之前,进程p0要先执行一个wait原语,对信号量S进行操作。wait原语中,通过循环语句检查当前剩余资源数量是否足够;若x小于等于0,则说明系统已经没有这种资源了,进程会一直循环。
但由于p0进程执行wait原语的时候,s的值是1,所以会跳出循环,执行下一句。将打印机资源分配给p0进程,系统当中打印机资源减一。
当p0在访问打印机资源时,如果发生了进程切换,有另外的进程也想使用打印机资源,使用之前同样先执行wait原语。由于系统当中已经没有打印机资源了,所以p1在执行while循环时,会一直循环等待。
直到p0进程用完释放资源,执行signal原语,将信号量s由0变为1。p1才会跳出循环,执行下一步。
补:
“检查”和“上锁”一气呵成,避免了并发、异步导致的问题。
存在的问题:不满足“让权等待”原则,会发生“忙等”。
五、信号量机制—记录型信号量
记录型信号量:用记录型整数结构表示的信号量。
block原语:如果剩余资源数不够,使用block原语使进程从运行态进入阻塞态,并把挂到信号量S的等待队列(即阻塞队列)中。
wakeup原语:释放资源后,若还有别的进程在等待这种资源,则使用wakeup原语唤醒等待队列中的一个进程,该进程从阻塞态变为就绪态。
在考研题目中wait(S)、signal(S)也可以记为P(S)、V(S),这对原语可用于实现系统资源的“申请”和“释放”。S.value 的初值表示系统中某种资源的数目。
/*记录型信号量的定义*/
typedef struct {
int value; //剩余资源数
struct process *L; //等待队列
} semaphore;
/*某进程需要使用资源时,通过wait原语申请*/
void wait ( semaphore s) {
s.value--;
if (S.value < 0 ) {
'block (S.L);
}
}
/*进程使用完资源后,通过signal原语释放*/
void signal (semaphore s) {
s.value++;
if (s.value <= 0) {
wakeup(S.L);
}
}
对信号量s的一次Р操作意味着进程请求一个单位的该类资源,因此需要执行S.value–,表示资源数减1,当S.value <0时表示该类资源己分配完毕,因此进程应调用block原语进行自我阻塞(当前运行的进程从运行态—>阻塞态),主动放弃处理机,并插入该类资源的等待队列S.L中。可见,该机制遵循了“让权等待”原则,不会出现“忙等”现象。
对信号量s的一次V操作意味着进程释放一个单位的该类资源,因此需要执行S.value++,表示资源数加1,若加1后仍是S.value <=o,表示依然有进程在等待该类资源,因此应调用wakeup原语唤醒等待队列中的第一个进程(被唤醒进程从阻塞态→就绪态)。
例题:
1. CPU为p0进程服务
2. 切换到p1进程
3. 切换到p2进程,没有资源等待。
4. 切换到p3进程,没有资源等待。
5. p2、p3均不能执行,CPU切换回为p1服务。p0执行完成释放资源。
6. p2使用打印机资源,执行完成后释放资源。
7. p1执行完成释放。
8. p3执行完释放,结束。