调度器, 从lost wake-up problem说起
文来自本人的旧博客 blog.163.com/awaken_ing/blog/static/1206131972016124113539444/
0. 引言
The lost wake-up problem 请参考 http://www.linuxjournal.com/article/8144
本篇主要解释为何修改后的代码没有问题.
修改后的代码为:
1 set_current_state(TASK_INTERRUPTIBLE);
2 spin_lock(&list_lock);
3 if(list_empty(&list_head)) {
4 spin_unlock(&list_lock); //如果这里面让出cpu?
//如果在这个点被生产者唤醒会如何?
5 schedule();
6 spin_lock(&list_lock);
7 }
8 set_current_state(TASK_RUNNING);
9
10 /* Rest of the code ... */
11 spin_unlock(&list_lock);
1. 先解释linux journal上的内容
The change is that whenever a wakeupprocess is called for a process whose state is TASKINTERRUPTIBLE or TASKUNINTERRUPTIBLE, and the process has not yet called schedule(), the state of the process is changed back to TASK_RUNNING.
本文假定调度器使用的是CFS. 假定在第4行和第5行之间 进程B调用 wakeupprocess().
linux-3.10.86/kernel/sched/core.c
wake_up_process
{
return try_to_wake_up(p, TASK_NORMAL,0);
}
#define TASK_NORMAL (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{
int success = 0;
if (! (p->state & state) )
goto out; //not our case
...
if (p->on_rq && ttwu_remote(p, wake_flags)) //our case
goto stat;
...
out:
...
return success;
}
ttwuremote -> ttwudowakeup -> p->state = TASKRUNNING;
进程TASK_RUNNING下调用schedule() :
__schedule
{
if(prev->state &&...)
deactivate_task //not our case
}
0==TASKRUNNING, 所以,
这个if (prev->state)等效于if ( prev->state != TASKRUNNING )
所以, TASKRUNNING调用schedule()不会调用到deactivatetask().
deactivate_task 都完成啥动作???
2. 再看看另外一种情况
在第4行spinunlock()中, 如果是个抢占的时机, 那么不是直接调用schedule(), 而是调用 preemptschedule()
preempt_schedule
{
...
add_preempt_count_notrace(PREEMPT_ACTIVE);
__schedule();
sub_preempt_count_notrace(PREEMPT_ACTIVE);
...
}
__schedule
{
if (prev->state && !( preempt_count() & PREEMPT_ACTIVE )) {
if(...)
{
deactivate_task
prev->on_rq = 0;
}
}
put_prev_task(rq, prev);
next = pick_next_task(rq);
}
这里的!( preemptcount() & PREEMPTACTIVE )就不会满足, 就不会执行deactivate_task().
3. 重新挂到红黑树上
由于当前运行的进程在被选中运行时, 就被移出红黑树了. 上面两种情况中, se节点又是如何重新放到rbtree上的呢? 看putpreventity() :
put_prev_entity(...)
{
if(prev->on_rq){
__enqueue_entity(cfs_rq, prev);
}
}
所以, 是这里的 putpreventity()将节点重新放到rbtree上.
4. 防范
既然容易出现上面的问题, 那么, 内核是如何处理这个问题的呢? 答案是内核提供了宏_waitevent, 可参考其写法:
#define __wait_event(wq, condition) \
do{ \
DEFINE_WAIT(__wait); \
\
for(;;){ \
prepare_to_wait(&wq,&__wait, TASK_UNINTERRUPTIBLE); \
if(condition) \
break; \
schedule(); \
} \
finish_wait(&wq,&__wait); \
}while(0)
preparetowait(... , TASKUNINTERRUPTIBLE) 会设置进程的状态为TASKUNINTERRUPTIBLE,
finishwait()设置状态为TASKRUNNING.
本文地址: https://awakening-fong.github.io/posts/scheduler/scheduler_01_lost_wake-up
转载请注明出处: https://awakening-fong.github.io
若无法评论, 请打开JavaScript, 并通过proxy.
blog comments powered by Disqus