start_kernel简单总结

首先是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。这样内核就会试图同步磁盘,确保其状态一致。如果超过了内核选项中定义的时间,它也可能会重新启动机器


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值