linux 内核 之 start_kernel()
来源:互联网 发布:java三大设计模式 编辑:程序博客网 时间:2024/05/22 01:31
//Linux内核由 start_kernel开始,到用户进程init 结束 调用了一系列的初始化函数 对所有的内核组件//进行初始化 其中 start_kernel rest_init kernel_init init_post 等4个初始化函数 构成了整个//初始化的主线asmlinkage void __init start_kernel(void){ char * command_line; extern struct kernel_param __start___param[], __stop___param[]; //设置对称多处理器的id,返回启动的cpu的id //当只有一个cpu时什么也不做 smp_setup_processor_id(); /* * Need to run as early as possible, to initialize the * lockdep hash: */ //这个函数的主要作用是对调用的栈的调试功能进一步初始化,在arm系统里是空函数 unwind_init(); // lockdep_init(); //关闭当前cpu的所有中断响应。在arm里主要是对cpsr寄存器进行操作 local_irq_disable(); //这个函数主要作用是标记内核还是在早期初始化代码阶段,并且中断在关闭状态 //如果有任何中断打开或是请求中断的事情出现,都会提出警告,以便跟踪代码 //错误情况。早期代码结束之后,会调用early_boot_irqs_on()来设置这个标志为真 early_boot_irqs_off(); //这个函数主要作用是对中断请求描述符进行锁的早期初始化。在arm里,这个函数 //没有任何代码 early_init_irq_lock_class(); /* * Interrupts are still disabled. Do necessary setups, then * enable them */ //初始化大内核锁。对于只能有一个cpu运行的代码,使用这样的一个锁,可以使一个 //cpu调用内核,而其他的cpu无法调用内核;并且,可以运行内核代码的这个cpu同时 //可以递归的调用内核去运行。 lock_kernel(); //初始化时钟事件管理器的回调函数,比如当时钟设备添加时处理。内核里定义俄罗时钟 //事件管理器,主要用来管理所有需要周期性的执行任务的设备 tick_init(); //设置当前引导系统的cpu在物理上存在,在逻辑上可以使用,并且初始化准备好。 //cpu_present_map位图 表示有多少个cpu,每一位表示一个cpu的存在。如果单cpu,则第 //0位设置为1。由于系统中可能有的cpu未初始化或者其他原因导致的不能使用,内核使用 //cpu_online_map 位图 表示可以运行内核代码和接受中断处理的cpu。由于系统中不一定 //所有cpu都参与工作,因此系统中引入cpu_possible_map位图表示最多可以使用的cpu //本函数就是以次设置这三个位图标志 boot_cpu_init(); //初始化高端内存映射表。在32位的系统里,最多能访问的内存为4G,其中3G空间给应用 //程序,内核只占用1G空间。而实际上,比这个还要小,因为其中的一部分(128M)空间 //用来映射高端内存 page_address_init(); //在终端上输出显示注意信息 KERN_NOTICE宏的定义为 "<5>" //在终端杀姑娘显示版本信息、编译的电脑用户名、编译起版本、编译时间 printk(KERN_NOTICE); printk(linux_banner); //对内核架构进行初始化。 //再次获取cpu类型和系统架构,分析引导程序传入的命令行参数,进行页面内存初始化 //处理器初始化,中断早期初始化等等 setup_arch(&command_line); //保存命令行,以便后面的使用 setup_command_line(command_line); //对调用栈的调试功能进一步的初始化,在arm里是空函数 unwind_setup(); //设置smp体系每个cpu使用的内存空间,同时拷贝初始化段里数据 setup_per_cpu_areas(); //为smp系统里引导cpu进行准备工作。在arm里是空函数 smp_prepare_boot_cpu();/* arch-specific boot-cpu hooks */ /* * 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. */ //调度器的初始化,比如分配调度器占用的内存,初始化人物队列,设置当前任务的空线程 //当前人物的调度策略为CFS(Completely Fair 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(); //对异常进行初始化,在arm里是空函数 trap_init(); //初始化直接读拷贝更新的锁机制。rcu主要提供在读取数据机会多、更新比较少的场合 //这样减少读取数据锁的性能下降的问题 rcu_init(); //初始化中断相关的工作,主要初始化中断描述数组,然后调用每个cpu架构的中断初始化 init_IRQ(); //初始化进程id的hash表,这样可以提供通过pid进行高效访问进程结构的信息。Linux里 //共有4种类型的pid,因此有4种hash表相对应 pidhash_init(); //初始化引导cpu的时钟相关的数据结构,注册时钟的回调函数,当时钟到达时可以回调 //时钟处理函数,最后初始化时钟软件中断处理 init_timers(); //初始化高精度定时器,并设置回调函数 hrtimers_init(); //初始化软中断 。软中断与硬件中断在处理时的区别是:软件中断是使用线程来监视中断号 //而硬件中断是使用cpu硬件来监视中断 softirq_init(); //初始化系统时钟计时,并且初始化内核里与时钟计时相关的变量 timekeeping_init(); //初始化系统时钟 time_init(); //分配内核性能统计保存的内存,以便统计的性能变量可以保存到这里 profile_init(); if (!irqs_disabled()) printk("start_kernel(): bug: interrupts were enabled early\n"); //设置内核还在早期初始化阶段的标志,为方便调试而做 early_boot_irqs_on(); //打开本地cpu中断,即允许本cpu处理中断。如果有多个cpu,那么此处并不处理别的cpu 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: */ //测试锁的api是否正常,进行自我测试 locking_selftest();#ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && initrd_start < min_low_pfn << PAGE_SHIFT) { printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " "disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT); initrd_start = 0; }#endif //初始化虚拟文件系统的缓存 vfs_caches_init_early(); //初始化cpu集合的内存分配变量 以便任务的内存分配于cpu集合进行比较,如果 //两者不一致,就更新任务的内存允许分配的内存大小 cpuset_init_early(); //标记哪些内存可用 mem_init(); //初始化内核内存的缓存,当初始化完成之后,就可以使用通用内存缓存了 kmem_cache_init(); //创建每个cpu的告诉缓存集合数组。因为每个cpu不定时需要使用一些页面内存和 //释放页面内存,为了提高效率,就预先创建了一些内存页面作为每个cpu页面集合 setup_per_cpu_pageset(); //初始化numa(NonUniform Memory Access Achitecture)的内存访问策略。 //主要用于提高多个cpu访问内存的速度。因为多个cpu访问同一个节点的内存速度 //远比访问多个节点的速度来的快 numa_policy_init(); //运行时钟相关后期的初始化功能 if (late_time_init) late_time_init(); //计算cpu需要校准的时间(相对于引导cpu的执行时间)。对于非引导cpu才有用 calibrate_delay(); //初始化进程位图 一般情况下使用一页来表示所有进程占用情况 pidmap_init(); //页表缓存初始化 pgtable_cache_init(); //初始化优先搜索树 prio_tree_init(); //初始化反向映射的匿名内存,提供方向查找内存的结构指针位置,快速回首内存 anon_vma_init();#ifdef CONFIG_X86 if (efi_enabled) efi_enter_virtual_mode();#endif //根据当前牡蛎内存计算出可以创建的进程(线程)的数量,并进行进程环境初始化 fork_init(num_physpages); //进程缓存初始化 proc_caches_init(); //初始化文件系统缓冲区,并计算最大可以使用的文件缓存 buffer_init(); unnamed_dev_init(); //初始化安全键管理列表和结构 key_init(); //初始化安全管理框架,以便提供访问文件/登录等权限 security_init(); //初始化虚拟文件系统缓存 vfs_caches_init(num_physpages); //初始化radix树,radix树基于二进制键的查找树 radix_tree_init(); //初始化信号队列缓存 signals_init(); /* rootfs populating might need page-writeback */ page_writeback_init();#ifdef CONFIG_PROC_FS //初始化proc文件系统 主要提供内核与用户交互的平台 //方便用户实时查看进程的信息 proc_root_init(); #endif //初始化CPUSET ,CPUSET主要为控制组提供CPU和内存节点 //管理的结构 cpuset_init(); //初始化任务状态相关的缓存、队列和信号量。任务状态主要向用户 //提供任务的状态信息 taskstats_init_early(); //初始化每个任务延时计数。当一个任务等待cpu、或者等待I/O同步时 //都需要计算等待时间 delayacct_init(); //检查cpu配置、fpu等是否非法使用不具备的功能 check_bugs(); //初始化acpi电源管理。 acpi_early_init(); /* before LAPIC and SMP init */ /* Do the rest non-__init'ed, we're now alive */ //后继初始化,主要是创建内核线程init,并运行。 rest_init();}