ARM linux源码分析之init进程创建和执行过程

来源:互联网 发布:seo外包的优势 编辑:程序博客网 时间:2024/06/05 22:56
init进程,是一个由内核启动的用户级进程,内核自行启动后,就通过启动 init来完成引导进程。所以,init始终是第一个进程(其进程编号始终为1)。

init 进程首先进行一系列的硬件初始化,并挂载根文件系统。最后 init 进程会执行用 户传递过来的“init=”启动参数执行用户指定的命令,或者执行以下几个进程之一,由内核态变为用户态:

<span style="font-size:18px;">static noinline int init_post(void)  __releases(kernel_lock){  ......[1]if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)    printk(KERN_WARNING "Warning: unable to open an initial console.\n");   ......[2]if (execute_command) {    run_init_process(execute_command);    printk(KERN_WARNING "Failed to execute %s.  Attempting "          "defaults...\n", execute_command);  }[3]run_init_process("/sbin/init");  run_init_process("/etc/init");  run_init_process("/bin/init");  run_init_process("/bin/sh");[4]panic("No init found.  Try passing init= option to kernel.");}</span>
代码[1]:在启动第一个程序前,首先打开控制台设备sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0),这样init进程就拥有一个控制台,并可以从中读取输入信息在串口终端显示,也可以向其中写入信息。
代码[2]: 如果bootloader里设置传给内核的参数init=/linuxrc,execute_command就等于/linuxrc,如果定义了,就会run_init_process(execute_command);执行这个应用程序,如果没有定义这个参数,就会往下走。
代码[3]: 如果bootloader中没有设置传给内核的参数init=,那么就依次执行这四个,如果进入其中一个之后,就不会再返回了。
代码[4]:如果bootloader没有传递参数,上面[3]中指定运行的四个文件都不在的话,系统就陷入暂停,等待用户输入init=?的正确地址。
init进程是所有其他进程的父进程,它根据文件的内容运行一系列程序和脚本文件,完成系统的各项配置,最终达到启动用户应用程序的目的。
在这里我们分析两种init初始化模式:
1.标准的system V 初始化
2.BusyBox初始化
在实际中内核只要有一个个可以运行的运行的init就可以了,所有,这两种模式对于内核来说是透明的。
当 init 进程启动时(使用传统的sysvinit版本),它会打开一个名为 /etc/inittab 的文件。这个文件是 init 的配置文件,定义了如何对系统进行初始化。这个文件还包含了有关出现电源故障时执行的操作(如果系统支持)、以及在检测到 Ctrl-Alt-Delete 键序列时应该如何反应的信息。
inittab 配置文件使用通用格式定义了几项内容:id:runlevels:action:process。其中 id 是惟一标识该项的字符序列。runlevels 定义了操作所使用的运行级别。action 指定了要执行的特定操作。最后,process 定义了要执行的进程。

<span style="font-size:18px;">/*运行级别就是操作系统当前正在运行的功能级别。这个级别从1到6 ,具有不同的功能。 *0 – 停机(千万不能把initdefault 设置为0 ) *1 – 单用户模式 *2 – 多用户,没有 NFS *3 – 完全多用户模式(标准的运行级) *4 – 没有用到 *5 – X11 (xwindow) *6 – 重新启动 (千万不要把initdefault 设置为6 ) */# The default runlevelid:2:initdefault# Boot-time system configuration/initialization scriptsi::sysinit:/etc/init.d/rcS# Runlevelsl0:0:wait:/etc/init.d/rc 0l1:1:wait:/etc/init.d/rc 1l2:2:wait:/etc/init.d/rc 2l3:3:wait:/etc/init.d/rc 3l4:4:wait:/etc/init.d/rc 4l5:5:wait:/etc/init.d/rc 5l6:6:wait:/etc/init.d/rc 6z6:6:respawn:/sbin/sulogin# How to react to ctrl-alt-delca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now</span>
在 init 加载 /etc/inittab 之后,就会将系统切换到 initdefault 操作所定义的运行级别。在本文中代码中运行模式2;
在定义初始的运行级别n之后,则调用 rcn来启动系统。这个脚本然后会调用各种服务和应用程序脚本来启动或停止特定的元素。在本例中,文件都是在 /etc/rc2.d/ 中定义的:
<span style="font-size:18px;">Snnn.script_name</span>
<span style="font-size:18px;">Knnn.script_name</span>

其 中,
S:代表启动相应的进程,K:代表杀死相应的进程。nn:是00至99的两位数字,不过在有些系统中是000至999三位数字。 在不同目录中的链接应采用同一数字。例如,如果某个服务在rc3.d中启动时名为S45.myscript,那么如果希望它在rc2.d中启动,应当使用 链接名S45.myscript。script_name:相应脚本的文件名,根据所在操作系统的不同,它们可能位于下列目录中:/usr/sbin/init.d、/etc/rc.d、/etc/init.d。
当 init进程调用相应的运行级别脚本时,杀进程按照从高到低的K序号进行,即K23.myscript -> K12.named;而启动进程按照从低到高的序号进行。
综上所述:
init执行的基本流程如下:
1.解析/etc/inittab:执行sysinit命令指定的进程,以前通常是/etc/init.d/rcS,在新版本的init程序中则通常是/etc/rc.d/rc.sysinit脚本。


2.执行/etc/rc.d/rc.sysinit:这是由init执行的第一个脚本,此步进行的工作包括配置网络、配置内核参数、挂载root文件系统、检查文件系统、设置系统时钟、配置机器、开启交换空间等。


3.执行/etc/rc.d/rcX.d/[K...][S...]:根据定义的initdefault运行级别,执行对应wait命令指定的程序,这会运行对应目录下的各个程序,并等待它们运行完。在rcX.d目录下,首先终止K开头的服务(用来关闭一个服务),然后启动S开头的服务(用来启动一个服务)。对每一个运行级别来说,在/etc/rc.d子目录中都有一个对应的下级目录。这些运行级别的下级子目录的命名方法为rcX.d, 其中X就是代表运行级别的数字。在各个运行级别的子目录中,都建立有到/etc/rc.d/init.d子目录中命令脚本程序的符号链接,链接的名称在K与S后有一个数字,表示执行顺序,数字小的先执行,例如K01tog-pegasus、S00microcode_ctl。对以K开头的脚本执行时系统会传递stop参数,而S开头的脚本系统会传递start参数。


4.执行/etc/rc.d/rc.local:Redhat中运行模式2,3,5都把/etc/rc.d/rc.local作为初始化脚本中的最后一个文件,所以用户可以自己在这个文件中添加一些需要在其他初始化工作之后,登陆之前执行的命令。
5.执行getty程序:为每个联机终端使用fork()创建一个子进程,并在子进程中运行getty程序,init进程则调用wait(),进入等待子进程结束状态。getty程序设置终端类型、属性、速度和线路规程等。对于字符界面的运行级别(如级别2和3),它会打开并初始化一个tty端口,显示提示信息。通常,若/etc/issue文本文件存在,则getty会首先显示其中的文本信息,然后显示登录提示信息(例如“plinux login:” ),出现字符登录界面,并等待用户键入用户名和口令。可以在inittab文件中配置使用哪一种getty程序(在“id:runlevels:action:process”的process部分指定,并可以传递相应的getty参数),如agetty, getty, mgetty, uugetty, mingetty,fbgetty等。getty程序只能由超级用户执行。
注意如果第1步中的inittab文件指定的默认运行级别是图形用户界面形式(如级别5),则init程序会转向去执行/etc/X11/prefdm脚本,它会执行/usr/sbin/gdm,启动图形登录界面。GDM管理的不只是X的启动,还有登录,注销,挂起等一系列操作。
启动登录界面(图形或字符界面),并输入完用户名后,getty会调用login程序。


6.执行login程序:getty调用exec()执行login程序,以核对输入的用户名和口令。由于调用了exec(而不是fork),login的执行环境会覆盖getty的执行环境。login进程会读取/etc/passwd,以用户名和口令。login根据用户输入的用户名,从口令文件passwd中取得对应用户的登录项,然后调用getpass()以显示”password:”提示信息,读取用户键入的密码,然后使用加密算法对键入的密码进行加密处理,并与口令文件中该用户项中pw_passwd字段作比较。如果用户几次键入的密码均无效,则login程序会以出错码1退出执行,表示此次登录过程失败。此时父进程(进程init)的wait()会返回该退出进程的pid,因此会根据记录下来的信息再次创建一个子进程,并在该子进程中针对该终端设备再次执行getty程序,重复上述过程。
如果用户键入的密码正确,则login就会把当前工作目录(Currend Work Directory)修改成口令文件中指定的起始工作目录。并把对该终端设备的访问权限修改成用户读/写和组写,设置进程的组ID。然后利用所得到的信息初始化环境变量信息,例如起始目录(HOME=)、使用的shell程序(SHELL=)、用户名(USER=和LOGNAME=)和系统执行程序的默认路径序列(PATH=)。接着显示/etc/motd文件(message-of-the-day)中的文本信息,并检查并显示该用户是否有邮件的信息。最后login程序改变成登录用户的用户ID,并执行口令文件中该用户项中指定的shell程序,如/bin/bash或/bin/csh等。有关login程序的一些执行选项和特殊访问限制的说明,可参见Linux系统中的在线手册页(man -8 login)。


7.执行shell程序或x-windows:如果用户名和口令正确,login调用exec执行shell命令行解释程序(当然,也可以执行X-windows的图形界面,如果用户设置了的话)。登录shell会首先从/etc/profile文件以及$HOME/.bash_profile文件(或.bashrc文件,若存在的话)读取命令并执行。因此用户可以把每次登录时都要执行的命令放在.bash_profile文件中。如果在进入shell时设置了ENV环境变量(或者在.bash_profile文件中设置了该变量),则shell还会从$ENV指定的文件中读去命令并执行。因此我们也可以把每次运行shell都要执行的命令放在ENV变量指定的文件中。设置ENV环境变量的方法是把下列语句放在你起始目录的.bash_profile文件中: ENV=$HOME/.anyfilename; export ENV。
运行shell时,原来的getty进程最终被替换成了bash进程,对应的getty,login,bash这三个程序也就具有相同的进程ID。在成功登录到Linux系统后,你会发现(使用”top”或”ps –ax”命令)自己终端原来的getty进程已经找不到了。因为getty进程执行了login程序,被替换成了login进程,并且最后被替换成你的登录shell进程。对于图形用户界面,login程序最后会被替换成图形界面进程(如gnome-session程序)。


8.Linux运行时:init进程会负责收取孤儿进程。如果某个进程创建子进程之后,在子进程终止之前终止,则子进程成为孤儿进程。在Linux中所有的进程必须属于单棵进程树,所以孤立进程必须被收取。一旦进程成为孤儿,它会立即成为init进程的子进程。这是为了保持进程树的完整性。


9.用户注销:当某个终端或虚拟控制台上的用户注销之后,该终端上的所有进程都会被终止(killed),包括bash。然后,init进程就会调用fork为该终端或虚拟控制台重新创建一个getty进程,以便能够让其他用户登录。这是为什么呢?你应该发现,当用户登录时,“getty”用的是“exec”而不是“fork”系统调用来执行“login”,这样,“login”在执行的时候会覆盖“getty”的执行环境(同理,用户注册成功后,“login”的执行环境也会被shell占用)。所以,如果想再次使用同一终端,必须再启动一个“getty”。对于图形界面,用户注销后会回到图形登录界面。

10.系统关闭:init负责杀死所有其它的进程,卸载所有的文件系统并停止处理器的工作,以及任何其它被配置成要做的工作。











0 0