Linux 启动过程分析 (SysV init启动模式)


      本篇主要分析传统的Linux启动方式  SysV init启动模式。(注:当前Linux发行版大多采用Systemd 启动模式来替代传统的 SysV init启动模式。)


     分析如下:

     1.内核初始后,创建Init进程。 init 进程是所有进程的起点,一般对应执行文件为/sbin/init。

      2. /sbin/init 读取/etc/initab 文件,根据里面内容进行初始化。

        参考文献 /etc/inittab详解: http://blog.csdn.net/newnewman80/article/details/8133797
     

# $Id: inittab,v 1.91 2002/01/25 13:35:21 miquels Exp $
#/etc/initab内容
# The default runlevel. 缺省运行级别为5
#
#runlevel用来表示在init进程结束之后的系统状态,在系统的硬件中没有固定的信息来表示runlevel
#Runlevel 0是让init关闭所有进程并终止系统。
#Runlevel 1是用来将系统转到单用户模式
#Runlevel 2是允许系统进入多用户的模式,但并不支持文件共享,这种模式非常少应用。
#Runlevel 3是最常用的运行模式,主要用来提供真正的多用户模式,也是多数服务器的缺省模式。
#Runlevel 4一般不被系统使用,用户能设计自己的系统状态并将其应用到runlevel 4阶段,尽管非常少使用,但使用该系统能#实现一些特定的登录请求。
#Runlevel 5是将系统初始化为专用的X Window终端。对功能强大的Linux系统来说,这并不是好的选择,但用户如果需要这样,也能通过在runlevel启动来实现该方案。
#Runlevel 6是关闭所有运行的进程并重新启动系统

id:5:initdefault:

# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
#首次执行的脚本,si 是系统初始化的进程
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
#S0:12345:respawn:/sbin/getty -L 115200 ttyS0
# /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
2:2345:respawn:/sbin/getty 38400 tty2
3:2345:respawn:/sbin/getty 38400 tty3
4:2345:respawn:/sbin/getty 38400 tty4
5:2345:respawn:/sbin/getty 38400 tty5
6:2345:respawn:/sbin/getty 38400 tty6


3. 执行/etc/init.d/rcS 脚本

   #!/bin/sh
    #
    # rcS           Call all S??* scripts in /etc/rcS.d in
    #               numerical/alphabetical order.
    #
    # Version:      @(#)/etc/init.d/rcS  2.76  19-Apr-1999  [email protected]
    #


    PATH=/sbin:/bin:/usr/sbin:/usr/bin
    runlevel=S
    prevlevel=N
    umask 022
    export PATH runlevel prevlevel


    #    Make sure proc is mounted
    #
    [ -d "/proc/1" ] || mount /proc


    #
    #       Source defaults.
    # 设置环境变量
    . /etc/default/rcS


    #
    #       Trap CTRL-C &c only in this shell so we can interrupt subprocesses.
    #
    trap ":" INT QUIT TSTP


    #
    #   Call all parts in order.
    # 
    exec /etc/init.d/rc S

    4.  /etc/init.d/rc S 的执行

  
  root@genericx86:~# cat /etc/init.d/rc

#!/bin/sh
#
# rc     This file is responsible for starting/stopping
#        services when the runlevel changes.
#
#               Optimization feature:
#               A startup script is _not_ run when the service was
#               running in the previous runlevel and it wasn't stopped
#               in the runlevel transition (most Debian services don't
#               have K?? links in rc{1,2,3,4,5} )
#
# Author:       Miquel van Smoorenburg <[email protected]>
#               Bruce Perens <[email protected]>
#
# Version:      @(#)rc  2.78  07-Nov-1999  [email protected]
#
#设置环境变量
. /etc/default/rcS
export VERBOSE

#设置启动进程,计算启动步骤
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
}
#
# 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
                        . $scriptname
                )
                ;;
        *)
                "$@"
                ;;
  esac
  startup_progress
}
  # Ignore CTRL-C only in this shell, so we can interrupt subprocesses.
  trap ":" INT QUIT TSTP
  # Set onlcr to avoid staircase effect.
  stty onlcr 0>&1

 # Limit stack size for startup scripts
  #设置启动脚本的栈大小
 [ "$STACK_SIZE" == "" ] || ulimit -S -s $STACK_SIZE
  # Now find out what the current and what the previous runlevel are.
  runlevel=$RUNLEVEL
  # 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
  export runlevel previous

 echo "runlevel...." $runlevel
  echo "previous....." $previous

 # Is there an rc directory for this new runlevel?
  if [ -d /etc/rc$runlevel.d ]
  then
        # Find out where in the progress bar the initramfs got to.
        PROGRESS_STATE=0
        #if [ -f /dev/.initramfs/progress_state ]; then
        #    . /dev/.initramfs/progress_state
        #fi
        # Split the remaining portion of the progress bar into thirds
        progress_size=$(((100 - $PROGRESS_STATE) / 3))
        case "$runlevel" in
                0|6)
                        # Count down from -100 to 0 and use the entire bar
                        first_step=-100
                        progress_size=100
                        step_change=1
                        ;;
                S) 
                        # Begin where the initramfs left off and use 2/3
                        # of the remaining space
                        first_step=$PROGRESS_STATE
                        progress_size=$(($progress_size * 2))
                        step_change=1
                        ;;
                *)
                        # Begin where rcS left off and use the final 1/3 of
                        # the space (by leaving progress_size unchanged)
                        first_step=$(($progress_size * 2 + $PROGRESS_STATE))
                        step_change=1
                        ;;
        esac
        num_steps=0
       #计算步伐num_steps
        for s in /etc/rc$runlevel.d/[SK]*; do
            case "${s##/etc/rc$runlevel.d/S??}" in
                gdm|xdm|kdm|reboot|halt)
                    break
                    ;;
            esac
            num_steps=$(($num_steps + 1))
        done
     
        step=0
        # First, run the KILL scripts.
        # 如果前一个状态不为N,执行K开头脚本,关闭或杀死脚本服务
        if [ $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
        #现在开始运行对应运行级别的启动脚本,S开头
        #第一次的运行级别为:runlevel为S,previous为N,执行 /etc/rcS.d/S*
        #第二次的运行级别: runlevel为3或5,previous为S
        #关机时,第三次的运行级别:runlevel为6,previous为5或3
        # 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 ]
                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
                                ;;
                esac
        done
  fi
#Uncomment to cause psplash to exit manually, otherwise it exits when it sees a VC switch
if [ "x$runlevel" != "xS" ] && [ ! -x /etc/rc${runlevel}.d/S??xserver-nodm ]; then
    if type psplash-write >/dev/null 2>&1; then
        TMPDIR=/mnt/.psplash psplash-write "QUIT" || true
        umount -l /mnt/.psplash
    fi
fi


     通过分析/etc/init.d/rc 脚本,接下来运行 /etc/rcS.d/中S开头的脚本,如下所示。

     root@genericx86:/etc/rcS.d# ls
     S00fbsetup      S04udev          S29read-only-rootfs-hook.sh  S38dmesg.sh
     S01keymap.sh    S05modutils.sh   S30urandom                   S39alsa-state
     S02banner.sh    S05psplash.sh    S36udev-cache                S39hostname.sh
     S02sysfs.sh     S06checkroot.sh  S37populate-volatile.sh      S55bootmisc.sh
     S03mountall.sh  S07bootlogd      S38devpts.sh
     root@genericx86:/etc/rcS.d#

         其中:S04udev 脚本的start 入口 动态加载系统驱动

   5. 执行/etc/initab 中运行级别对应的脚本

    接下来执行  l5:5:wait:/etc/init.d/rc 5 对应级别的脚本,即/etc/rc5.d 文件夹下的脚本。

root@genericx86:/etc/rc5.d# ls  

S01networking   S10dropbear     S20hwclock.sh    S64neard       S02dbus-1              S12rpcbind               S20syslog           S99rmnologin.sh   S05connman       S15mountnfs.sh  S21avahi-daemon  S99stop-        bootlogd  S09xserver-nodm  S20acpid        S22ofono  

root@genericx86:/etc/rc5.d#

 

其中 ,S01 networking 用于设置和配置网络接口

           09xserver-nodm用于启动XServer系统,其执行步骤如下:

   (1)执行/etc/X11/Xserver的脚本;

   (2)/etc/X11/Xserver 接着执行 exec xinit /etc/X11/Xsession -- $XSERVER $DISPLAY $ARGS $*  

       其中,xinit 命令首先执行Xserver 命令即$XSERVER $DISPLAY $ARGS $*,接着执行X 客户端命令即 /etc/X11/Xsession脚本;            

    (3)/etc/X11/Xsession 接着执行/etc/X11/Xsession.d 下的脚本 ,其中,90XWindowManager.sh 为窗口管理器脚 本,其执行 /usr/bin/x-session-manager脚本;                   

   (4)/usr/bin/x-session-manager脚本,接着执行/usr/local/AutoStart/StartSys、OpenBox-session 脚本等。

S

 /usr/bin/x-session-manager脚本如下

#!/bin/sh
#
# Very simple session manager for matchbox tools
#
#if [ -e /usr/bin/openbox-session]
#then
exec /usr/local/AutoStart/StartSys &
exec openbox-session
#fi
# Uncomment below to enable parsing of debian menu entrys
# export MB_USE_DEB_MENUS=1
if [ -e $HOME/.matchbox/session ]
then
exec $HOME/.matchbox/session
fi
if [ -e /etc/matchbox/session ]
then
exec /etc/matchbox/session
fi
# Default files to run if $HOME/.matchbox/session or /etc/matchbox/session
# dont exist.
matchbox-desktop &
matchbox-panel &
exec matchbox-window-manager $@   

  脚本S09xserver-nodm内容如下:

  root@genericx86:/etc/rc5.d# cat S09xserver-nodm

#!/bin/sh
#
### BEGIN INIT INFO
# Provides: xserver
# Required-Start: $local_fs $remote_fs dbus
# Required-Stop: $local_fs $remote_fs
# Default-Start:     5
# Default-Stop:      0 1 2 3 6
### END INIT INFO
#根据进程名杀死进程
killproc() {            # kill the named process(es)
        pid=`/bin/pidof $1`
        [ "$pid" != "" ] && kill $pid
}
#读取传入的内核的命令行参数
read CMDLINE < /proc/cmdline
for x in $CMDLINE; do
        case $x in
#禁止启动X11
        x11=false)
                echo "X Server disabled"
                exit 0;
                ;;
        esac
done

case "$1" in
#启动分支
  start)
       . /etc/profile
       username=root
       echo "Starting Xserver"
       if [ -f /etc/X11/Xusername ]; then
           username=`cat /etc/X11/Xusername`
           # setting for rootless X
           chmod o+w /var/log
           chmod g+r /dev/tty[0-3]
           # hidraw device is probably needed
           if [ -e /dev/hidraw0 ]; then
               chmod o+rw /dev/hidraw*
           fi
       fi
       # Using su rather than sudo as latest 1.8.1 cause failure [YOCTO #1211]
       su -l -c '/etc/X11/Xserver&' $username
       # Wait for the desktop to say its finished loading
       # before loading the rest of the system
       # dbus-wait org.matchbox_project.desktop Loaded
  ;;
  stop)
        echo "Stopping XServer"
        killproc xinit
  ;;
  restart)
        $0 stop
        sleep 1
        $0 start
  ;;
  *)
        echo "usage: $0 { start | stop | restart }"
  ;;
esac
exit 0





猜你喜欢

转载自blog.csdn.net/yellowblue2/article/details/78745172
今日推荐