Linux内核启动过程学习

    过完年来,敲着键盘都有点生疏了,很多东西都有点忘记了,还有刚上班还有点不习惯,状态还没有转换过来,没有办法趁这个机会就复习一下Linux的启动过程吧。


    

asmlinkage void __init start_kernel(void)
{
    char * command_line;
    extern const struct kernel_param __start___param[], __stop___param[];

    /*
      这两个变量为地址指针,指向内核启动参数处理相关结构体段在内存中的位置(虚拟地址)。 
    声明传入参数的外部参数对于ARM平台,位于 include\asm-generic\vmlinux.lds.h
    */  
 
    smp_setup_processor_id();

    /*
     * Need to run as early as possible, to initialize the
     * lockdep hash:
     */
    lockdep_init();  /* 初始化 lockdep hash 表,用于检测内核互斥机制所存在的潜在死锁问题 */
    debug_objects_early_init();//对调试对象进行早期的初始化,其实就是HASH锁和静态对象池进行初始化 

    /*
     * Set up the the initial canary ASAP:
     */
    boot_init_stack_canary();//防止栈溢出攻击的保护

    cgroup_init_early();

    local_irq_disable();//关闭CPU当前中断
    early_boot_irqs_off();
    early_init_irq_lock_class();

/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
    tick_init(); //初始化tick功能,注册clockevent框架通知
    boot_cpu_init();//软件上设置CPU为活跃、在线、存在、可用状态,注:多核CPU为第一个核
    page_address_init();//初始化页地址,然而S3C6410中内存不可能使用高端地址即大于896M的地址空间,故这个函数为空。
    printk(KERN_NOTICE "%s", linux_banner);
    setup_arch(&command_line);
/*每种体系结构都有自己的setup_arch()函数,是体系结构相关的,具体编译哪个 体系结构的setup_arch()函数,由源码树顶层目录下的Makefile中的ARCH变量决定  
  例如: ARM体系结构的。
  SUBARCH :=arm   
  ARCH        ?= $(SUBARCH)
该函数在所在的路劲为 /arm/kernel/setup.c,其中那个command_line就是有bootloader传过来的!这个函数是个重量级的函数,大家不可忽视!该函数完成体系结构相关的初始化,内核移植的过程一般也就到此函数为止了,其余的就只是一些相关的外设驱动。*/

    mm_init_owner(&init_mm, &init_task);
    setup_command_line(command_line); /*1.对cmdline进行备份和保存:保存未改变的comand_line到字符数组static_command_line[] 中。保存  boot_command_line到字符数组saved_command_line[]中 
    */  
    setup_nr_cpu_ids();
    setup_per_cpu_areas(); /* 每个CPU分配pre-cpu结构内存并复制.data.percpu段的数据 */  
           /*如果没有定义CONFIG_SMP宏,则这个函数为空函数。如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间。 
        */  
        /*下面三段1.针对SMP处理器的内存初始化函数,如果不是SMP系统则都为空函数。 (arm为空)  
        2.他们的目的是给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间并为boot CPU设置一些数据。  
        3.在SMP系统中,在引导过程中使用的CPU称为boot CPU*/ 
    smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */

    build_all_zonelists(NULL);//  建立系统内存页区(zone)链表 
    page_alloc_init();//初始化内存页

    printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
    parse_early_param();//  解析早期格式的内核参数  
        /*函数对Linux启动命令行参数进行在分析和处理, 
        当不能够识别前面的命令时,所调用的函数。*/ 
    parse_args("Booting kernel", static_command_line, __start___param,
           __stop___param - __start___param,
           &unknown_bootoption);
    /*
     * These use large bootmem allocations and must precede
     * kmem_cache_init()
     */
    pidhash_init(); /*初始化hash表,以便于从进程的PID获得对应的进程描述指针,按照开发办上的物理内存初始化pid hash表 
        */ 
    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 (!irqs_disabled()) {
        printk(KERN_WARNING "start_kernel(): bug: interrupts were "
                "enabled *very* early, fixing it\n");
        local_irq_disable();
    }
    rcu_init();//互斥访问初始化
    radix_tree_init();//创建idr缓冲区
    /* init some links before init_ISA_irqs() */
    early_irq_init();//早起中断初始化,实质为空
    init_IRQ();/*
该函数主要对内核数据结构irq_desc[]和fiqdesc[]数据结构进行了初始化,同时对fiq的堆栈指针进行了设置。另外对DMA进行了初始化,源码如下
其中的那个init_arch_irq是在什么地方定义的呢?
其实在上面的setup_arch()函数中已经进行定义了:arch/arm/kernel/setup.c:      init_arch_irq = mdesc->init_irq;,而这个mdesc->init_irq则是在
arch\arm\mach-s3c6410\mach-mini6410.c中定义


*/
    prio_tree_init();
    init_timers();//定时器初始化
    hrtimers_init();
    softirq_init();
    timekeeping_init();
    time_init();
    profile_init();/* 对内核的  profile 功能
进行初始化,这是一个内核调式工具,通过这个可以发现内核在内核态的什么地方花费时间最多,
即发现内核的“hot spot”——执行最频繁的内核代码。
这个  profile_init()  分配一段内存,用来存放  profile 信息,每条指令都有一个计数器。 */
    if (!irqs_disabled())
        printk(KERN_CRIT "start_kernel(): bug: interrupts were "
                 "enabled early\n");
    early_boot_irqs_on();
    local_irq_enable();

    /* Interrupts are enabled now so all GFP allocations are safe. */
    gfp_allowed_mask = __GFP_BITS_MASK;

    kmem_cache_init_late();//内存管理的slab机制

    /*
     * 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();//用来对控制台初始化,这个函数执行完成后,串口才可以看到printk打印的调试信息。这里还有个问题就是,在console_ini()之前用printk()也能打印出来,这个因为,如果没有注册console,
printk只是将信息放到缓冲区中,console_init->con_init->register_console,在register_console时输
出缓冲区中暂存的信息。 
    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) {
        printk(KERN_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();
    enable_debug_pagealloc();
    kmemleak_init();
    debug_objects_mem_init();
    idr_init_cache();
    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_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();

    ftrace_init();

    /* Do the rest non-__init'ed, we're now alive */
    rest_init();//这个函数留个接下来的文章学习。。。。
}


本文很多部分来源于以下文章,感谢原作者的无私分享。
linux内核启动第二阶段分析 http://blog.csdn.net/boarmy/article/details/8652338
理解start_kernel中函数语句的作用 http://blog.csdn.net/u013012494/article/details/39229843

 
  

你可能感兴趣的:(linux,内核,kernel,linux,s3c6410,源码)