开发者社区> binarydady> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

start_kernel启动函数

简介:
+关注继续查看

start_kernel启动函数

机器上电后,会进行BIOS自检,之后是系统引导,内核加载,最后是初始化阶段。我们这里主要关心的是初始化阶段(start_kernel)的事情,而从上电到初始化之前的事情直接忽略了。

            start_kernel函数, 也是内核启动函数,位于init/main.c文件中,该函数中调用的函数都是一个大分支。

asmlinkage void __init start_kernel(void)

{

        char * command_line;

        extern const struct kernel_param __start___param[], __stop___param[];

 

        /* 

         * Need to run as early as possible, to initialize the

         * lockdep hash:

         */

        lockdep_init();

        smp_setup_processor_id();

        debug_objects_early_init();

 

        /* 

         * Set up the the initial canary ASAP:

         */

        boot_init_stack_canary();

 

        cgroup_init_early();

 

        local_irq_disable();

        early_boot_irqs_disabled = true;

 

/*

 * Interrupts are still disabled. Do necessary setups, then

 * enable them

 */

        boot_cpu_init();

        page_address_init();

        pr_notice("%s", linux_banner);

        setup_arch(&command_line);// 特定于体系结构的、内存管理的高层设置

        mm_init_owner(&init_mm, &init_task);

        mm_init_cpumask(&init_mm);

        setup_command_line(command_line);

        setup_nr_cpu_ids();

        setup_per_cpu_areas();

        smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */

 

        build_all_zonelists(NULL, NULL);

        page_alloc_init();

 

        pr_notice("Kernel command line: %s\n", boot_command_line);

        parse_early_param();

        parse_args("Booting kernel", static_command_line, __start___param,

                   __stop___param - __start___param,

                   -1, -1, &unknown_bootoption);

 

        jump_label_init();

 

        /*

         * These use large bootmem allocations and must precede

         * kmem_cache_init()

         */

        setup_log_buf(0);

        pidhash_init();

        vfs_caches_init_early();

        sort_main_extable();

        trap_init();

        mm_init();

 

        /*

         * Set up the scheduler prior starting any interrupts (such as the

         * timer interrupt). Full topology setup happens at smp_init()

         * time - but meanwhile we still have a functioning scheduler.

         */

        sched_init();//初始化调度器的数据结构,并创建运行队列

        /*

         * Disable preemption - early bootup scheduling is extremely

         * fragile until we cpu_idle() for the first time.

         */

        preempt_disable();

        if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n"))

                      local_irq_disable();

        idr_init_cache();

        perf_event_init();

        rcu_init();

        tick_nohz_init();

        radix_tree_init();

        /* init some links before init_ISA_irqs() */

        early_irq_init();

        init_IRQ();

        tick_init();

        init_timers();

        hrtimers_init();

        softirq_init();//注册用于普通和高优先级的tasklet的软中断队列

        timekeeping_init();

        time_init();//从硬件读取系统时间

        profile_init();

        call_function_init();

        WARN(!irqs_disabled(), "Interrupts were enabled early\n");

        early_boot_irqs_disabled = false;

        local_irq_enable();

 

        kmem_cache_init_late();

 

        /*

         * HACK ALERT! This is early. We're enabling the console before

         * we've done PCI setups etc, and console_init() must be aware of

         * this. But we do want output early, in case something goes wrong.

         */

        console_init();

        if (panic_later)

                panic(panic_later, panic_param);

 

        lockdep_info();

 

        /*

         * Need to run this when irqs are enabled, because it wants

         * to self-test [hard/soft]-irqs on/off lock inversion bugs

         * too:

         */

        locking_selftest();

 

#ifdef CONFIG_BLK_DEV_INITRD

        if (initrd_start && !initrd_below_start_ok &&

            page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {

                pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",

                    page_to_pfn(virt_to_page((void *)initrd_start)),

                    min_low_pfn);

                initrd_start = 0;

        }

#endif

        page_cgroup_init();

        debug_objects_mem_init();

        kmemleak_init();

        setup_per_cpu_pageset();

        numa_policy_init();

        if (late_time_init)

                late_time_init();

        sched_clock_init();

        calibrate_delay();

        pidmap_init();

        anon_vma_init();

#ifdef CONFIG_X86

        if (efi_enabled(EFI_RUNTIME_SERVICES))

                efi_enter_virtual_mode();

#endif

        thread_info_cache_init();

        cred_init();

        fork_init(totalram_pages);

        proc_caches_init();

        buffer_init();

        key_init();

        security_init();

        dbg_late_init();

        vfs_caches_init(totalram_pages);

        signals_init();

        /* rootfs populating might need page-writeback */

        page_writeback_init();

#ifdef CONFIG_PROC_FS

        proc_root_init();

#endif

        cgroup_init();

        cpuset_init();

        taskstats_init_early();

        delayacct_init();

 

        check_bugs();

 

        acpi_early_init(); /* before LAPIC and SMP init */

        sfi_init_late();

 

        if (efi_enabled(EFI_RUNTIME_SERVICES)) {

                efi_late_init();

                efi_free_boot_services();

        }

 

        ftrace_init();

 

        /* Do the rest non-__init'ed, we're now alive */

        rest_init();//rest_init();启动init进程。

}

            主要有如下几点:

l   特定于体系结构的设置

l   解释命令行参数

l   初始化数据结构和缓存

l   查找已知的系统错误

l   最后创建idle和init进程,调用do_basic_setup函数,进行驱动设置。

驱动程序和子系统的一般初始化工作是在do_basic_setup函数中开始的。

startup_kernel->rest_init->kernel_init->kernel_init_freeable->do_basic_setup();

static void __init do_basic_setup(void)

{

        cpuset_init_smp();

        shmem_init();

        driver_init();

        init_irq_proc();

        do_ctors();

        usermodehelper_enable();

        do_initcalls();

}  

            其中do_initcalls函数调用驱动程序相关的初始化函数。

1.1     initcal机制

我们需要先来看下系统的initcall机制。

linux初始化的过程中,内核采用了一种initcall的机制,它利用gcc的扩展功能以及ld的连接控制脚本实现了在内核初始化的过程中通过简单的循环就实现了相关驱动的初始化。

Linux提供了一个头文件“vmlinux.lds.h”,该文件定义了一些宏用于辅助写连接脚本,从其中可以看到最终会出现在连接脚本中的各个内存section以及它们的相对位置.

#define INIT_CALLS_LEVEL(level)                                         \

                VMLINUX_SYMBOL(__initcall##level##_start) = .;          \

                KEEP(*(.initcall##level##.init))                        \

                KEEP(*(.initcall##level##s.init))                       \

 

#define INIT_CALLS                                                      \

                VMLINUX_SYMBOL(__initcall_start) = .;                   \

                KEEP(*(.initcallearly.init))                            \

                INIT_CALLS_LEVEL(0)                                     \

                INIT_CALLS_LEVEL(1)                                     \

                INIT_CALLS_LEVEL(2)                                     \

                INIT_CALLS_LEVEL(3)                                     \

                INIT_CALLS_LEVEL(4)                                     \

                INIT_CALLS_LEVEL(5)                                     \

                INIT_CALLS_LEVEL(rootfs)                                \

                INIT_CALLS_LEVEL(6)                                     \

                INIT_CALLS_LEVEL(7)                                     \

                VMLINUX_SYMBOL(__initcall_end) = .;

do_initcalls是从特定的内存区域取出初始化函数的指针,然后来调用它的,定义的各个等级的初始化函数都应该被放在特定的内存区域,每个等级的初始化函数都被放在自己特定的初始化区域中。

static void __init do_initcalls(void)

{

        int level;

       

        for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)

                do_initcall_level(level);

内核中经常遇到初始化函数是这样定义的: static int __init init_func(); 与普通函数相比,定义中多了__init。

  __exit宏告知编译器,将函数放在".exit.text"这个区域中。__exitdata宏则告知编译器将数据放在".exit.data"这个区域中。

在内核初始化时,从__initcall_start到__initcall_end之间的initcall被一次执行。

  内核组建可以利用定义在include/linux/init.h中的__setup宏来注册关键字和相关联的处理函数,此外还有如下__init开头的宏。

#define __init          __section(.init.text) __cold  __latent_entropy __noinitretpoline                                 

#define __initdata      __section(.init.data)

#define __initconst     __section(.init.rodata)

#define __exitdata      __section(.exit.data)

#define __exit_call     __used __section(.exitcall.exit)  

            gcc在编译时会将被修饰的内容放到这些宏所代表的section.

            在文件include/linux/init.h中定义了初始化例程并定义其顺序或优先级:

#define pure_initcall(fn)               __define_initcall(fn, 0)

#define core_initcall(fn)               __define_initcall(fn, 1)

#define core_initcall_sync(fn)          __define_initcall(fn, 1s)

#define postcore_initcall(fn)           __define_initcall(fn, 2)

#define postcore_initcall_sync(fn)      __define_initcall(fn, 2s)

#define arch_initcall(fn)               __define_initcall(fn, 3)

#define arch_initcall_sync(fn)          __define_initcall(fn, 3s)

#define subsys_initcall(fn)             __define_initcall(fn, 4)

#define subsys_initcall_sync(fn)        __define_initcall(fn, 4s)

#define fs_initcall(fn)                 __define_initcall(fn, 5)

#define fs_initcall_sync(fn)            __define_initcall(fn, 5s)

#define rootfs_initcall(fn)             __define_initcall(fn, rootfs)

#define device_initcall(fn)             __define_initcall(fn, 6)

#define device_initcall_sync(fn)        __define_initcall(fn, 6s)

#define late_initcall(fn)               __define_initcall(fn, 7)

#define late_initcall_sync(fn)          __define_initcall(fn, 7s)

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
[console,log,时执行,页面节点树,组件实例]微信小程序中使用async/await语法的方法
  1、在微信小程序项目添加package.json文件或者直接npm init.   2.在package.json中添加regenerator包和版本   `"devDependencies": {   "regenerator":"0.13.3"}`   3.微信开发者工具-》工具-》npm构建
55 0
ld: warning: cannot find entry symbol _start; defaulting to 00000000080481d8
场景: 正常 经过  gcc -o loccheck  loccheck.c [root@luozhonghua 9]# ./loccheck in main(),pooh=2 and &pooh=0xbfcfa31c in main(),bah=5 and &bah=0xbfcfa318 ------------------------ in main(),p
4900 0
阿里云 DataV 产品简介
产品简介 相比于传统图表与数据仪表盘,如今的数据可视化致力于用更生动、友好的形式,即时呈现隐藏在瞬息万变且庞杂数据背后的业务洞察。无论在零售、物流、电力、水利、环保、还是交通领域,通过交互式实时数据可视化视屏墙来帮助业务人员发现、诊断业务问题,越来越成为大数据解决方案中不可或缺的一环。
5786 0
阿里云 DataV 产品简介
产品简介 相比于传统图表与数据仪表盘,如今的数据可视化致力于用更生动、友好的形式,即时呈现隐藏在瞬息万变且庞杂数据背后的业务洞察。无论在零售、物流、电力、水利、环保、还是交通领域,通过交互式实时数据可视化视屏墙来帮助业务人员发现、诊断业务问题,越来越成为大数据解决方案中不可或缺的一环。
4559 0
+关注
binarydady
深入底层,挖掘应用 Problem Shooter/Performance Analyzer
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载