前言
根据前文Linux根文件系统挂载流程中的分析,内核通过调用根文件系统中的init程序跳转到用户空间,并对用户空间所需的基础框架进行初始化。类比于内核启动时,需要对各个功能模块进行初始化,当进入用户空间时,也需要启动各项服务来搭建基础的应用环境。对于不同的init系统管理器,服务启动的方式有所区别,下文主要对yocto中使用的sysVinit、busybox init和systemd进行介绍。
sysVinit
在sysvinit中,服务由shell脚本提供,并且不同的runlevel包含不同的shell脚本集合,可将系统启动到具有不同服务的用户模式。其中,所有能够提供服务的shell脚本都包含在/etc/init.d目录下,对目录下的shell脚本进行不同的组合便可以组成具有不同服务的用户模式,即runlevel,其结构映射如下所示:
如上图,可以将包含所有shell脚本的/etc/init.d看作一个服务库,通过提取库中的不同服务并组合便可以定制不同的系统模式,而系统启动到哪种模式,由配置文件/etc/inittab指定,/etc/inittab文件的内容由多个指定格式的项所组成,每一项的格式规定如下:
label:runlevel:action:process
- label:标识符,与各项唯一对应,限于1-4个字节。其中,包括如下几个默认标识符的定义:
- id:缺省的init运行级别。无实际执行内容,只是向init程序指示系统需要启动到哪个runlevel对应的模式。
- si:系统初始化时启动的进程,在进入任何一个runlevel之前都需执行。
- ln:指定进程在哪个/哪些runlevel下运行,n为runlevel的值。
- ca:当检测到Ctrl+Alt+Del时启动的进程。
- runlevel:运行级别,进入指定运行级别时启动对应的进程,各运行级别定义如下:
- 0:Halt,关闭系统
- 1:单用户模式,root权限,需输入root口令,进入该模式之前关闭所有应用和服务。
- 2:无网络多用户模式
- 3:有网络多用户模式(控制台命令行)
- 4:保留,可供用户自行定义
- 5:xwindow图形界面模式(图形GUI)
- 6:重启系统
- S/s:单用户模式(初始化),当系统初次启动或者从多用户模式切换到单用户模式时,该进程首先被执行。
- action:如何运行该进程,有效的运行方式定义如下:
- boot:系统引导期间执行。
- bootwait:系统引导期间执行,并且等待该进程运行结束,该参数用于多用户模式。
- sysinit:初始化时执行(boot、bootwait之前),runlevel域被忽略。
- initdefault:特定用于指定系统引导进入的runlevel级别,若该项不存在,将通过控制台向用户询问。
- respawn:自启动进程,当进程被终止时自动重启。
- once:指定进程只启动一次。
- off:终止进程,当启动到指定运行级别中发现指定进程处于运行状态,则终止该进程。
- wait:等待进程运行结束才启动下一进程。
- ctrlaltdel:当检测到当检测到Ctrl+Alt+Del时启动的进程。
- process:可执行程序,即待启动进程。
以一个具体的文件作为示例:
id:5:initdefault: //默认的运行级别为5
si::sysinit:/etc/init.d/rcS //系统初始化首先运行/etc/init.d/rcS脚本
~~:S:wait:/sbin/sulogin //单用户模式需要运行sbin/sulogin脚本
l0:0:wait:/etc/init.d/rc 0 //执行/etc/init.d/rc 传入参数0,等待脚本执行完毕
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
sysvinit系统启动流程:
- init程序首先解析/etc/inittab文件,获取需要进入的runlevel模式
- 执行初始化进程(action=sysinit),进入单用户模式(s)?;
- 进入runlevel模式(执行shell脚本集合)。
根据以上示例可以看出:系统默认启动到图形化多用户模式(runlevel 5);然后执行初始化的rcS脚本,该rcS脚本会启动单用户模式(S)的脚本集合;进入单用户模式,启动sulogin;进入多用户模式,启动/etc/init.d/rc脚本并输入参数5,该进程将执行属于runlevel=5的脚本集合。
每个runlevel的脚本集合存储在对应的/etc/rc$(runlevel).d目录下,该目录下所有的文件为/etc/init.d的符号链接。当init.d中的文件不存在时,runlevel对应目录下的文件将不复存在。在yocto项目中,符号链接在编译阶段,通过 update.rc映射产生,并且生成shell脚本的执行顺序。update.rc的示例如下:
update-rc.d apache2 start 20 2 3 4 5 . stop 80 0 1 6 .
apache2为可执行程序的名称,start 20 2 3 4 5表示该服务在进入runlevel 2 3 4 5时需要执行,并在第20个启动。此时,update.rc将在runlevel 2 3 4 5中分别映射一个指向/etc/init.d/apache2的符号链接,名称均为S20apache2。stop 80 0 1 6表示在进入runlevel 0 1 6时需要禁止,并在第80个关闭。此时,update.rc将在runlevel 0 1 6 中分别映射一个指向/etc/init.d/apache2的符号链接,名称均为K80apache2。
sysvinit在进入对应runlevel时,根据/etc/init.d/rc可知,首先执行所有以K开头的脚本(传入脚本的参数为stop),然后执行所有以S开头的脚本(传入脚本的参数为start)。所有以K开头的脚本按照紧接着的数字,从小到大开始执行,相同数字的脚本按照创建顺序执行。同理,对于所有以S开头的脚本,其执行顺序遵循同样的规则。
busybox init
busybox是众多linux命令的一个工具集,根目录下的bin,sbin,usr和linuxrc通常就是Busybox。使用busybox时,kernel cmdline的init一般指向linuxrc,但是linuxrc最终还是会调用到/sbin/init这个真正的init进程。init进程的执行流如下:
- 为init进程设置信号处理进程
- 对控制台进行初始化
- 解析inittab文件,即etc/inittab,若无inittab文件,则采用默认的配置
- 执行/etc/init.d/rcS
busybox中/etc/inittab文件具有与sysvinit类似的格式,只是各字段定义有所区别:
id:runlevel_ignored:action:command
- id:启动进程的控制终端,如shell终端
- runlevel_ignored:该字段在busybox中被忽略,保持为空
- action