分享

关于rest_init()的函数分析

 langhuayipian 2010-10-26
今天看了下IMX28驱动初始化部分时有些迷惑,跟踪了下代码,现把调用过程记录下来
内核初始化后跳转到start_kernel()
,start_kernel()最后调用rest_init();驱动中初始化内容在rest_init()中调用
init/main.c中
static noinline void __init_refok rest_init(void)
    __releases(kernel_lock)
{
    int pid;

    kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);  //加载
    numa_default_policy();
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
    kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
    unlock_kernel();

    /*
     * The boot idle thread must execute schedule()
     * at least once to get things moving:
     */
    init_idle_bootup_task(current);
    rcu_scheduler_starting();
    preempt_enable_no_resched();
    schedule();
    preempt_disable();

    /* Call into cpu_idle with preempt disabled */
    cpu_idle();
}

kernel_init在
static int __init kernel_init(void * unused)
{
    lock_kernel();

    /*
     * init can allocate pages on any node
     */
    set_mems_allowed(node_possible_map);
    /*
     * init can run on any cpu.
     */
    set_cpus_allowed_ptr(current, cpu_all_mask);
    /*
     * Tell the world that we're going to be the grim
     * reaper of innocent orphaned children.
     *
     * We don't want people to have to make incorrect
     * assumptions about where in the task array this
     * can be found.
     */
    init_pid_ns.child_reaper = current;

    cad_pid = task_pid(current);

    smp_prepare_cpus(setup_max_cpus);

    do_pre_smp_initcalls();
    start_boot_trace();

    smp_init();
    sched_init_smp();

    do_basic_setup();  //在此基本设置

    /*
     * check if there is an early userspace init.  If yes, let it do all
     * the work
     */

    if (!ramdisk_execute_command)
        ramdisk_execute_command = "/init";

    if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
        ramdisk_execute_command = NULL;
        prepare_namespace();
    }

    /*
     * Ok, we have completed the initial bootup, and
     * we're essentially up and running. Get rid of the
     * initmem segments and start the user-mode stuff..
     */

    init_post();
    return 0;
}

do_basic_setup();
static void __init do_basic_setup(void)
{
    rcu_init_sched(); /* needed by module_init stage. */
    init_workqueues();
    cpuset_init_smp();
    usermodehelper_init();
    driver_init();//驱动初始化
    init_irq_proc();
    do_ctors();
    do_initcalls();  //调用
}

关于调用顺序知识
在2.6内核中,initcall.init区段又分成7个子区段,分别是
.initcall1.init
.initcall2.init
.initcall3.init
.initcall4.init
.initcall5.init
.initcall6.init
.initcall7.init

当需要把函数fn放到.initcall1.init区段时,只要声明
core_initcall(fn);
即可。

其他的各个区段的定义方法分别是:
core_initcall(fn) --->.initcall1.init
postcore_initcall(fn) --->.initcall2.init
arch_initcall(fn) --->.initcall3.init
subsys_initcall(fn) --->.initcall4.init
fs_initcall(fn) --->.initcall5.init
device_initcall(fn) --->.initcall6.init
late_initcall(fn) --->.initcall7.init

而与2.4兼容的initcall(fn)则等价于device_initcall(fn)。

各个子区段之间的顺序是确定的,即先调用.initcall1.init中的函数指针
再调用.initcall2.init中的函数指针,等等。
而在每个子区段中的函数指针的顺序是和链接顺序相关的,是不确定的。

在内核中,不同的init函数被放在不同的子区段中,因此也就决定了它们的调用顺序。
这样也就解决了一些init函数之间必须保证一定的调用顺序的问题。
********************************************
MACHINE_START(MX28EVK, "Freescale MX28EVK board")
    .phys_io    = 0x80000000,
    .io_pg_offst    = ((0xf0000000) >> 18) & 0xfffc,
    .boot_params    = 0x40000100,
    .fixup        = fixup_board,
    .map_io        = mx28_map_io,
    .init_irq    = mx28_irq_init,
    .init_machine    = mx28evk_init_machine,
    .timer        = &mx28_timer.timer,
MACHINE_END
由上发现,MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。

各个成员函数在不同时期被调用:
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
2. init_irq在start_kernel() --> init_IRQ() --> init_arch_irq() 被调用
3. map_io 在 setup_arch() --> paging_init() --> devicemaps_init()被调用
其他主要都在 setup_arch() 中用到。



*************************************
static void __init do_initcalls(void)
{
    initcall_t *call;

    for (call = __early_initcall_end; call < __initcall_end; call++)
        do_one_initcall(*call);

    /* Make sure there is no pending stuff from the initcall sequence */
    flush_scheduled_work();
}
这个__initcall_start是在文件        arch/xxx/kernel/vmlinux.lds.S (其中的xxx 是你的体系结构的名称,例如i386)
这个文件是内核ld的时候使用的.其中定义了各个sectioin,看看就明白了。
在这个文件中有个.initcall.init, 代码如下:













    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多