- 论坛徽章:
- 9
|
回复 1# findream_liao
前几天看到你这个问题,想回复,但一时间又想不出答案。今天碰到一个问题,正好把这段代码看了一下。想起来你了。呵呵,重新翻出来,希望你能看到。
首先,理解一下你的意思,你的意思应该是:
void fastcall __sched sleep_on(wait_queue_head_t *q)
{
unsigned long flags;
wait_queue_t wait;
init_waitqueue_entry((&wait,current);
current->state = TASK_UNINTERRUPTIBLE;
(A) ----->如果在这被抢占,并且发生了调度,调度到别的进程,此时因为进程的状态已经为TASK_UNINTERRUPTIBLE,因此,就不会调回来的,也不可以有机会加入wait队列了,当然把不会被wait队列**。我想,这应该是你想问的问题吧。
spin_lock_irqsave(&q->lock,flags);
__add_wait_queue(q,&wait);
spin_unlock(&q->lock);
(B)----》如果在这被抢占,由于已经加入wait队列,后续是会有人**的。这就应该是“镇水铁牛”兄弟想到的答案。
schedule();
spin_lock_irq(q,&wait);
__remove_wait_queue(q,&wait);
spin_unlock_irqrestore(&q->lock,flags);
}
我想,你估计也看到schedule函数的代码,不知到你有没有留意如下的代码:
if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&
unlikely(signal_pending(prev)))) {
prev->state = TASK_RUNNING;
} else {
deactivate_task(rq, prev, 1);
}
switch_count = &prev->nvcsw;
}
标红的,不知你之前是否真理解,为什么prev->state非零(不是运行态)还不直接deactivate_task,而要判段半天??
要说明一点,估计我们还得把置PREEMPT_ACTIVE的代码找出来,他们就是:
1. asmlinkage void __sched preempt_schedule(void)
2. asmlinkage void __sched preempt_schedule_irq(void)
3. static void __cond_resched(void)
你会发现,这三个函数就是内核发生抢占时调用的作有函数了,发生内核抢占是不会直接调用schedule函数的,他们和schedule函数的其中一个本质差别就是先置PREEMPT_ACTIVE再调用schedule函数。
这就是占对你所说的这种情况的,因为我们知道,正常来说,一个进程把自己设成非running状态,下一步肯定是把自己加入一个队列然后主动调schedule,这是任何一个合格的内核程序会都是写出的。如果在进程已经置了TASK_INTERRUPTIBLE情况底下发生了抢占(调用以上三个函数之一),都会在调用schedule(这个schedule调用并不是sleep_on函数的调用,我们现在还停在A处呢)前置上PREEMPT_ACTIVE,这样,在schedule函数里的PREEMPT_ACTIVE判断就生效,此时,并不会调用deactivate_task把任务从运行队列摘除,就算把另外一个任务调度出来了,此后还是有机会再调度回来的,因为它还在运行队列当中(TASK_INTERRUPTIBLE不应影响它重新被选出)。这么做是为了让它有机会往下走,让他自己去调用非抢占版本的正常的schedule把自己切换出去。这样是安全的。因为他们肯定已经加到一个队列中了,后面肯定有人会wakup它。
|
|