临界区:一次只允许一个进程进入
为什么需要临界区?两个生产者因竞争资源会产生随机的执行顺序,造成信号量的修改不当,信号量语义错误,所以需要保护信号量。
临界区代码的保护原则:
1、基本原则——互斥
2、有空让进
3、有限等待
临界区保护方法
1、轮换法:
但是,若P1被什么阻塞了,而P0结束了之后又不能继续进入临界区,导致两个都不在临界区。
2、标记法:
但是,因为变量不共享,所以看不到对方的标记。若如上图所示执行顺序,则会导致两个都不在临界区。
3、Peterson
其实就是在上面一种方法的基础上加上了一个共享变量(非共享+共享)
虽然turn是共享变量,但是不需要保护
可验证Peterson算法满足临界区三条原则
4、多个进程?——面包店算法
注意while(choosing);
也满足三条原则:
5、临界区的另一类保护解法:开关中断
cli=clear interuption ; sti=set interuption
原理是,在进入区阻止调度schedule的发生,而调度是因为中断,所以就是关闭中断
但在多CPU上不行,因为无法控制关闭所有cpu上的中断
6、硬件原子法
原子指令(一次执行,要不不做,做就做完):当TestAndSet得到结果时已经上锁
总结:
临界区域保护信号量的语义正确:面包店算法、开关中断、硬件原子……
信号量实现进程同步
进入睡眠的方式
有if和while两种进入睡眠方式,if非常直白,下面介绍while:
如何理解while?
先看sleep_on函数:
睡眠的实质,就是把自己写到阻塞队列上+改变自己的状态(也就是圈圈里的内容) ,然后再调用schedule
事实上,圈圈里的内容形成了一个睡眠队列:
tmp是局部变量,存储在当前进程的内核栈中,所以可以通过当前的task_struct找到内核栈中的tmp;tmp指向的是下一个task_struct,又可以通过下一个task_struct找到tmp,指向下下一个……由此形成队列
唤醒函数:
进程被唤醒后要从当时进入sleep的后面一句继续执行:
也就是if(tmp) tmp->state=0;
因为tmp指向睡眠队列中的下一个进程,所以会继续唤醒队列中下一个进程
while的好处:一次性全部唤醒,让schedule来决定switch_to谁,这样可以不按照先来先后,可以按优先级。
并且优先级最高的执行后,其他被唤醒的进程会继续上锁,不会一连串执行:
被唤醒后还要判断一遍while(),schedule切换到优先级最高的进程,执行bh->b_lock=1
,而其他被唤醒的在进行while(bh->b_lock)判断时,就执行sleep_on了。