免费注册 查看新帖 |

Chinaunix

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

[内核入门] 对kernel_init()里的wait_for_completion(&kthreadd_done)不理解 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-01-19 20:32 |只看该作者 |倒序浏览
     最近学习linux内核源代码,发现对kernel_init()里的wait_for_completion(&kthreadd_done)不是很理解,请大侠给解释一下。

    先说说我的疑惑:

    在start_kernel()函数里先调用preempt_disable()禁止内核抢占,并在start_kernel的最后调用了rest_init();

    与rest_init函数相关的代码如下:

    static __initdata DECLARE_COMPLETION(kthreadd_done);

     static noinline void __init_refok rest_init(void)
     {
        int pid;

        rcu_scheduler_starting();
        /*
         * We need to spawn init first so that it obtains pid 1, however
         * the init task will end up wanting to create kthreads, which, if
         * we schedule it before we create kthreadd, will OOPS.
         */
1:        kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
        numa_default_policy();
2:        pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
        rcu_read_lock();
        kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
        rcu_read_unlock();
3:        complete(&kthreadd_done);

        /*
         * The boot idle thread must execute schedule()
         * at least once to get things moving:
         */
        init_idle_bootup_task(current);
4:        schedule_preempt_disabled();
        /* Call into cpu_idle with preempt disabled */
        cpu_idle();
     }
     rest_init里利用kernel_thread创建了kernel_init和kthreadd两个线程。从rest_init里的注释可看到kernel_init线程不能在kthreadd线程创建完成前被调度,否则会产生oops.(注:标号1/2/3/4是我加上去的,方便后文描述)

      与kernel_init函数相关的内容:
    static int __init kernel_init(void * unused)
     {
        /*
         * Wait until kthreadd is all set-up.
         */
        wait_for_completion(&kthreadd_done);
                         .
                         .
                         .
     }
    在kernel_init函数的入口处就开始等待kthreadd_done完成量,也就是说如果kernel_init先于标号3前被调度的话,必须等kthreadd_done完成后才能继续往下走。可问题来了,start_kernel里是先关抢占后才调用rest_init函数创建kernel_init线程的,也就是说在标号4处重新开抢占前,kernel_init线程不可能先于idle线程运行,即kernel_init线程肯定是在kthreadd线程创建完成后才被调度执行,如果是这样的话,此处的完成量就没有起到作用。那为什么还在kernel_init的入口处弄这么一完成量呢?
   
    也许是我的理解有误,请大侠们给排一下疑。
   

论坛徽章:
0
2 [报告]
发表于 2013-01-20 18:16 |只看该作者
咋的没人给解释一下呢,自己顶一下,安慰一下自己的处女帖!!

论坛徽章:
0
3 [报告]
发表于 2013-01-21 09:57 来自手机 |只看该作者
楼主,可能是由于多核的原因。

论坛徽章:
0
4 [报告]
发表于 2013-01-21 15:31 来自手机 |只看该作者
这个时候多核里的其它核都还没有处始化呢,所以不可能被别的核调度,不过还是谢谢你的回复,总算消灭了零回复

论坛徽章:
0
5 [报告]
发表于 2013-01-21 15:45 |只看该作者
https://lkml.org/lkml/2010/6/23/281

There is a bug in rest_init function. The problem is that kernel_init
thread starts before initialization of kthreadd_task when
CONFIG_PREEMPT_VOLUNTARY is enabled.

论坛徽章:
0
6 [报告]
发表于 2013-01-21 16:23 来自手机 |只看该作者
5#楼的eexplorer兄弟,这段e文我理解没错的话,是说,如果内核配置成抢占模式,kernel_init会先于kthreadd运行,对吧?可这并不能解决我的疑问啊……

论坛徽章:
0
7 [报告]
发表于 2013-01-21 16:48 来自手机 |只看该作者
上面少打了几个字,”如果内核配置成抢占模式,kernel_init会先于kthreadd初始化完成前运行“。在rest_init里即使没有这个完成量,因为在start_kernel里已经preemt_disable了,所以也能保证kernel_init在kthreadd初始化完成后才运行

论坛徽章:
0
8 [报告]
发表于 2013-01-21 21:57 |只看该作者
7楼那后面的地方弄错了,应该是”如果内核配置成抢占模式,kernel_init会先于kthreadd初始化完成前运行“。在rest_init里即使没有这个完成量,因为在start_kernel里已经preemt_disable了,所以至少能保证kernel_init在标号3complete(&kthreadd_done)完成后才运行,这样讲来,wait_for_completion(&kthreadd_done)就是没有起作用

论坛徽章:
0
9 [报告]
发表于 2013-01-21 23:37 |只看该作者
你所说的抢占模式是CONFIG_PREEMPT, 而CONFIG_PREEMPT_VOLUNTARY是另外一个config option.

看一下include/linux/kernel.h的code,CONFIG_PREEMPT_VOLUNTARY其实就是在might_sleep()里增加了__cond_sched()的调用点。这个patch是Ingo Molnar为了提高linux desktop的responsiveness,减少process的schedule latency而加入了。

引起这个bug的原因在https://lkml.org/lkml/2010/6/23/281也有描述,他是在一个非常慢的FPGA上跑kernel,这样有很大的可能性,当运行完
  1. kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
复制代码
进入kernel_thread创建kthreadd的时候,如果当前thread的时间片用完了,在kernel_thread()里有调用might_sleep()的地方,会将当前thread schedule出去,init thread就会先于kthreadd运行。

  1. numa_default_policy();
  2. pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
复制代码
__cond_thread()进行调度的时候并不会check preempt count,所以即使preemption disable,当前thread也会被调度出去。具体kernel_thread()哪里调用了might_sleep()我没仔细查,一个比较common的调用might_sleep()的点是在mutex_lock里。

论坛徽章:
0
10 [报告]
发表于 2013-01-23 22:44 |只看该作者
回复 9# eexplorer


    谢谢eexplorer兄的回复,根据你的提示,我又重新追踪了一下kernel_thread里的代码,确实如你所说,如果配置了CONFIG_PREEMT_VOLUNTARY,则会在do_fork里的copy_process()里引用might_sleep()进行进程调度。这次在阅读do_fork时启发颇多,等有续有时间写个博客讨论学习一下,再次谢过!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP