sleep与wakeup分析
1. 案例
首先来看下xv6的sleep和wakeup使用例子:
cquire(lock) sleep(chan,lock) release(lock) acquire(lock) wakeup(chan) release(lock)
可以看出,在xv6下使用sleep和wakeup时必须要锁将这两个接口保护起来。
2. sleep函数
- 检查sleep接口中的锁是否存在。
- 检查此锁是否是保护进程表的锁,如果是,则表明在调用sleep的时候已经被ptable.lock锁保护住,那么就继续执行;如果不是,那么由于要修改进程表中的数据,所以需要获取进程表锁,当获取了进程表锁后,sleep就被进程表保护了,那么之前的锁就可以释放了。
- 修改进程的状态为休眠态。
- 调用sched()函数切换到内核scheduler线程。
sched函数如下:
从代码可以看出,调用sched()函数之前需要满足以下几个条件:
- 需要持有ptable.lock。
- 进程状态必须是非运行态。
- 处理器必须是关中断状态。
其中处理器关中断状态是通过acquire接口间接实现的,因为acquire接口实现会关闭中断。
接着调用swtch()函数切换到内核schduler线程,回头来看看schduler线程:
schduler线程再次运行时,从switchkvm开始运行,然后选出下一个线程运行,从而可以看出,不管是内核scduler线程还是通过sched调用swtch()函数,前后都是获取和释放ptable.lock。
3. wakeup函数
wakeup的逻辑很简单,就是到进程表中寻找满足以下两个条件的进程:
- 状态时睡眠状态的进程。
- 进程中记录的chan和wakeup传进来的相同的进程。
从代码中可以看出,当调用完wakeup之后,只是将进程的状态改成可运行,并没有立即切换到新进程运行。下一个调度点应该是tick中断中调度运行的。
评论