首先是start_kernel中80个左右的函数:
smp_setup_processor_id //获取当前CPU的的硬件ID
lockdep_init //初始化同步与互斥环境,用来检查内核互斥机制(尤其是自旋锁)潜在的死锁问题
debug_objects_early_init //^^^^^^^^^^^^^^^^^^^
boot_init_stack_canary //canary值的是用于防止栈溢出攻击的堆栈的保护字
cgroup_init_early //Linux cgroup机制分析之框架分析---------------------
local_irq_disable //关闭系统总中断(底层调用汇编指令)======================================
early_boot_irqs_disabled = true //设置系统中断的关闭标志(bool全局变量)
lock_kernel
tick_init //初始化内核时钟系统
boot_cpu_init //激活当前CPU(在内核全局变量中将当前CPU的状态设为激活状态)
page_address_init //高端内存相关,未定义高端内存的话为空函数
setup_arch(&command_line); //--分页,完成boot_mem
mm_init_owner(&init_mm, &init_task); //初始化代表内核本身内存使用的管理结构体init_mm。
mm_init_cpumask(&init_mm); //
setup_command_line(command_line); //对cmdline进行备份和保存
setup_per_cpu_areas(); ///针对SMP处理器的内存初始化函数,如果不是SMP系统则都为空函
setup_nr_cpu_ids();
smp_prepare_boot_cpu(); //设置启动的CPU为在线状态.在多CPU架构下
build_all_zonelists //建立系统内存页区(zone)链表
page_alloc_init //内存页初始化
parse_early_param //解析参数
jump_label_init //
setup_log_buf(0); //
pidhash_init //进程Hash table的初始化
vfs_caches_init_early //虚拟文件系统的初始化$$$$$$$$$$$$$$$
sort_main_extable //对异常处理函数排序
trap_init //系统保留中断向量(异常、非屏蔽中断以及系统调用)
mm_init
sched_init //进程调度器初始化
preempt_disable //禁止内核抢占
idr_init_cache //idr初始化缓冲
perf_event_init //
rcu_init //初始化RCU(Read-Copy Update)机制
radix_tree_init //radix树的初始化,供页面查找
early_irq_init //中断向量的初始化
init_IRQ //完成其余中断向量的初始化
init_timers //初始化定时器
hrtimers_init //高精度时钟初始化
softirq_init //软中断初始化
timekeeping_init //共用时钟的初始化
time_init //初始化系统时钟
profile_init //对内核的profile(一个内核性能调式工具)功能进行初始化
call_function_init //
early_boot_irqs_disabled = false //
local_irq_enable //========================================================================
kmem_cache_init_late //
console_init //++++++++++++++++++++++++++++
lockdep_info //如果定义了CONFIG_LOCKDEP宏,则打印锁依赖信息,否则什么也不做
locking_selftest //
page_cgroup_init //----------------
debug_objects_mem_init //^^^^^^^^^^^^^
kmemleak_init //
setup_per_cpu_pageset //
numa_policy_init //
sched_clock_init //进程调度时钟初始化
calibrate_delay //校验延时函数的精确度
pidmap_init //进程号位图初始化,一般用一个page来只是所有的进程PID占用情况
anon_vma_init //匿名虚拟内存域( anonymous VMA)初始化
thread_info_cache_init ///获取thread_info缓存空间,大部分构架为空函数(包括ARM)
cred_init //任务信用系统初始化
fork_init(totalram_pages); //进程创建机制初始化。为内核"task_struct"分配空间,计算最大任务数
proc_caches_init //初始化进程创建机制所需的其他数据结构,为其申请空间
buffer_init //缓存系统初始化,创建缓存头空间,并检查其大小限时
key_init //内核密钥管理系统初始化
security_init //内核安全框架初始化
dbg_late_init //
vfs_caches_init(num_physpages);//虚拟文件系统(VFS)缓存初始化 $$$$$$$$$$$$
signals_init //信号管理系统初始化
page_writeback_init //页写回机制初始化,CPU在内存中开辟高速缓存
proc_root_init //proc文件系统初始化
cgroup_init //control group正式初始化----------
cpuset_init //CPUSET初始化
taskstats_init_early //进程状态初始化,实际上就是分配了一个存储线程状态的高速缓存
delayacct_init //任务延迟初始化
check_bugs //检查CPU BUG的函数,通过软件规避BUG
acpi_early_init //ACPI早期初始化函数
sfi_init_late //
ftrace_init //功能跟踪调试机制初始化,ftrace 是 function trace 的简称
rest_init //
再着重说下rest_init
reset_init函数
在start_kernel函数的最后调用了reset_init函数进行后续的初始化。
代码清单2 reset_init函数
438 static void noinline __init_refok rest_init(void)
439 __releases(kernel_lock)
440 {
441 int pid;
442
/* reset_init()函数最主要的历史使命就是启动内核线程kernel_init */
443 kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
444 numa_default_policy();
/* 启动内核线程kthreadd,运行kthread_create_list全局链表中的kthread */
445 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
446 kthreadd_task = find_task_by_pid(pid);
447 unlock_kernel();
448
449 /*
450 * The boot idle thread must execute schedule()
451 * at least once to get things moving:
452 */
/*
* 增加idle进程的need_resched标志,并且调用schedule释放CPU,
* 将其赋给更应该获取CPU的进程。
*/
453 init_idle_bootup_task(current);
454 preempt_enable_no_resched();
455 schedule();
456 preempt_disable();
457
458 /* Call into cpu_idle with preempt disabled */
/*
* 进入idle循环以消耗空闲的CPU时间片,该函数从不返回。然而,当有实际工作
* 要处理时,该函数就会被抢占。
*/
459 cpu_idle();
460 }
3 kernel_init函数
kernel_init函数将完成设备驱动程序的初始化,并调用init_post函数启动用户空间的init进程。
代码清单3 kernel_init函数
813 static int __init kernel_init(void * unused)
814 {
815 lock_kernel();
816 /*
817 * init can run on any cpu.
818 */
/* 修改进程的CPU亲和力 */
819 set_cpus_allowed(current, CPU_MASK_ALL);
820 /*
821 * Tell the world that we're going to be the grim
822 * reaper of innocent orphaned children.
823 *
824 * We don't want people to have to make incorrect
825 * assumptions about where in the task array this
826 * can be found.
827 */
/* 把当前进程设为接受其他孤儿进程的进程 */
828 init_pid_ns.child_reaper = current;
829
830 __set_special_pids(1, 1);
831 cad_pid = task_pid(current);
832
833 smp_prepare_cpus(max_cpus);
834
835 do_pre_smp_initcalls();
836
/* 激活SMP系统中其他CPU */
837 smp_init();
838 sched_init_smp();
839
840 cpuset_init_smp();
841
/*
* 此时与体系结构相关的部分已经初始化完成,现在开始调用do_basic_setup函数
* 初始化设备,完成外设及其驱动程序(直接编译进内核的模块)的加载和初始化
*/
842 do_basic_setup();
843
844 /*
845 * check if there is an early userspace init. If yes, let it do all
846 * the work
847 */
848
849 if (!ramdisk_execute_command)
850 ramdisk_execute_command = "/init";
851
852 if (sys_access((const char __user *)ramdisk_execute_command, 0) != 0) {
853 ramdisk_execute_command = NULL;
854 prepare_namespace();
855 }
856
857 /*
858 * Ok, we have completed the initial bootup, and
859 * we're essentially up and running. Get rid of the
860 * initmem segments and start the user-mode stuff.
861 */
862 init_post();
863 return 0;
864 }
4 init_post函数
到init_post函数为止,内核的初始化已经进入尾声,第一个用户空间进程init将姗姗来迟。
代码清单4 init_post函数
774 static int noinline init_post(void)
775 {
776 free_initmem();
777 unlock_kernel();
778 mark_rodata_ro();
779 system_state = SYSTEM_RUNNING;
780 numa_default_policy();
781
782 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
783 printk(KERN_WARNING "Warning: unable to open an initial console.\n");
784
785 (void) sys_dup(0);
786 (void) sys_dup(0);
787
788 if (ramdisk_execute_command) {
789 run_init_process(ramdisk_execute_command);
790 printk(KERN_WARNING "Failed to execute %s\n",
791 ramdisk_execute_command);
792 }
793
794 /*
795 * We try each of these until one succeeds.
796 *
797 * The Bourne shell can be used instead of init if we are
798 * trying to recover a really broken machine.
799 */
800 if (execute_command) {
801 run_init_process(execute_command);
802 printk(KERN_WARNING "Failed to execute %s. Attempting "
803 "defaults...\n", execute_command);
804 }
805 run_init_process("/sbin/init");
806 run_init_process("/etc/init");
807 run_init_process("/bin/init");
808 run_init_process("/bin/sh");
809
810 panic("No init found. Try passing init= option to kernel.");
811 }
第776行,到此,内核初始化已经接近尾声了,所有的初始化函数都已经被调用,因此free_initmem函数可以舍弃内存的__init_begin至__init_end(包括.init.setup、.initcall.init等节)之间的数据。
所有使用__init标记过的函数和使用__initdata标记过的数据,在free_initmem函数执行后,都不能使用,它们曾经获得的内存现在可以重新用于其他目的。
第782行,如果可能,打开控制台设备,这样init进程就拥有一个控制台,并可以从中读取输入信息,也可以向其中写入信息。
实际上init进程除了打印错误信息以外,并不使用控制台,但是如果调用的是shell或者其他需要交互的进程,而不是init,那么就需要一个可以交互的输入源。如果成功执行open,/dev/console即成为init的标准输入源(文件描述符0)。
第785~786行,调用dup打开/dev/console文件描述符两次。这样,该控制台设备就也可以供标准输出和标准错误使用(文件描述符1和2)。假设第782行的open成功执行(正常情况),init进程现在就拥有3个文件描述符--标准输入、标准输出以及标准错误。
第788~804行,如果内核命令行中给出了到init进程的直接路径(或者别的可替代的程序),这里就试图执行init。
因为当kernel_execve函数成功执行目标程序时并不返回,只有失败时,才能执行相关的表达式。接下来的几行会在几个地方查找init,按照可能性由高到低的顺序依次是: /sbin/init,这是init标准的位置;/etc/init和/bin/init,两个可能的位置。
第805~807行,这些是init可能出现的所有地方。如果在这3个地方都没有发现init,也就无法找到它的同名者了,系统可能就此崩溃。因此,第808行会试图建立一个交互的shell(/bin/sh)来代替,希望root用户可以修复这种错误并重新启动机器。
第810行,由于某些原因,init甚至不能创建shell。当前面的所有情况都失败时,调用panic。这样内核就会试图同步磁盘,确保其状态一致。如果超过了内核选项中定义的时间,它也可能会重新启动机器