sleep_on 先设 state 不会有问题吗
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;
spin_lock_irqsave(&q->lock,flags);
__add_wait_queue(q,&wait);
spin_unlock(&q->lock);
schedule();
spin_lock_irq(q,&wait);
__remove_wait_queue(q,&wait);
spin_unlock_irqrestore(&q->lock,flags);
}
想请教下为什么先设置 state再 插入 等待队列不会有问题?
如果 在设置完state 后, 发生内核抢占 , 那当前进程是不是会以 TASK_UNINTERRUPRIBALE的 状态被切出去?
是的话 还没插入等待队列 那在哪里还能**该进程?
还是说着种情况下 current task 不会被从运行队列中删除 , 而选择可运行进程时只看bitmap 不管 state的, 所以它还是会在某次schedule中被调度运行呢? sleep_on(wait_queue_head_t *q)这个函数的名字就表达其它的作用。
如果直接current->state = TASK_UNINTERRUPTIBLE;
schedule();
这样用,肯定是有问题的。
但是先加
spin_lock_irqsave(&q->lock,flags);
__add_wait_queue(q,&wait);
spin_unlock(&q->lock);
就是可以的了。 回复 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它。
原先只知道PREEMPT_ACTIVE是内核在抢占的时候会置位,但没有追过是那几个函数置位的,现在领教了。:lol
页:
[1]