wait_event_interruptible() 和 wake_up()的使用
2016-12-04 20:44
295 查看
1. 关于 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一下就立刻会重新进入运行调度。
2. 关于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。
附1:wait_event_interruptible 宏
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
注: C语言中{a,b,
..., x}的的值等于最后一项,即x,因此上述
宏的值是 __ret。
附2:_wait_event_interruptible()和 wake_up的等效代码
#define __wait_event_interruptible(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); \
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
if (condition) \
break; \
if (!signal_pending(current)) { \
schedule(); \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
finish_wait(&wq, &__wait); \
} while (0)
void wake_up(wait_queue_head_t *q)
{
struct list_head *tmp;
wait_queue_t *curr;
list_for_each(tmp, &q->task_list)
{
curr = list_entry(tmp, wait_queue_t, task_list);
wake_up_process(curr->task);
/* implicit call: add_to_runqueue(curr->task);*/
if (curr->flags)
break;
}
}
//从等待队列中唤醒状态为TASK_UNITERRUPTABLE的进程
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
//从等待队列中唤醒状态为TASK_ITERRUPTABLE的进程
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
3, _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。 从而可以在下次调度时运行。 唤醒是别人的动作。
读一下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一下就立刻会重新进入运行调度。
2. 关于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。
附1:wait_event_interruptible 宏
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
注: C语言中{a,b,
..., x}的的值等于最后一项,即x,因此上述
宏的值是 __ret。
附2:_wait_event_interruptible()和 wake_up的等效代码
#define __wait_event_interruptible(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); \
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
if (condition) \
break; \
if (!signal_pending(current)) { \
schedule(); \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
finish_wait(&wq, &__wait); \
} while (0)
void wake_up(wait_queue_head_t *q)
{
struct list_head *tmp;
wait_queue_t *curr;
list_for_each(tmp, &q->task_list)
{
curr = list_entry(tmp, wait_queue_t, task_list);
wake_up_process(curr->task);
/* implicit call: add_to_runqueue(curr->task);*/
if (curr->flags)
break;
}
}
//从等待队列中唤醒状态为TASK_UNITERRUPTABLE的进程
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
//从等待队列中唤醒状态为TASK_ITERRUPTABLE的进程
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
3, _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。 从而可以在下次调度时运行。 唤醒是别人的动作。
相关文章推荐
- wait_event_interruptible() 和 wake_up()的使用
- wait_event_interruptible与wake_up配套使用
- 关于 wait_event_interruptible() 和 wake_up()的使用
- wait_event_interruptible() 和 wake_up()的使用 .
- wait_event_interruptible() 和 wake_up()的使用
- 关于 wait_event_interruptible() 和 wake_up()的使用
- wait_event_interruptible() 和 wake_up()的使用
- wait_event_interruptible()与wake_up()的使用
- wait_event_interruptible() 和 wake_up()的使用
- 有关 等待队列和wait_event_interruptible() 和 wake_up()
- wait_event_interruptible和wake_up_interruptible
- wait_event_interruptible() 和 wake_up()的工作流程
- Linux驱动中的 wait_event_interruptible 与 wake_up_interruptible 深度理解
- linux内核中等待队列 (函数wait_event与wake_up)
- linux内核中等待队列(wait_event,wake_up...)
- wait_event_interruptible 使用方法
- linux内核中等待队列 (函数wait_event与wake_up)
- wait_event_interruptible 使用方法
- linux等待队列之wait_event和wake_up
- wait_event_interruptible 使用方法