免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1324 | 回复: 3
打印 上一主题 下一主题

[内核入门] sleep_on 先设 state 不会有问题吗 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-11-16 12:28 |只看该作者 |倒序浏览
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中被调度运行呢?

论坛徽章:
2
寅虎
日期:2014-11-25 21:47:342015小元宵徽章
日期:2015-03-06 15:58:18
2 [报告]
发表于 2014-11-16 20:17 |只看该作者
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);
就是可以的了。

论坛徽章:
9
辰龙
日期:2014-08-18 20:38:42未羊
日期:2014-09-04 08:50:45丑牛
日期:2014-09-06 00:12:55寅虎
日期:2014-12-22 20:50:56摩羯座
日期:2015-01-14 22:28:15巳蛇
日期:2015-01-23 20:39:272015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之青岛
日期:2016-03-13 23:37:1915-16赛季CBA联赛之深圳
日期:2016-03-29 18:52:38
3 [报告]
发表于 2014-11-24 23:29 |只看该作者
回复 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它。

论坛徽章:
2
寅虎
日期:2014-11-25 21:47:342015小元宵徽章
日期:2015-03-06 15:58:18
4 [报告]
发表于 2014-11-25 06:50 |只看该作者
原先只知道PREEMPT_ACTIVE是内核在抢占的时候会置位,但没有追过是那几个函数置位的,现在领教了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP