Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1078849
  • 博文数量: 277
  • 博客积分: 8313
  • 博客等级: 中将
  • 技术积分: 2976
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-22 11:25
文章分类

全部博文(277)

文章存档

2013年(17)

2012年(66)

2011年(104)

2010年(90)

我的朋友

分类:

2012-12-08 22:49:15

原文地址:start_kernel分析 作者:梦来是缘

   在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段。
   下面就顺这代码逐个函数的解释,但是这里并不会过于深入每个函数,因为这样就会只见树木,不见森林。分析代码首先要从构架上宏观地理解,然后再去考虑细节问题

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

点击(此处)折叠或打开

  1. 声明结构标签为kernel_param 的2个变量__start___param[], __stop___param[];
  2. 这里关键是这两个变量的地址是如何确定的。
  3. 这两个变量为地址指针,指向内核启动参数处理相关结构体段在内存中的位置(虚拟地址)。
  4. 对于ARM平台,位于 kernel\include\asm-generic\vmlinux.lds.h
  5. kernel_param结构体的定义是:(标签为kernel_param的结构体

  6. 点击(此处)折叠或打开

    1. struct kernel_param {
    2.     const char *name;
    3.     unsigned int perm;
    4.     param_set_fn set;
    5.     param_get_fn get;
    6.     union {
    7.         void *arg;
    8.         const struct kparam_string *str;
    9.         const struct kparam_array *arr;
    10.     };


     

 smp_setup_processor_id();

点击(此处)折叠或打开

  1. 这个函数是针对SMP处理器的,经查阅资料,其作用是获取当前CPU的的硬件ID。
  2. 如果不是多处理器构架,在其他文件中就不会定义这个函数,此时使用本文件定义的弱引用函数:
  3. void __init __weak smp_setup_processor_id(void)
  4. {
  5. }

 /*
  *必须尽早运行这个程序, 作用是初始化

  * lockdep 模块的hash表:

  */
 lockdep_init();

点击(此处)折叠或打开

  1. lockdep是一个内核调试模块,用来检查内核互斥机制(尤其是自旋锁)潜在的死锁问题。
  2. 由于自旋锁以查询方式等待,不释放处理器,比一般互斥机制更容易死锁,故引入lockdep检查以下几种可能的死锁情况:
  3. 1.同一个进程递归地加锁同一把锁;
  4. 2.一把锁既在中断(或中断下半部)使能的情况下执行过加锁操作, 又在中断(或中断下半部)里执行过加锁操作。这样该锁有可能在锁定时由于中断发生又试图在同一处理器上加锁;
  5. 3.加锁后导致依赖图产生成闭环,这是典型的死锁现象。

 debug_objects_early_init();

点击(此处)折叠或打开

  1. 在启动早期初始化hash buckets 和链接静态的 pool objects对象到 poll 列表. 在这个调用完成后 object tracker 已经开始完全运作了.
 /*
  * Set up the the initial canary ASAP: 初始化栈canary值:
  */
 boot_init_stack_canary();

点击(此处)折叠或打开

  1. canary值的是用于防止栈溢出攻击的堆栈的保护字 。
  2. 参考资料: GCC 中的编译器堆栈保护技术
 cgroup_init_early();

点击(此处)折叠或打开

  1. cgroup: 它的全称为control group.即一组进程的行为控制.
  2. 该函数主要是做数据结构和其中链表的初始化
  3. 参考资料: Linux cgroup机制分析之框架分析
 local_irq_disable();             //关闭系统总中断(底层调用汇编指令)

 early_boot_irqs_off();           //设置系统中断的关闭标志(bool全局变量)

 early_init_irq_lock_class();

/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them       中断依然被禁用。必要设置后 再使能它
 */
 lock_kernel();
 tick_init();

点击(此处)折叠或打开

  1. 初始化内核时钟系统
  2. -->clockevents_register_notifier(&tick_notifier)
  3. 往内核通知链中注册内核时钟时间的通知函数
  4. 参考资料:《Linux 时钟处理机制》 《Linux 时钟管理》


 boot_cpu_init();

点击(此处)折叠或打开

  1. 激活当前CPU(在内核全局变量中将当前CPU的状态设为激活状态)
  2. 参考资料:《激活第一个CPU》


 page_address_init();        //高端内存相关,未定义高端内存的话为空函数
 printk(KERN_NOTICE "%s", linux_banner);

//打印内核版本信息,也就是平时我们在内核启动时在串口中看到的:Linux version 2.6.30.4mingjie (root@EmbedSky) (gcc version 4.3.3 (Sourcery G  Lite 2009q1176) ) 

#2 Sun Oct 21 08:54:39 CST 2012
 setup_arch(&command_line);

点击(此处)折叠或打开

  1. 内核构架相关初始化函数,可以说是非常重要的一个初始化步骤。其中包含了处理器相关参数的初始化、内核启动参数(tagged list)的获取和前期处理、内存子系统的早期的初始化(bootmem分配器)。
  2. 对于ARM构架来说,这个函数位于:arch/arm/kernel/setup.c
  3. 以后会详细分析这个函数。


 mm_init_owner(&init_mm, &init_task);

点击(此处)折叠或打开

  1. 初始化代表内核本身内存使用的管理结构体init_mm。
  2. ps:每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核的mm_struct,其中:
  3. 设置成员变量* mmap指向自己,意味着内核只有一个内存管理结构;
  4. 设置* pgd=swapper_pg_dir,swapper_pg_dir是内核的页目录(在arm体系结构有16k, 所以init_mm定义了整个kernel的内存空间)。
  5. 这些内容涉及到内存管理子系统,以后再仔细分析。


 setup_command_line(command_line);

点击(此处)折叠或打开

  1. 对cmdline进行备份和保存:
  2. /* 为处理的command line备份 (例如eg. 用于 /proc) */
  3. char *saved_command_line;
  4. /* 用于参数处理的command line */
  5. static char *static_command_line;


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

点击(此处)折叠或打开

  1. 针对SMP处理器的内存初始化函数,如果不是SMP系统则都为空函数。 (arm为空)
  2. 他们的目的是给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间并为boot CPU设置一些数据。
  3. 在SMP系统中,在引导过程中使用的CPU称为boot CPU


 /*
  * 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();
 build_all_zonelists();
 page_alloc_init();
 printk(KERN_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,
     &unknown_bootoption);
 if (!irqs_disabled()) {
  printk(KERN_WARNING "start_kernel(): bug: interrupts were "
    "enabled *very* early, fixing it\n");
  local_irq_disable();
 }
 sort_main_extable();
 trap_init();
 rcu_init();
 /* init some links before init_ISA_irqs() */
 early_irq_init();
 init_IRQ();
 pidhash_init();
 init_timers();
 hrtimers_init();
 softirq_init();
 timekeeping_init();
 time_init();
 sched_clock_init();
 profile_init();
 if (!irqs_disabled())
  printk(KERN_CRIT "start_kernel(): bug: interrupts were "
     "enabled early\n");
 early_boot_irqs_on();
 local_irq_enable();

 /*
  * 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) {
  printk(KERN_CRIT "initrd overwritten (0xlx < 0xlx) - "
      "disabling it.\n",
      page_to_pfn(virt_to_page((void *)initrd_start)),
      min_low_pfn);
  initrd_start = 0;
 }
#endif
 vmalloc_init();
 vfs_caches_init_early();
 cpuset_init_early();
 page_cgroup_init();
 mem_init();
 enable_debug_pagealloc();
 cpu_hotplug_init();
 kmem_cache_init();
 kmemtrace_init();
 debug_objects_mem_init();
 idr_init_cache();
 setup_per_cpu_pageset();
 numa_policy_init();
 if (late_time_init)
  late_time_init();
 calibrate_delay();       // 计算BogoMIPS值,他是衡量一个CPU性能的标志。
 pidmap_init();        //PID分配映射初始化。
 pgtable_cache_init();
 prio_tree_init();
 anon_vma_init();  
  //  匿名虚拟内存域( anonymous VMA)初始化
#ifdef CONFIG_X86
 if (efi_enabled)
  efi_enter_virtual_mode();
#endif
 thread_info_cache_init();//获取thread_info缓存空间,大部分构架为空函数(包括ARM)
 cred_init();          //任务信用系统初始化。详见:Documentation/credentials.txt
 fork_init(num_physpages);//进程创建机制初始化。为内核"task_struct"分配空间,计算最大任务数。
 proc_caches_init();      
//初始化进程创建机制所需的其他数据结构,为其申请空间。
 buffer_init();            //缓存系统初始化,创建缓存头空间,并检查其大小限时。
 key_init();               //内核密钥管理系统初始化
 security_init();          //内核安全框架初始化
 vfs_caches_init(num_physpages
);//虚拟文件系统(VFS)缓存初始化
 radix_tree_init();
 signals_init();          //信号管理系统初始化
 /* rootfs populating might need page-writeback */
 page_writeback_init();  //页写回机制初始化
#ifdef CONFIG_PROC_FS
 proc_root_init();   //proc文件系统初始化
#endif
 cgroup_init();  //control group正式初始化
 cpuset_init(); //CPUSET初始化。 参考资料:

 taskstats_init_early();  //任务状态早期初始化函数:为结构体获取高速缓存,并初始化互斥机制。

 delayacct_init();    //任务延迟初始化

 check_bugs();       //检查CPU BUG的函数,通过软件规避BUG
 
 acpi_early_init(); /* before LAPIC and SMP init  在 LAPIC  SMP 前初始化 */
  1. ACPI早期初始化函数。 ACPI - Advanced Configuration and Power Interface高级配置及电源接口
 ftrace_init();        //功能跟踪调试机制初始化,ftrace 是 function trace 的简称
 /* Do the rest non-__init'ed, we're now alive 所剩下的非-__init的初始化, 内核现在已经启了 */

 rest_init();

          // 虽然从名字上来说是剩余的初始化。但是这个函数中的初始化包含了很多的内容
}
在看完上面的代码之后,你会发现内容很多。但是归纳起来,我认为需要注意的有以下几点:

  1. 内核启动参数的获取和处理
  2. setup_arch(&command_line);函数
  3. 内存管理的初始化(从bootmem到slab)
  4. rest_init();函数

其他的部分都是对内核各个组件的数据结构申请内存,并初始化。
阅读(1998) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~