Chinaunix首页 | 论坛 | 博客
  • 博客访问: 33758
  • 博文数量: 22
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 105
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-17 22:07
文章分类

全部博文(22)

文章存档

2016年(9)

2015年(1)

2014年(10)

2013年(2)

我的朋友

分类: LINUX

2013-07-18 22:20:09


点击(此处)折叠或打开

  1. #define wait_event_interruptible(wq, condition)                \
  2. ({                                    \
  3.     int __ret = 0;                            \
  4.     if (!(condition))                        \
  5.         __wait_event_interruptible(wq, condition, __ret);    \
  6.     __ret;                                \
  7. })

  8. #define __wait_event_interruptible(wq, condition, ret)            \
  9. do {                                    \
  10.     DEFINE_WAIT(__wait);                        \
  11.                                     \
  12.     for (;;) {                            \
  13.         prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);    \
  14.         if (condition)                        \
  15.             break;                        \
  16.         if (!signal_pending(current)) {                \
  17.             schedule();                    \
  18.             continue;                    \
  19.         }                            \
  20.         ret = -ERESTARTSYS;                    \
  21.         break;                            \
  22.     }                                \
  23.     finish_wait(&wq, &__wait);                    \
  24. } while (0)

1._wait_event 具体实现过程 

prepare_to_wait()和finish_wait()并不是进程睡眠的地方,进程睡眠的地方是schedule()。

prepare_to_wait()只是进行一些链表的操作,以确保自己在等待队列中,不会漏掉事件。

进程在确信自己已经在队列中后,再次检查条件, 这里,如果不检查,可能条件已经满足,直接去睡眠的话,可能再也没有人来唤醒它了。

然后,如果条件不满足,就调用schedule()去睡眠,这里,进程的状态在prepare_to_wait()里设置为TASK_UNINTERRUPTIBLE, 所以,以后调度时就看不到该进程了,因此,该进程将没有机会运行,这就是睡眠。

注意,这里,该进程自己已经无能为力了,因为它自己已经不可能运行了。 只有等待他人来唤醒了。

当条件满足后,会有一个人(或者是其他进程,或者内核本身,等等)来唤醒某个等待队列上的进程。

具体是唤醒全部等待队列中的所有进程,还是只唤醒第一个进程,完全取决于该唤醒者, 等待在队列中的睡眠进程是无能为力的,与它们是没有关系的(呵呵,确切说,有一点关系)。

总是唤醒所有等待该事件的进程并不一定是合适的。比如考虑这样一种情况:如果队列中的多个进程等待的资源是要互斥访问的,一定时间内只允许一个进程去访问的话,这时候,只需要唤醒一个进程就可以了,其他进程继续睡眠。如果唤醒所有的进程,最终也只有一个进程获得该资源,其他进程让需返回睡眠。

因此,等待队列中的睡眠进程可被划分为互斥、非互斥进程。

互斥进程:等待的资源是互斥访问的;互斥进程由内核有选择的唤醒,等待队列项的flag字段为1;
        非互斥进程:等待的资源是可多进程同时访问的。非互斥进程在事件发生时,总是被内核唤醒,等待队列元素的flag字段为0。

唤醒者通常调用__wake_up_common(),这样,依次取下等待队列中的__wait_queue_t结构, 调用该睡眠进程设置的func函数,即这里的autoremove_wake_function(), 将该进程的状态重新设置为RUNNING。

注意,此时该睡眠进程并不会立刻执行,只有等到下次调度的时候,该进程才有机会运行, 即醒来了。醒来是从schedule()回来,继续运行__wait_event()

总结一下, 睡眠是自己设置好进程状态(TASK_UNINTERRUPTIBLE,等等),加入等待队列, 并调用schedule()去睡眠。 睡眠是自己的动作。

唤醒是别人发现条件满足,调用__wake_up_common(),将睡眠进程从等待队列取下, 调用该睡眠进程设置的唤醒func,重新设置该睡眠进程为RUNNING。 从而可以在下次调度时运行。 唤醒是别人的动作。

2. 关于 wait_event_interruptible() 和 wake_up()的使用 
   
读一下wait_event_interruptible()的源码,不难发现这个函数先将当前进程的状态设置成TASK_INTERRUPTIBLE,然后调用schedule(), 而schedule()会将位于TASK_INTERRUPTIBLE状态的当前进程从runqueue 队列中删除。从runqueue队列中删除的结果是,当前这个进程将不再参 与调度,除非通过其他函数将这个进程重新放入这个runqueue队列中,这就是wake_up()的作用了。 
  
由于这一段代码位于一个由condition控制的for(;;)循环中,所以当由shedule()返回时(当然是被wake_up之后,通过其他进程的schedule()而再次调度本进程),如果条件condition不满足,本进程将自动再次被设置为TASK_INTERRUPTIBLE状态,接下来执行schedule()的结果是再次被 从runqueue队列中删除。这时候就需要再次通过wake_up重新添加到 runqueue队列中。 
  
如此反复,直到condition为真的时候被wake_up. 
  
可见,成功地唤醒一个被wait_event_interruptible()的进程,需要满足: 
  
   在 1)condition为真的前提下,2) 调用wake_up()。 

所以,如果你仅仅修改condition,那么只是满足其中一个条件,这个时候,被wait_event_interruptible()起来的进程尚未位于runqueue队列中,因 此不会被schedule。这个时候只要wake_up一下就立刻会重新进入运行调度。 
  
3. 关于wait_event_interruptible的返回值 
  
根据 wait_event_interruptible 的宏定义知: 
  
   1) 条件condition为真时调用这个函数将直接返回0,而当前进程不会 被 wait_event_interruptible和从runqueue队列中删除。 
  
   2) 如果要被wait_event_interruptible的当前进程有nonblocked pending  signals, 那么会直接返回-ERESTARTSYS(i.e. -512),当前进程不会被wait_event_interruptible 和从runqueue队列中删除。 
  
   3) 其他情况下,当前进程会被正常的wait_event_interruptible,并从 runqueue队列中删除,进入TASK_INTERRUPTIBLE状态退出运行调度,直到再次被唤醒加入runqueue队列中后而参与调度,将正常返回0。 

阅读(6517) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~