内核等待队列机制介绍
们两的差异只在那一行而已。
在 sched.c 里,SLEEP_ON_VAR 是一个 macro,其实它只是定义两个区域变量出来而已。
#defineSLEEP_ON_VAR\
unsigned long flags;\
struct wait_queue wait;
刚才我也说过,current 这个变量是指到目前正在执行的 process 的 task_struct 结构。所以 current->state = TASK_INTERRUPTIBLE 会设定在呼叫 interruptible_sleep_on() 的 process 身上。至于 SLEEP_ON_HEAD 做的事,则是将 current 的值放到 SLEEP_ON_VAR 宣告的 wait 变量里,并把 wait 放到 interruptible_sleep_on() 的参数所属的 wait_queue list 中。
#defineSLEEP_ON_HEAD\
wait.task = current;\
write_lock_irqsave(waitqueue_lock,flags);\
__add_wait_queue(p,wait);\
write_unlock(waitqueue_lock);
wait 是在 SLEEP_ON_VAR 中宣告的区域变量。其 task 字段被设成呼叫 interruptible_sleep_on() 的 process。至于 waitqueue_lock 这个变量是一个 spin lock。 waitqueue_lock 是用来确保同一时间只能有一个 writer。但同一时间则可以有好几个 reader。也就是说 waitqueue_lock 是用来保证 critical section 的 mutual exclusive access。
unsigned long flags;
write_lock_irqsave(waitqueue_lock,flags);
...critical section ...
write_unlock(waitqueue_lock)
学过 OS 的人应该知道 critical section 的作用是什么,如有需要,请自行参考 OS 参考书。在 critical section 里只做一件事,就是将 wait 这个区域变量放到 p 这个 wait_queue list 中。 p 是 user 在呼叫 interruptible_sleep_on() 时传进来的,它的型别是 struct wait_queue **。在此, critical section 只呼叫 __add_wait_queue()。
extern inline void __add_wait_queue(struct wait_queue ** p,
struct wait_queue * wait)
{
wait->next = *p ? : WAIT_QUEUE_HEAD(p);
*p = wait;
}
__add_wait_queue() 是一个inline function,定义在 中。WAIT_QUEUE_HEAD()是个很有趣的 macro,待会我们再讨论。现在只要知道它会传回这个 wait_queue 的开头就可以了。所以,__add_wait_queue() 的意思就是要把 wait 放到 p 所属的 wait_queue list 的开头。但是,大家还记得吗? 在上面的例子里,一开始我们是把 write_wq 设为 NULL。也就是说 *p 是 NULL。所以,当 *p 是 NULL 时,
wait->next = WAIT_QUEUE_HEAD(p)
是什么意思呢?
所以,现在,我们来看一下 WAIT_QUEUE_HEAD() 是怎么样的一个 macro,它是定义在 里。
#define WAIT_QUEUE_HEAD(x) ((struct wait_queue *)((x)-1))
x 型别是 struct wait_queue **,因为是一个 pointer,所以大小是 4 byte。因此,若 x 为 100 的话,那 ((x)-1) 就变成 96。如下图所示。 WAIT_QUEUE_HEAD(x) 其实会传回 96,而且将其转型为 struct wait_queue*,各位可以看看。原本的 wait_queue* 只配制在 100-104 之间。现在 WAIT_QUEUE_HEAD(x) 却直接传回96,但是 96-100 这块位置根本没有被我们配置起来。更妙的事。由于 x 是一个 wait_queue list 的开头,我们始终不会用到 96-100 这块,我们只会直接使用到 100-104 这块内存。这也算是 wait_queue 一项比较奇怪的 implementation 方式吧。下面有三张图,第一张表示我们宣告了一个 wait_queue* 的变量,地址在 100。另外还有一个 wait_queue 的变量,名叫 wait。第二张图是我们呼叫 interruptible_sleep_on() 之后得到的结果。第三张则是我们又宣告一个 wait_queue,名叫 ano_wait,将 ano_wait 放到 wait_queue list 后的结果就第三张图所显示的。http:/linuxfab.cx/Columns/10/wqq.GIF
在 interruptible_sleep_on() 中,当呼叫完 SLEEP_ON_HEAD 之后,目前的 process 就已经被放到 wait_queue 中了。接下来会直接呼叫 schedule(),这个 function 是用来做 scheduling 用的。current 所指到的 process 会被放到 scheduling queue 中等待被挑出来执行。执行完 schedule() 之后,current 就没办法继续执行了。而当 current 以后被 wake up 时,就会从 schedule() 之后,也就是从 SLEEP_ON_TAIL 开始执行。SLEEP_ON_TAIL 做的事刚好跟 SLEEP_ON_HEAD 相反,它会将此 process 从 wait_queue 中移除。
#defineSLEEP_ON_TAIL\
write_lock_irq(waitqueue_lock);\
__remove_wait_queue(p,wait);\
write_unlock_irqrestore(waitqueue_lock,flags);
跟 SLEEP_ON_HEAD 一样。SLEEP_ON_TAIL 也是利用 spin lock 包住一
嵌入式新闻 嵌入式资料 嵌入式培训 嵌入式linux 嵌入式系统 嵌入式开发 嵌入式 相关文章:
- 煤矿井下综合自动化系统中的应用(04-06)
- 软件Overlay:程序编写与调试(01-20)
- USB数据通信接□模块的程序设计(10-17)
- 东江产业园:力争2017年产值达千亿(09-30)
- 硅谷数模的SlimPort扩大了Nexus7的显示屏选择(08-01)
- Lonworks控制网络技术在城市排水泵站自动化中的应用(06-06)