Linux内核启动及加载根文件系统
</pre></h1><p><span style="font-family:KaiTi_GB2312;font-size:18px;">上接博文《<a target=_blank href="http://blog.csdn.net/gqb_driver/article/details/8931775" style="text-decoration: none; font-family: 'Microsoft YaHei'; font-size: 20px; line-height: 30px;"><span style="color:#ff0000;">u-boot之u-boot-2009.11启动过程分析</span></a>》</span></p><p><span style="font-family:KaiTi_GB2312;font-size:18px;"><strong>Linux内核启动及加载根文件系统</strong></span></p><p><span style="font-family:KaiTi_GB2312; font-size:14px"> 当</span><span style="font-family:Times New Roman; font-size:14px">u-boot</span><span style="font-family:KaiTi_GB2312; font-size:14px">开始执行</span><span style="font-family:Times New Roman; font-size:14px">bootcmd</span><span style="font-family:KaiTi_GB2312; font-size:14px">命令,就进入</span><span style="font-family:Times New Roman; font-size:14px">Linux</span><span style="font-family:KaiTi_GB2312; font-size:14px">内核启动阶段,与</span><span style="font-family:Times New Roman; font-size:14px">u-boot</span><span style="font-family:KaiTi_GB2312; font-size:14px">类似,普通</span><span style="font-family:Times New Roman; font-size:14px">Linux</span><span style="font-family:KaiTi_GB2312; font-size:14px">内核的启动过程也可以分为两个阶段,但针对压缩了的内核如</span><span style="font-family:Times New Roman; font-size:14px">uImage</span><span style="font-family:KaiTi_GB2312; font-size:14px">就要包括内核自解压过程了。本文以项目中使用的</span><span style="font-family:Times New Roman; font-size:14px">linux-2.6.37</span><span style="font-family:KaiTi_GB2312; font-size:14px">版源码为例分三个阶段来描述内核启动全过程。第一阶段为内核自解压过程,第二阶段主要工作是设置</span><span style="font-family:Times New Roman; font-size:14px">ARM</span><span style="font-family:KaiTi_GB2312; font-size:14px">处理器工作模式、使能</span><span style="font-family:Times New Roman; font-size:14px">MMU</span><span style="font-family:KaiTi_GB2312; font-size:14px">、设置一级页表等,而第三阶段则主要为C代码,包括内核初始化的全部工作,下面是详细介绍。</span></p><p><span style="font-family:KaiTi_GB2312; font-size:14px"></span><h2 style="margin: 0px; padding: 0px; color: rgb(51, 51, 51); font-family: Arial; line-height: 26px;"><p style="font-size: 14px; line-height: 25px;"><span style="line-height: 26px;">/******************************************************************************************************************************************/</span></p><p><span style="font-size: 10px; font-weight: normal;">原创作品,转载时请务必以超链接形式标明文章原始出处:<a target=_blank target="_blank" href="http://blog.csdn.net/gqb666/article/details/8976263" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb666/article/details/8976263" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb666/article/details/9117597" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb666/article/details/9120483" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb666/article/details/8994789" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb666/article/details/9054413" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb666/article/details/9054413" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb_driver/article/details/17019275" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb_driver/article/details/17055901" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb_driver/article/details/17067201" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb_driver/article/details/17091659" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb_driver/article/details/17114011" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb_driver/article/details/18034377" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb_driver/article/details/18238063" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb_driver/article/details/8917451" style="color: rgb(51, 102, 153); text-decoration: none;"></a><a target=_blank target="_blank" href="http://blog.csdn.net/gqb_driver/article/details/20069643" style="color: rgb(51, 102, 153); text-decoration: none;">http://blog.csdn.net/gqb_driver/article/details/26161357</a>,作者:<a target=_blank target="_blank" href="http://blog.csdn.net/gqb666" style="color: rgb(51, 102, 153); text-decoration: none;">gqb666</a> </span></p><span style="font-size: 14px;">/******************************************************************************************************************************************/</span></h2></p><p><span style="font-family:KaiTi_GB2312;"><strong><span style="font-size: 14px;">一、Linux</span><span style="font-size: 14px;">内核自解压过程</span></strong></span></p><p><span style="font-family:KaiTi_GB2312; font-size:14px">在</span><span style="font-family:Times New Roman; font-size:14px">linux</span><span style="font-family:KaiTi_GB2312; font-size:14px">内核启动过程中一般能看到图1内核自解压界面,本小节本文重点讨论内核的自解压过程。</span></p><p><span style="font-family:KaiTi_GB2312; font-size:14px"> <img src="http://img.blog.csdn.net/20140525130224968?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ3FiX2RyaXZlcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" /></span></p><p></p><p><span style="font-family:KaiTi_GB2312; font-size:14px"> </span><span style="font-family:SimSun; font-size:12px">图1解压内核</span></p><p><span style="font-family:KaiTi_GB2312; font-size:14px">内核压缩和解压缩代码都在目录</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">kernel/arch/arm/boot/compressed</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">,编译完成后将产生</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">head.o、misc.o、piggy.gzip.o、vmlinux、decompress.o</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">这几个文件,</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">head.o</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">是内核的头部文件,负责初始设置;</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">misc.o</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">将主要负责内核的解压工作,它在</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">head.o</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">之后;</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">piggy.gzip.o</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">是一个中间文件,其实是一个压缩的内核(kernel/vmlinux),只不过没有和初始化文件及解压文件链接而已;vmlinux是没有(zImage是压缩过的内核)压缩过的内核,就是由</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">piggy.gzip.o、head.o、misc.o</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">组成的,而d</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">ecompress.o</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">是为支持更多的压缩格式而新引入的。</span></p><p><span style="font-family:KaiTi_GB2312; font-size:14px">在</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">BootLoader</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">完成系统的引导以后并将</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">Linux</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">内核调入内存之后,调用</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">do_bootm_linux()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">,这个函数将跳转到</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">kernel</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">的起始位置。如果</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">kernel</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">没有被压缩,就可以启动了。如果</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">kernel</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">被压缩过,则要进行解压,在压缩过的</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">kernel</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">头部有解压程序。压缩过的</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">kernel</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">入口第一个文件源码位置在</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">arch/arm/boot/compressed/head.S</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">。它将调用函数</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">decompress_kernel()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">,这个函数在文件</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">arch/arm/boot/compressed/misc.c</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">中,</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">decompress_kernel()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">又调用</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">proc_decomp_setup()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">,</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">arch_decomp_setup()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">进行设置,然后打印出信息“</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">Uncompressing Linux...</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">”后,调用</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">gunzip()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">将内核放于指定的位置。</span></p><p><span style="font-family:KaiTi_GB2312; font-size:14px">下面简单介绍一下解压缩过程,也就是函数</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">decompress_kernel</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">实现的功能:解压缩代码位于</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">kernel/lib/inflate.c,inflate.c</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">是从</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">gzip</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">源程序中分离出来的,包含了一些对全局数据的直接引用,在使用时需要直接嵌入到代码中。</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">gzip</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">压缩文件时总是在前32K字节的范围内寻找重复的字符串进行编码, 在解压时需要一个至少为</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">32K</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">字节的解压缓冲区,它定义为</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">window[WSIZE]</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">。inflate.c使用get_byte()读取输入文件,它被定义成宏来提高效率。输入缓冲区指针必须定义为</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">inptr,inflate.c</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">中对之有减量操作。</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">i</span></span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">nflate.c</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">调用</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">flush_window()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">来输出</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">window</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">缓冲区中的解压出的字节串,每次输出长度用</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">outcnt</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">变量表示。在</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">flush_window()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">中,还必须对输出字节串计算</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">CRC</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">并且刷新</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">crc</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">变量。在调用</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">gunzip()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">开始解压之前,调用</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">makecrc()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">初始化</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">CRC</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">计算表。最后</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">gunzip()</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">返回</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">0</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">表示解压成功。我们在内核启动的开始都会看到这样的输出:</span></p><p><span style="font-size: 14px;"><span style="font-family:Times New Roman;">UncompressingLinux...done, booting the kernel.</span></span></p><p><span style="font-family:KaiTi_GB2312; font-size:14px">这也是由</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">decompress_kernel</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">函数输出的,执行完解压过程,再返回到</span><span style="font-size: 14px;"><span style="font-family:Times New Roman;">head.S</span></span><span style="font-family:KaiTi_GB2312; font-size:14px">中的583行,启动内核:</span></p><p><span style="font-family:KaiTi_GB2312; font-size:14px"></span></p><pre name="code" class="plain">call_kernel: bl cache_clean_flush
bl cache_off
mov r0, #0 @ must be zero
mov r1, r7 @ restore architecture number
mov r2, r8 @ restore atags pointer
mov pc, r4 @ call kernel
其中r4中已经在head.S的第180行处预置为内核镜像的地址,如下代码:
#ifdef CONFIG_AUTO_ZRELADDR @determine final kernel image address mov r4, pc and r4, r4, #0xf8000000 add r4, r4, #TEXT_OFFSET #else ldr r4, =zreladdr #endif
这样就进入Linux内核的第一阶段,我们也称之为stage1。
二、Linux内核启动第一阶段stage1
承接上文,这里所以说的第一阶段stage1就是内核解压完成并出现Uncompressing Linux...done,booting the kernel.之后的阶段。该部分代码实现在arch/arm/kernel的 head.S中,该文件中的汇编代码通过查找处理器内核类型和机器码类型调用相应的初始化函数,再建 立页表,最后跳转到 start_kernel()函数开始内核的初始化工作。检测处理器类型是在汇编子函数__lookup_processor_type中完成的,通过以下代码可实现对它的调用: bl__lookup_processor_type(在文件head-commom.S实现)。__lookup_processor_type调用结束返回原程序时,会将返回结果保存到寄存器中。其中r5寄存器返回一个用来描述处理器的结构体地址,并对r5进行判断,如果r5的值为0则说明不支持这种处理器,将进入__error_p。 r8 保存了页表的标志位,r9 保存了处理器的 ID 号,r10 保存了与处理器相关的 struct proc_info_list 结构地址。Head.S核心代码如下:
ENTRY(stext) setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @设置SVC模式关中断 mrc p15, 0, r9, c0, c0 @获得处理器ID,存入r9寄存器 bl __lookup_processor_type @ 返回值r5=procinfo r9=cpuid movs r10, r5 THUMB( it eq ) @ force fixup-able long branch encoding beq __error_p @如果返回值r5=0,则不支持当前处理器' bl __lookup_machine_type @ 调用函数,返回值r5=machinfo movs r8, r5 @ 如果返回值r5=0,则不支持当前机器(开发板) THUMB( it eq ) @ force fixup-able long branch encoding beq __error_a @ 机器码不匹配,转__error_a并打印错误信息 bl __vet_atags #ifdef CONFIG_SMP_ON_UP @ 如果是多核处理器进行相应设置 bl __fixup_smp #endif bl __create_page_tables @最后开始创建页表
检测机器码类型是在汇编子函数__lookup_machine_type (同样在文件head-common.S实现) 中完成的。与__lookup_processor_type类似,通过代码:“bl __lookup_machine_type”来实现对它的调 用。该函数返回时,会将返回结构保存放在 r5、r6 和 r7 三个寄存器中。其中r5寄存器返回一个用来描述机器(也就是开发板)的结构体地址,并对r5进行判断,如果r5的值为0则说明不支持这种机器(开发板),将进入__error_a,打印出内核不支持u-boot传入的机器码的错误如图2。r6 保存了 I/O基地址,r7 保存了 I/O的页表偏移地址。 当检测处理器类型和机器码类型结束后,将调用__create_page_tables子函数来建立页表,它所要做的工作就是将 RAM 基地址开始的1M 空间的物理地址映射到 0xC0000000 开始的虚拟地址处。对本项目的开发板DM3730而言,RAM 挂接到物理地址 0x80000000处,当调用__create_page_tables 结束后 0x80000000 ~ 0x80100000 物理地址将映射到 0xC0000000~0xC0100000 虚拟地址处。当所有的初始化结束之后,使用如下代码来跳到 C 程序的入口函数 start_kernel()处,开始之后的内核初始化工作: bSYMBOL_NAME(start_kernel) 。
图2 机器码不匹配错误
三、Linux内核启动第二阶段stage2
从start_kernel函数开始
Linux内核启动的第二阶段从start_kernel函数开始。start_kernel是所有 Linux 平台进入系统内核初始化后的入口函数,它主要完成剩余的与 硬件平台相关的初始化工作,在进行一系列与内核相关的初始化后,调用第一个用户进程- init 进程并等待用户进程的执行,这样整个 Linux 内核便启动完毕。该函数位于init/main.c文件中,主要工作流程如图3所示:
图3 start_kernel流程图
该函数所做的具体工作有 :
1) 调用 setup_arch()函数进行与体系结构相关的第一个初始化工作;对不同的体系结构来说该函数有不同的定义。对于ARM 平台而言,该函数定义在 arch/arm/kernel/setup.c。它首先通过检测出来的处理器类型进行处理器内核的初始化,然后 通过 bootmem_init()函数根据系统定义的 meminfo 结构进行内存结构的初始化,最后调用 paging_init()开启 MMU,创建内核页表,映射所有的物理内存和IO空间。
2) 创建异常向量表和初始化中断处理函数;
3) 初始化系统核心进程调度器和时钟中断处理机制;
4) 初始化串口控制台(console_init);
ARM-Linux 在初始化过程中一般都会初始化一个串口做为内核的控制台,而串口Uart驱动却把串口设备名写死了,如本例中linux2.6.37串口设备名为ttyO0,而不是常用的ttyS0。有了控制台内核在启动过程中就可以通过串口输出信息以便开发者或用户了解系统的启动进程。
5) 创建和初始化系统 cache,为各种内存调用机制提供缓存,包括;动态内存分配,虚拟文件系统(VirtualFile System)及页缓存。
6) 初始化内存管理,检测内存大小及被内核占用的内存情况;
7) 初始化系统的进程间通信机制(IPC); 当以上所有的初始化工作结束后,start_kernel()函数会调用rest_init()函数来进行最后的初始化,包括创建系统的第一个进程-init 进程来结束内核的启动。
挂载根文件系统并启动init
Linux内核启动的下一过程是启动第一个进程init,但必须以根文件系统为载体,所以在启动init之前,还要挂载根文件系统。
四、挂载根文件系统
根文件系统至少包括以下目录:
/etc/:存储重要的配置文件。
/bin/:存储常用且开机时必须用到的执行文件。
/sbin/:存储着开机过程中所需的系统执行文件。
/lib/:存储/bin/及/sbin/的执行文件所需的链接库,以及Linux的内核模块。
/dev/:存储设备文件。
注:五大目录必须存储在根文件系统上,缺一不可。
以只读的方式挂载根文件系统,之所以采用只读的方式挂载根文件系统是因为:此时Linux内核仍在启动阶段,还不是很稳定,如果采用可读可写的方式挂载根文件系统,万一Linux不小心宕机了,一来可能破坏根文件系统上的数据,再者Linux下次开机时得花上很长的时间来检查并修复根文件系统。
挂载根文件系统的而目的有两个:一是安装适当的内核模块,以便驱动某些硬件设备或启用某些功能;二是启动存储于文件系统中的init服务,以便让init服务接手后续的启动工作。
执行init服务
Linux内核启动后的最后一个动作,就是从根文件系统上找出并执行init服务。Linux内核会依照下列的顺序寻找init服务:
1)/sbin/是否有init服务
2)/etc/是否有init服务
3)/bin/是否有init服务
4)如果都找不到最后执行/bin/sh
找到init服务后,Linux会让init服务负责后续初始化系统使用环境的工作,init启动后,就代表系统已经顺利地启动了linux内核。启动init服务时,init服务会读取/etc/inittab文件,根据/etc/inittab中的设置数据进行初始化系统环境的工作。/etc/inittab定义init服务在linux启动过程中必须依序执行以下几个Script:
/etc/rc.d/rc.sysinit
/etc/rc.d/rc
/etc/rc.d/rc.local
/etc/rc.d/rc.sysinit主要的功能是设置系统的基本环境,当init服务执行rc.sysinit时 要依次完成下面一系列工作:
(1)启动udev
(2)设置内核参数
执行sysctl –p,以便从/etc/sysctl.conf设置内核参数
(3)设置系统时间
将硬件时间设置为系统时间
(4)启用交换内存空间
执行swpaon –a –e,以便根据/etc/fstab的设置启用所有的交换内存空间。
(5)检查并挂载所有文件系统
检查所有需要挂载的文件系统,以确保这些文件系统的完整性。检查完毕后以可读可写的方式挂载文件系统。
(6)初始化硬件设备
Linux除了在启动内核时以静态驱动程序驱动部分的硬件外,在执行rc.sysinit时,也会试着驱动剩余的硬件设备。rc.sysinit驱动的硬件设备包含以下几项:
a)定义在/etc/modprobe.conf的模块
b)ISA PnP的硬件设备
c)USB设备
(7)初始化串行端口设备
Init服务会管理所有的串行端口设备,比如调制解调器、不断电系统、串行端口控制台等。Init服务则通过rc.sysinit来初始化linux的串行端口设备。当rc.sysinit发现linux才能在这/etc/rc.serial时,才会执行/etc/rc.serial,借以初始化所有的串行端口设备。因此,你可以在/etc/rc.serial中定义如何初始化linux所有的串行端口设备。
(8)清除过期的锁定文件与IPC文件
(9)建立用户接口
在执行完3个主要的RC Script后,init服务的最后一个工作,就是建立linux的用户界面,好让用户可以使用linux。此时init服务会执行以下两项工作:
(10)建立虚拟控制台
Init会在若干个虚拟控制台中执行/bin/login,以便用户可以从虚拟控制台登陆linux。linux默认在前6个虚拟控制台,也就是tty1~tty6,执行/bin/login登陆程序。当所有的初始化工作结束后,cpu_idle()函数会被调用来使系统处于闲置(idle)状态并等待用户程序的执行。至此,整个 Linux 内核启动完毕。整个过程见图4。
图4:linux内核启动及文件系统加载全过程