linux 文件系统sysvinit 流程分析

来源:互联网 发布:淘宝有人肉搜索吗 编辑:程序博客网 时间:2024/06/08 16:25

http://www.cnblogs.com/chenfulin5/p/5892507.html


参考网上许多的教程。

然后有一下相关的笔记:

kernel 在挂载完文件系统后,会执行第一个进程init 

这个进程的PID为1

这个进程是所有进程的父进程

 

init 进程,首先要去读取inittab中的数据,根据这里面的数据去执行相关的初始化。

在inittab  可以指定默认的运行级别

  

id:5:initdefault:

还会规定第一个执行的程序

si::sysinit:/etc/init.d/rcS

 

//指定单用户模式~~:S:wait:/sbin/sulogin

 

在TI的板子上还规定终端的显示的开关

0:2345:respawn:/sbin/getty 115200 ttyO0

 

 

这里提到,他规定下一个执行的是/etc/init.d/rcS

在/etc/init.d/rcS 内又做了哪些工作呢?

复制代码
PATH=/sbin:/bin:/usr/sbin:/usr/bin                                 //设置环境变量             runlevel=S                                                                      //设置运行级别prevlevel=N                                                                     umask 022                                                                       //设置默认权限补码export PATH runlevel prevlevel                                          //对外发布这些变量
复制代码

 

trap ":" INT QUIT TSTP //这里话的含义是忽略ctrl + c中断信号

 

exec /etc/init.d/rc S    //运行/etc/init.d/rc    并加一个参数S

 

 

下面进入rc文件分析

1 # rc        This file is responsible for starting/stopping                      2 #       services when the runlevel changes.                                     3 4 //在文件一开始,就说明了这个文件是当运行级别改变就要更改相关的服务

 

//设置终端,将 CR 字符映射为 NL 字符,避免阶梯效应  stty onlcr 0>&1

 

 

复制代码
  # Get first argument. Set new runlevel to this argument.                        [ "$1" != "" ] && runlevel=$1                                                   if [ "$runlevel" = "" ]                                                         then                                                                              echo "Usage: $0 <runlevel>" >&2                                                 exit 1                                                                        fi                                                                            //如果没有获取到运行级别就退出
复制代码

 

previous=$PREVLEVEL  [ "$previous" = "" ] && previous=N    //传入参数是S的话,则$runleve=S $previous=N  export runlevel previous

 

复制代码
//若$runlevel=“S”,即检查rcS.d是否为目录。  if [ -d /etc/rc$runlevel.d ]  then    //rcS.d是目录    PROGRESS_STATE=0    //Split the remaining portion of the progress bar into thirds    progress_size=$(((100 - $PROGRESS_STATE) / 3))//progress_size = 100/3 =33    case "$runlevel" in//runlevel=S        0|6)            first_step=-100            progress_size=100            step_change=1            ;;     S)            //Begin where the initramfs left off and use 2/3of the remaining space            first_step=$PROGRESS_STATE ///progress_size = 100/3 =33            progress_size=$(($progress_size * 2))//progress_size=66            step_change=1            ;;        *)            //Begin where rcS left off and use the final 1/3 ofthe space (by leaving progress_size unchanged)            first_step=$(($progress_size * 2 + $PROGRESS_STATE))            step_change=1            ;;    esac
复制代码

 

 

复制代码
num_steps=0    for s in /etc/rc$runlevel.d/[SK]*; //s取/etc/rcS.d目录下以S或K开头的文件名    do        //这句话的含义去掉变量s中所有的/etc/rcS.d/S??的部分        //例:s=/etc/rc$runlevel.d/S10checkroot,那么去掉/etc/rc$runlevel.d/K??部分后,s为checkroot    case "${s##/etc/rc$runlevel.d/S??}" in        gdm|xdm|kdm|reboot|halt)//若s剩下的文件名中为这五个则跳出for语句            break            ;;    esac    num_steps=$(($num_steps + 1))//num_steps递加,表示查找到此目录下/etc/rcS.d有多少个脚本  done//for语句结束
复制代码

 

复制代码
        step=0     # First, run the KILL scripts.       首先运行KILL          if [ $previous != N ]                                    //由于previous = N ,所以一下不执行                       then                                                                                for i in /etc/rc$runlevel.d/K[0-9][0-9]*                                        do                                                                                  # Check if the script is there.                                                 [ ! -f $i ] && continue                                                                                                                                         # Stop the service.                                                             startup $i stop                                                             done                                                                        fi                                                                          
复制代码

 

 

复制代码
    # Now run the START scripts for this runlevel.                                  for i in /etc/rc$runlevel.d/S*                                                  do                                                                                  [ ! -f $i ] && continue                                                                                                                                         if [ $previous != N ] && [ $previous != S ]     //由于previous为N所以跳过                    then                                                                                #                                                                               # Find start script in previous runlevel and                                    # stop script in this runlevel.                                                 #                                                                               suffix=${i#/etc/rc$runlevel.d/S[0-9][0-9]}                                      stop=/etc/rc$runlevel.d/K[0-9][0-9]$suffix                                      previous_start=/etc/rc$previous.d/S[0-9][0-9]$suffix                            #                                                                               # If there is a start script in the previous level                              # and _no_ stop script in this level, we don't                                  # have to re-start the service.                                                 #                                                                               [ -f $previous_start ] && [ ! -f $stop ] && continue                        fi                                                                              case "$runlevel" in                                                                 0|6)                                                                                startup $i stop                                                                 ;;                                                *)                                                                                  startup $i start            //然后启动了相关的服务   运行在文件前面定义的 startup 函数                                   ;;                                                                      esac                                                                        done                                                                          fi                                                                            
复制代码

 

startup函数有如下定义:

复制代码
#                                                                               # Start script or program.                                                      #                                                                               startup() {                                                                       # Handle verbosity                                                              [ "$VERBOSE" = very ] && echo "INIT: Running $@..."                                                                                                             case "$1" in             //判断第一个参数,如果是一个脚本,执行第一个                    *.sh)                                                                               # Source shell script for speed.                                                (                                                                                   trap - INT QUIT TSTP                                                            scriptname=$1                                                                   shift                               //shift 命令每执行一次,变量的个数($#)减一,而变                    量值提前一位                                          . $scriptname                  //在执行第一个参数                                             )                                                                               ;;                                                                          *)                            //如果不是一个脚本,那么认为是一个服务                                                      "$@"                                                                            ;;                                                                        esac                                                                            startup_progress             //再运行前一个函数                                                  }                                                                                                               
复制代码

 

startup_progress 函数详解

复制代码
startup_progress() {                                                                step=$(($step + $step_change))                                                  if [ "$num_steps" != "0" ]; then                                                    progress=$((($step * $progress_size / $num_steps) + $first_step))           else                                                                                progress=$progress_size                                                     fi                                                                              #echo "PROGRESS is $progress $runlevel $first_step + ($step of $num_steps) $step_change $progress_size"    #if type psplash-write >/dev/null 2>&1; then                                    #    TMPDIR=/mnt/.psplash psplash-write "PROGRESS $progress" || true            #fi                                                                             if [ -e /mnt/.psplash/psplash_fifo ]; then                                          echo "PROGRESS $progress" > /mnt/.psplash/psplash_fifo                      fi                                                                              echo progress over...                                                       }                                                                                                                                               
复制代码

 

 

 

最后,执行qt.sh

复制代码
#Uncomment to cause psplash to exit manually, otherwise it exits when it sees a VC switchif [ "x$runlevel" != "xS" ] && [ ! -x /etc/init.d/xserver-nodm ]; then              . /etc/init.d/qt.sh                                                         #    if type psplash-write >/dev/null 2>&1; then                                #        TMPDIR=/mnt/.psplash psplash-write "QUIT" || true                      #       umount /mnt/.psplash                                                    #    fi                                                                         fi                                                                           
复制代码

 

 

参考:

http://www.cnblogs.com/cnland/archive/2013/03/26/2981967.html

https://wiki.archlinux.org/index.php/Arch_boot_process_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#Init_.E6.B5.81.E7.A8.8B

http://blog.chinaunix.net/uid-17188120-id-4073497.html


init 进程是内核初始化完成后启动的第一个进程。这个进程会创建相应的系统服务,从而使系统正常工作。
init进程会首先读配置文件/etc/inittab,根据相应的命令,执行相应的脚本,启动相应的服务。
下面介绍下inittab的规则:
inittab脚本以#开头的行为注释。
inittab脚本的命令格式如下:
label:runlevel:action:process。
详细解释如下。
1.label
登记项标志符,是一个任意指定的、4个字符以内的序列标号,在本文件内必须唯一。
label是1到4个字符的标签,用来标示输入的值。但有一些系统只支持2个字符的标签。
该标签可以是任意字符构成的字符串。另外LINUX预先定义一些保留的标签,介绍如下:
id 用来定义缺省的init运行的级别,该标签定义的语句不执行,只是说明系统默认的运行级别,例如:
id:5:initdefault:
就是定义默认运行runlevels=5 ,也就是xwindow
si 是系统初始化的进程
ln 其中的n从1~6,指明该进程可以使用的runlevel的级别
ud 是升级进程
ca 指明当按下Ctrl+Alt+Del是运行的进程
pf 指当UPS表明断电时运行的进程
pr 是在系统真正关闭之前,UPS发出电源恢复的信号时需要运行的进程
x 是将系统转入X终端时需要运行的进程


2.runlevels
系统运行级,即执行登记项的init级别。用于指定相应的登记项适用于哪一个运行级,即在哪一个运行级中被处理。
如果该字段为空,那么相应的登记项将适用于所有的运行级。
在该字段中,可以同时指定一个或多个运行级,其中各运行级分别以数字0, 1, 2, 3, 4, 5, 6且无须对其进行分隔。
0 Halt,关闭系统.
1 单用户,在grub启动时加上为kernel加上参数single即可进入此运行等级
2 无网络多用户模式.
3 有网络多用户模式.
4 一般不被系统使用,用户可以用来设计自己的系统状态.
5 xwindow模式
6 reboot重启系统
这里需要注意的是,系统的默认运行级别一定不能设置为0或者6.


3.action
表示进入对应的runlevel时,init应该运行process字段的命令的方式,有效的action值如下。
boot:只有在引导过程中,才执行该进程,但不等待该进程的结束。当该进程死亡时,也不重新启动该进程。
bootwait:只有在引导过程中,才执行该进程,并等待进程的结束。当该进程死亡时,也不重新启动该进程。
     实际上,只有在系统被引导后,并从单用户模式进入多用户模式时,这些登记项才被处理;
     如果系统的默认运行级设置为2-5(即多用户模式),那么这些登记项在系统引导后将马上被处理。
initdefault:指定系统的默认运行级。系统启动时,init将首先查找该登记项,如果存在,init将依据此决定系统最初要进入的运行级。
off:如果相应的进程正在运行,那么就发出一个告警信号,等待20秒后,再通过关闭信号强行终止该进程。如果相应的进程并不存在,那么就忽略该登记项。
once:启动相应的进程,但不等待该进程结束便继续处理/etc/inittab文件中的下一个登记项;当该进程终止时,init也不重新启动该进程。
   在从一个运行级进入另一个运行级时,如果相应的进程仍然在运行,那么init就不重新启动该进程。
powerfail:只在init接收到电源失败信号时,才执行该进程,但不等待该进程结束。
powerwait:只在init接收到电源失败信号时,才执行该进程,并在继续对/etc/inittab文件进行任何处理前等待该进程结束。
respawn:如果相应的进程还不存在,那么init就启动该进程,同时不等待该进程的结束就继续扫描/etc/inittab文件;当该进程终止时,init将重新启动该进程。
    如果相应的进程已经存在,那么init将忽略该登记项并继续扫描/etc/inittab文件。
sysinit:只有在启动或重新启动系统并首先进入单用户模式时,init才执行这些登记项。
     而在系统从运行级1~6进入单用户模式时,init并不执行这些登记项。"action"字段为"sysinit"的登记项在"run_level"字段不指定任何运行级。
wait:启动进程并等待其结束,然后再处理/etc/inittab文件中的下一个登记项。
ctrlaltdel:用户在控制台键盘上按下Ctrl+Alt+Del组合键时,允许init重新启动系统。


4.process
具体应该执行的命令。


下面以imx6q的linux为例子说明下init的执行流程。下面先看看imx6q的inittab


# /etc/inittab: init(8) configuration.
# $Id: inittab,v 1.91 2002/01/25 13:35:21 miquels Exp $


# The default runlevel.
id:5:initdefault:


# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS


# What to do in single-user mode.
~~:S:wait:/sbin/sulogin


# /etc/init.d executes the S and K scripts upon change
# of runlevel.
#
# Runlevel 0 is halt.
# Runlevel 1 is single-user.
# Runlevels 2-5 are multi-user.
# Runlevel 6 is reboot.


l0:0:wait:/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
# Normally not reached, but fallthrough in case of emergency.
z6:6:respawn:/sbin/sulogin
mxc::respawn:/etc/init.d/rc_mxc.S
#mxc0:12345:respawn:/sbin/getty 115200 ttymxc0
# /sbin/getty invocations for the runlevels.
#
# The "id" field MUST be the same as the last
# characters of the device (after "tty").
#
# Format:
# <id>:<runlevels>:<action>:<process>
#


1:2345:respawn:/sbin/getty 38400 tty1


gpu::sysinit:/etc/init.d/rc_gpu.S


下面是详细说明:
首先第一句 id:5:initdefault: 说明默认的运行级别是5 也就是默认是xserver的图形界面
然后执行si::sysinit:/etc/init.d/rcS
也就是运行这个脚本/etc/init.d/rcS,这个脚本实际上就是执行/etc/rcS.d目录下以S+数字开头的脚本,按照数字的顺序依次执行。
然后执行gpu::sysinit:/etc/init.d/rc_gpu.S,运行/etc/init.d/rc_gpu.S这个脚本。
之后执行~~:S:wait:/sbin/sulogin,运行/sbin/sulogin这个可执行程序。
然后进入运行级别5
执行l5:5:wait:/etc/init.d/rc 5 ,这个脚本实际上就是执行/etc/rc5.d目录下以S+数字开头的脚本,按照数字的顺序依次执行。
最后执行mxc::respawn:/etc/init.d/rc_mxc.S,运行/etc/init.d/rc_mxc.S这个脚本


通过上面的说明,我们就可以清楚的知道init启动脚本的顺序了
1 在 /etc/rcS.d目录下找到以S+数字开头的脚本,按照数字的顺序依次执行。
2 运行/etc/init.d/rc_gpu.S这个脚本。
3 运行/sbin/sulogin
4 在 /etc/rc5.d目录下找到以S+数字开头的脚本,按照数字的顺序依次执行。
5 运行/etc/init.d/rc_mxc.S这个脚本


最后介绍下在yocto下如何在rc5.d和rcS.d目录下增加自己的脚本,或者修改已有脚本的顺序。
如果要增加一个脚本,我们可以在对应的bb文件中增加下面的语句:
inherit update-rc.d


INITSCRIPT_NAME = "name"
INITSCRIPT_PARAMS = "start xx level . "
name 是我们的脚本的文件名,这个脚本一定要放在/etc/init.d目录下
xx 是S后面的数字,level是运行级别,可以是0-5或者S


例如我们要在rcS.d目录下创建一个S01test.sh的脚本。
那么我们先将test.sh安装到/etc/init.d
然后在bb文件增加下面的语句
inherit update-rc.d


INITSCRIPT_NAME = "test.sh"
INITSCRIPT_PARAMS = "start 01 S . "


重新编译后烧录后启动,我们就可以在/etc/rcS.d会有一个S01test.sh的链接文件,指向/etc/init.d/test.sh
如果我们在脚本中加上echo "test.sh run"
我们启动机器就可以看到test.sh run的串口信息了。 以上就是linux init进程的全文介绍,希望对您学习和使用linux系统开发有所帮助.



原创粉丝点击