zoukankan      html  css  js  c++  java
  • slackware启动脚本详解

    当kernel启动成功之后

    我们知道,kernel成功启动之后,在init/main.c中调用execve执行程序/sbin/initinit进程被称为初始化进程,因为它负责系统的启动。
    我以我的slackware10为例(不光因为它是我用的套件,而且启动脚本十分清晰明了),讲一讲启动的具体过程:

    (史前时期)loadlin,grub,lilo把内核参数传给kernel,如(BOOT_IMAGE=Linux ro root=307),,存储在/proc/cmdline中。
    内核启动成功后,把它的参数传给1号进程(init),因为也许有一些参数应被用户程序解释。

    init进程将从上到下的读取/etc/inittab文件,只要状态符合当前运行级就会去执行脚本。
    inittab由几行组成,每行被三个冒号分隔成四个部分,每个部分具有不同的含义。格式如下:
    行标识符:状态:动作:命令

    行标识符是你的运行级脚本的名字,不能使用重复的行标识符。
    状态是表示运行级脚本何时应该执行的数字。状态由0,1,2,3,4,5,6和S一个或多个数字字母组成。如果状态为空,就是系统启动必须执行的脚本。
    下面是slackware的状态定义:
    0 = halt
    1 = single user mode
    2 = unused (but configured the same as runlevel 3)
    3 = multiuser mode (default Slackware runlevel)
    4 = X11 with KDM/GDM/XDM (session managers)
    5 = unused (but configured the same as runlevel 3)
    6 = reboot
    S同状态1相同。

    动作有once, wait, respawn, sysinit, crtlaltdel, initdefault组成,说明了init执行脚本的方式。
    once:init在进入后只执行一次。init不等待命令的结束。
    wait:和once不同的是,init等待命令的结束。
    respawn:命令结束后会被重起。
    sysinit:init最先执行的运行级脚本,状态被忽略。说穿了就是无论何是都优先执行的脚本,应指向系统初始化脚本。
    ctrlaltdel:当“三指禅”被按下时,该运行级被启动,一般是指向重启脚本。
    initdefault:指定系统启动时的缺省运行级。在sysinit后执行。

    对于slackware的inittab:
    id:3:initdefault:
    id指出缺省为多用户字符界面(3),不要把它设置成0或6!注意没有命令。

    si:S:sysinit:/etc/rc.d/rc.S
    si指出系统初始化运行级,其中S等同于状态1,它指向/etc/rc.d/rc.S,也就是说init第一个去执行的shell脚本。

    rc:2345:wait:/etc/rc.d/rc.M
    rc指出多用户启动运行级,当状态为2,3,4,5时被执行,init等待命令的结束,这也是为什么启动时没有shell可用的原因

    c1:1235:respawn:/sbin/agetty 38400 tty1 linux
    c1指出控制台1,当它被杀死时init将重启它。它打开一个终端tty1供你使用。

    x1:4:wait:/etc/rc.d/rc.4
    x1指出多用户GUI运行级,指向脚本rc.4。

    rc.S(系统初始化运行级脚本)做的事:slackware的启动过程(2)之rc.S

     

    上节我们介绍了init进程执行的inittab脚本,这节我们分析在inittab中出现的 /etc/rc.d目录中的 “rc.X”脚本.

    首先是系统初始化时执行的“rc.S”脚本,inittab中的对应行为si:S:sysinit:/etc/rc.d/rc.S

    rc.S文件内容有380行之多,我们尽可能的逐行命令一一解释.
    1、设置path环境变量
    PATH=/sbin:/usr/sbin:/bin:/usr/bin
    2、挂载proc文件系统

     /sbin/mount -v proc /proc -n -t proc 2> /dev/null
    proc文件系统是一个伪文件系统,它只存在于内存当中,它以文件系统的方式为访问系统内核数据的操作提供接口.例如我们查看cpu信息时经常使用”cat /proc/cpuinfo“ 命令.
    3、挂载sysfs文件系统

    if [ -d /sys ]; then
    if grep -wq sysfs /proc/filesystems ; then
    if ! grep -wq sysfs /proc/mounts ; then
    /sbin/mount -v sysfs /sys -n -t sysfs
    fi
    fi
    fi
    sysfs文件系统是用来对系统的设备进行管理的,它把实际连接到系统上的设备和总线组织成一个分级的文件,用户空间的程序同样可以利用这些信息以实现和内核的交互.这种文件系统只存在于2.6内核中.
    4、为2.6内核启动执行udev脚本.

    if grep -wq sysfs /proc/mounts && grep -q tmpfs /proc/filesystems ; then
    if ! grep -wq nohotplug /proc/cmdline ; then
    if [ -x /etc/rc.d/rc.udev ]; then
    /bin/sh /etc/rc.d/rc.udev start
    fi
    fi
    fi
    udev是一种工具,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等.设备文件通常放在/dev目录下.使用udev后,在/dev目录下就只包含系统中真正存在的设备.

    #安装proc文件系统。
    #决定是否有需要使用Hotplug自动检测硬件。
    #启动devfsd。
    #安装devfs。
    #为2.6内核启动udev。
    #打开交换分区,其中要读取/etc/fstab中获取分区信息。
    #检查根分区是否为只读,因为只有只读时才能检查硬盘。
    #如有需要检查根分区。
    #安装sysfs到/proc/sys。
    #设置硬件时钟。
    #配置isa设备。
    #执行/etc/rc.d/rc.modules (这个文件将剩下的内核模块装入内核,稍后再讲)。
    #初始化lvm卷,不要问我为什么。
    #检查非根分区。
    #安装本地硬盘分区。
    #删除临时文件。
    #initrd被安装在/initrd中,它用来在内核启动第一时间载入一些内核模块和必需程序(如fsck)等。现在卸载它。
    #创建utmp。
    #如果你是用的zipslack(一个工作在vfat上的linux套件),配置umsdos。
    #把Linux 2.4.27写入你的mtod文件
    #执行rc.sysvinit。(不像rh,slackware是一个“叛徒”。它的init脚本结构不像大多数linux套件(基于SVR4),而像BSD,所以只是一个假文件。)
    #执行rc.serical。(串口)
    #安装随机数种子

    rc.modules:
    #决定内核版本,到目录”/lib/modules/你的内核版本号/“去寻找模块
    #更新内核模块依赖关系。
    #装入APM高级管理,它被注释掉了,建议你启用它以正常关机。
    #一大堆被注释掉了的硬件模块,如果hotplug找不到你的硬件就到这里来。

    rc.S执行完成后,slackware将执行缺省的运行级(3),也就是先执行rc.M(多用户进程级)。
    #设置黑屏时间。
    #设置主机名,主机名存储在/etc/HOSTNAME里,缺省为darkstar.example.net。
    #设置dmesg缓冲区的大小。越多越好。
    #执行rc.syslog,打开syslog和klogd。
    #执行rc.pcmcia,初始化PCMCIA卡,我不懂。
    #执行rc.inet1,设置网络。重点。
    #执行rc.hotplug,即插即用。
    #执行rc.inet2,网络守护进程。重点。
    #把所有的锁文件删掉。
    #把黑洞设备和临时目录设成777。
    #运行ldconfig,更新共享库,我喜欢关掉。
    #更新X字体缓存,关掉吧。不过安了新字体后自己要手动运行一次”fc-cache"而已。
    #执行rc.CUPS,UNIX打印守护进程。
    #打开appletalk。关了吧。
    #打开用户限额。请看/usr/doc/Linux-HowTOs/Quota。
    #执行rc.acpid。高级能源管理。
    #执行rc.alsa。alsa声音系统。
    #执行rc.font。用户自己的字体。
    #执行rc.keymap。用户自己的keymap。
    #把你的一大堆网络standalone进程打开。
    #执行rc.gpm。字符界面上用鼠标。
    #又执行rc.sysinit一次。BUG?
    #执行rc.local。最后,执行用户你的自己的配置文件。

    rc.inet1:
    #读入另一个脚本文件rc.inet1.conf的变量,里面有网络的基本配置。
    #打开lo环回接口。
    #打开eth0 eth1 eth3 eth4, 请看eth_up函数,很长。
    #如有需要使用无线网卡。
    #如有需要使用DHCP。

    rc.inet2
    #启动rc.portmap,准备安装NFS文件系统
    #安装所有smb文件系统
    #执行rc.firewall,防火墙脚本。
    #执行rc.ip_forward,IP转发脚本,如果你要共享上网,改它。
    #一大堆网络服务程序(NFS,BIND,SSH,INETD)

    rc.M执行完了,init按照inittab的配置打开终端,它创建若干个agetty进程,这些进程通过系统调用执行login程序,用户密码验证成功后,再执行shell。一天的工作开始了。在这之后,init除了监视运行级的改变外,还干一些副业:收养孤儿进程。

    如果你是用的运行级4(多用户GUI运行级),init就会去执行rc.4,并打开gdm或kdm(Gnome和Kde的登录界面)。

    经验:
    1。init脚本里面请使用绝对路径,且限制PATH。
    2。可以适当增减脚本的内容来充实功能或加快启动速度。不过你一旦挂了的话,请用init=/bin/sh或single模式开机。
    3。就像看Linux内核源代码能增强C语言功底一样,看init脚本能提高Bash功底。
    4。一个清晰明了的结构胜过一切描述,这也是我选择Slackware的原因。
    5。如果说Windows是所见即所得的操作系统,那么说Unix/Linux是所要即所得的操作系统。(因为Unix/Linux在能彻底定制系统,要什么有什么)

     
     
    Linux系统脚本分析之rc.sysinit
    2008-09-11 08:51:53
    #!/bin/bash
    #
    # /etc/rc.d/rc.sysinit - run once at boot time

    #
     
    # Rerun ourselves through initlog                                                // 通过 /sbin/initlog 命令重新运行自己
    if [ -z "$IN_INITLOG" -a -x /sbin/initlog ]; then                            
    // 条件是 :如果 IN_INITLOG 变量的值不为空,且 /sbin/initlog 可执行
        exec /sbin/initlog -r /etc/rc.d/rc.sysinit                                
    // 调用 exec /sbin/initlog ,-r 是表示运行某个程序
    fi
     
    ######################################################################################################################################################
     
    HOSTNAME=`/bin/hostname`                            # 取得主机名
    HOSTTYPE=`uname -m`                                    
    # 取得主机类型
    unamer=`uname -r`                                          # 取得内核的 release 版本(例如 2.4.9.30-8)

    eval version=`echo $unamer | awk -F '.' '{ print "(" $1 " " $2 ")" }'`            # 取得版本号
     
    if [ -f /etc/sysconfig/network ]; then                # 如果存在 /etc/sysconfig/network ,则执行该文件。
        . /etc/sysconfig/network                             # network 文件主要控制是否启用网络、默认网关、主机名
    fi
    if [ -z "$HOSTNAME" -o "$HOSTNAME" = "(none)" ]; then            # 如果执行 network 文件后 HOSTNAME 为空或者为 "(none)" ,
        HOSTNAME=localhost                                                        
    # 则将主机名设置为 "localhost"
    fi
     
     
    # Mount /proc and /sys (done here so volume labels can work with fsck)        # 接下来是挂载 /proc 和 /sys ,这样 fsck 才能使用卷标
    mount -n -t proc /proc /proc                                                                      #  -n 表示不写 /etc/mtab ,这在 /etc 所在的文件系统为只读时用。因为此时的/还是只读
    [ -d /proc/bus/usb ] && mount -n -t usbfs /proc/bus/usb /proc/bus/usb        # 如果存在 /proc/bus/usb 目录则把 /proc/bus/usb 以 usbfs 挂载到 /proc/bus/usb 下
    mount -n -t sysfs /sys /sys >/dev/null 2>&1                                                    # 接下来就是把 /sys 目录以 sysfs 格式挂载到 /sys 目录下
     
    ########################################################################################################################################################
     
    . /etc/init.d/functions             # 执行 /etc/init.d/functions 文件,该文件提供了很多有用的函数,具体见 “functions 脚本提供的函数”一文
     
    ########################################################################################################################################################
     
    # Check SELinux status                                                       
    selinuxfs=`awk '/ selinuxfs / { print $2 }' /proc/mounts`        
    SELINUX=                                                                                                    
    if [ -n "$selinuxfs" ] && [ "`cat /proc/self/attr/current`" != "kernel" ]; then            
     if [ -r $selinuxfs/enforce ] ; then
      SELINUX=`cat $selinuxfs/enforce`
     else
      # assume enforcing if you can't read it
      SELINUX=1
     fi
    fi
     
    if [ -x /sbin/restorecon ] && LC_ALL=C fgrep -q " /dev " /proc/mounts ; then
     /sbin/restorecon  -R /dev 2>/dev/null
    fi
     
    disable_selinux() {
     echo "*** Warning -- SELinux is active"
     echo "*** Disabling security enforcement for system recovery."
     echo "*** Run 'setenforce 1' to reenable."
     echo "0" > $selinuxfs/enforce
    }
     
    relabel_selinux() {
        if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
     chvt 1
        fi
        echo "
             *** Warning -- SELinux relabel is required. ***
      *** Disabling security enforcement.         ***
      *** Relabeling could take a very long time, ***
      *** depending on file system size.          ***
      "
        echo "0" > $selinuxfs/enforce
        /sbin/fixfiles -F relabel > /dev/null 2>&1
        rm -f  /.autorelabel
        echo "*** Enabling security enforcement.         ***"
        echo $SELINUX > $selinuxfs/enforce
        if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
     chvt 8
        fi
    }
     
    ########################################################################################################################################################
     
    if [ "$HOSTTYPE" != "s390" -a "$HOSTTYPE" != "s390x" ]; then
      last=0
      for i in `LC_ALL=C grep '^[0-9].*respawn:/sbin/mingetty' /etc/inittab | sed 's/^.* tty\([0-9][0-9]*\).*/\1/g'`; do
            > /dev/tty$i
            last=$i
      done
      if [ $last -gt 0 ]; then
           > /dev/tty$((last+1))
           > /dev/tty$((last+2))
      fi
    fi
     
    ########################################################################################################################################################
     
    if [ "$CONSOLETYPE" = "vt" -a -x /sbin/setsysfont ]; then            # 下面是设置屏幕的默认字体。如果 CONSOLETYPE 变量的值为 vt 且 /sbin/setsysfont 命令可执行
       echo -n "Setting default font ($SYSFONT): "                            # 打印 "setting deafault font xxxx " ,默认字体应该是 xxxx
       /sbin/setsysfont                                                                   
    # 执行 /sbin/setsysfont
       if [ $? -eq 0 ]; then                                                                
    # 如果上述命令执行返回的 exit status 为 0
          success                                                                               
     # 则调用 success 函数(来自于 functions 脚本),记录一个成功的事件
       else
          failure                                                                                
    # 否则调用 failure 函数
       fi
       echo ; echo                                                
    fi
     
    ########################################################################################################################################################
     
    # Print a text banner.                                                                # 下面部分是打印 "welcome to xxxxx" 的标题栏

    echo -en $"\t\tWelcome to "                                                      # 打印 "<tab><tab>Welcom to" ,同时不换行
    if LC_ALL=C fgrep -q "Red Hat" /etc/redhat-release ; then           # 从 /etc/redhat-release 文件中找出含有 "Red Hat" 的行,如果找到
     [ "$BOOTUP" = "color" ] && echo -en "
    \\033[0;31m"                        # 则变量 BOOTUP 的值为 color ,并设置输出字体输出红色
     echo -en "Red Hat"                                                                    # 同时打印 "Red Hat" ,接下来打印发行版本(产品)
     [ "$BOOTUP" = "color" ] && echo -en "
    \\033[0;39m"                        #  如果变量 BOOTUP 的值为 color 则设置输出字体为白色
     PRODUCT=`sed "s/Red Hat \(.*\) release.*/\1/" /etc/redhat-release`    # 从 /etc/redhat-release 中找出含有 "Red Hat" 且后面若干字符,然后是 "release" 的行,并截取中间部分给 PRODUCT
                                                                                                        
    echo " $PRODUCT"                                                                            #  输出变量 PRODUCT 的值(白色)
    elif LC_ALL=C fgrep -q "Fedora" /etc/redhat-release ; then             # 如果/etc/redhat-release 中没有 Red Hat 字符串,但有 Fedora ,则执行类似过程
     [ "$BOOTUP" = "color" ] && echo -en "
    \\033[0;31m"
     echo -en "Fedora"
     [ "$BOOTUP" = "color" ] && echo -en "
    \\033[0;39m"
     PRODUCT=`sed "s/Fedora \(.*\) release.*/\1/" /etc/redhat-release`
     echo " $PRODUCT"
    else                                                                                            
    # 如果 /etc/redhat-release 中既没有含 Red Hat 也没有含 Fedora 的行,则
     PRODUCT=`sed "s/ release.*//g" /etc/redhat-release`                    # 找到含有 ‘release' 的行,并把它前面的部分输出,作为 PRODUCT 变量的值并输出
     echo "$PRODUCT"
    fi
     
    # 补充 :实际效果是 Red Hat 两个字是红色,其他都是白色
     
    ########################################################################################################################################################

    if [ "$PROMPT" != "no" ]; then                                                        # 如果变量 PROMPT 的值不为 "no" (表示允许交互启动),则
     echo -en $"\t\tPress 'I' to enter interactive startup."                           
     # 打印提示信息“Press I to enter interactive startup”,但此时按 I 还未起作用
     echo    
    fi
     
    ######################################################################################################################################################## 
    # 注释 :下面部分是设置输出到 console 的日志的详细级别
     
    # Fix console loglevel                                                                  # 设置控制台的日志级别
    if [ -n "$LOGLEVEL" ]; then                                                            # 如果 LOGLEVEL 变量的值不为空
     /bin/dmesg -n $LOGLEVEL                                                                # 则执行 dmesg ,设置打印到 consoel 的日志的级别为 $LOGLEVEL
    fi
     
    ######################################################################################################################################################## 
     
    # 注释 :下面部分是启动 udev 并加载 ide、scsi、network、audio 以及其他类型的设备的模块的部分
     
    [ -x /sbin/start_udev ] && /sbin/start_udev                                    # 如果 /sbin/start_udev 可执行,则执行它,会在屏幕上显示 Starting udev ... [OK]”
     
    # Only read this once.
    cmdline=$(cat /proc/cmdline)                                                        # 读取 /proc/cmdline ,这是内核启动的时的参数,赋予变量 cmdline
     
    # Initialize hardware                                                                     # 下面初始化硬件
    if [ -f /proc/sys/kernel/modprobe ]; then                                        # 如果 /proc/sys/kernel/modprobe 文件存在(该文件告诉内核用什么命令来加载模块),则
       if ! strstr "$cmdline" nomodules && [ -f /proc/modules ] ; then            # 如果 $cmdline 变量的值含有 nomodules ,且存在 /proc/modules 文件,则
           sysctl -w kernel.modprobe="/sbin/modprobe" >/dev/null 2>&1            # 使用 sysctl 设置 kernel.modprobe 为 /sbin/modprobe 命令
           sysctl -w kernel.hotplug="/sbin/hotplug" >/dev/null 2>&1                    # 使用 sysctl 设置  kernel.hotplug 为 /sbin/hotplug 命令
       else                                                                                           # 如果不存在 /proc/sys/kernel/modprobe 文件,则                             
           # We used to set this to NULL, but that causes 'failed to exec' messages"    
           sysctl -w kernel.modprobe="/bin/true" >/dev/null 2>&1                # 使用 sysctl 设置 kernel.modprobe 为 /bin/true
           sysctl -w kernel.hotplug="/bin/true" >/dev/null 2>&1                    # kernel.hotplug 也一样
       fi
    fi
     
    ######################################################################################################################################################## 
     
    # 注释 :下面开始真正的加载各种类型的设备的驱动
     
    # 首先是找出那些模块需要被加载,并把模块名按类分别放到 ide、scsi、network、audio、other 5个变量中
     
    echo -n $"Initializing hardware... "                                                    # 打印 "initalizing hardware.." (不换行),并开始初始化硬件
     
    ide=""                                                                                              # 下面这些变量都是为了存储的型号,格式为 " <module1> <module2> <module3> ..."           
    scsi=""
    network=""
    audio=""
    other=""
    eval `kmodule | while read devtype mod ; do                                # 从 kmodule 命令的输出一次读入两个变量 devtype (设备类型)和 mod(模块名)
     case "$devtype" in                                                                            # 根据 devtype 做出适当的选择 
      "IDE") ide="$ide $mod"                                                                            # 如果 devtype 的值为 IDE ,则把 mod 加入到现有的 ide 变量的值中 
         echo "ide=\"$ide"\";;                                                                            # 输出 "ide=" 然后是变量 ide 的值
      "SCSI") scsi="$scsi $mod"                                                                          # 如果 devtype 的值为 SCSI ,则把 mod 变量的值加入到 scsi 变量的值中
         echo "scsi=\"$scsi"\";;                                                                           # 输出 "scsi=" 再输出 $scsi  
      "NETWORK") network="$network $mod"                                                    # 下面的 NETWORK 和 AUDIO 也一样
         echo "network=\"$network"\";;
      "AUDIO") audio="$audio $mod"
         echo "audio=\"$audio"\";;
      *) other="$other $mod"                                                                            # 如果是属于 other 类型的则把模块名 $mod 加入到 other 变量的值列表中
         echo "other=\"$other"\";;
     esac
    done`
     
    load_module () {                    # 定义一个函数名为 load_module
     LC_ALL=C fgrep -xq "$1" /etc/hotplug/blacklist 2>/dev/null || modprobe $1 >/dev/null 2>&1        # 在 /etc/hotplug/blacklist 文件中查找是否有指定的模块,如果没有
    }                                                                                                                                            # 再用 modprobe 加载指定的模块
     
    # IDE                                        # 下面开始加载所有 IDE 类型的设备的模块
    for module in $ide ; do                 # 从 ide 变量中每次取出1个值,赋予变量 module 
     load_module $module                 # 再用 load_module 函数加载它。如果该模块存在于 /etc/hotplug/blacklist ,则不会被加载(因为存在于黑名单中)
    done
     
    # SCSI                                    # SCSI 方面的比较特殊,除了加载 scsi 变量中的模块外,还会从 modprobe -c (显示所有配置)中找出 scsi 卡(hostadapter)的模块
    for module in `/sbin/modprobe -c | awk '/^alias[[:space:]]+scsi_hostadapter[[:space:]]/ { print $3 }'` $scsi; do   # 从 modprobe -c 中找出所有scsi卡的模块别名,
     load_module $module             # 并和 scsi 变量一起,通过 for 循环一一加载
    done

    load_module floppy                # 然后加载 floppy 模块
     
    echo -n $" storage"                # 输出 "storage" ,表示存储方面的模块加载已经完成
     
    # Network                            # 接下来是加载网络设备模块

    pushd /etc/sysconfig/network-scripts >/dev/null 2>&1        # pushd 是一个 bash 的内建命令,把目录名放入目录堆栈的顶部,并进入指定目录
    interfaces=`ls ifcfg* | LC_ALL=C egrep -v '(ifcfg-lo|:|rpmsave|rpmorig|rpmnew)' | \        # 找出 network-scripts 目录下所有 lscfg 开头的文件名,排除指定备份和loopback
                LC_ALL=C egrep -v '(~|\.bak)$' | \                                                                    # 排除以 ~ 和 .bask 结尾的文件名
                LC_ALL=C egrep 'ifcfg-[A-Za-z0-9\._-]+$' | \                                                       # 找出所有以 ifcfg- 开头,并以多个字母/数字结尾的文件名
         sed 's/^ifcfg-//g' |                                                                                                  # 把前缀 ifcfg- 去掉,只留下后面的接口名
         sed 's/[0-9]/ &/' | LC_ALL=C sort -k 1,1 -k 2n | sed 's/ //'`                                         # 在数字前面加上空格,是类型和编号分离,便于 sort 排序,然后再组合回去
                                                                                                                                     # 目的是按顺序启动接口
    for i in $interfaces ; do                                                                                                # 对于在 $interfaces 变量中的每个接口
     eval $(LC_ALL=C fgrep "DEVICE=" ifcfg-$i)                                                                     # 从对应的文件 ifcfg-$i 找出 DEVICE= 行
     load_module $DEVICE                                                                                                 # 然后用 load_module 加载它,注意,DEVICE= 行可以是别名,例如 eth1.n7css
    done
    popd >/dev/null 2>&1                                                                                                 # 把 network-scripts 从目录名堆栈中弹出,并返回原来的目录
                                                                                    
    for module in $network ; do                                                                                        #  剩下还有 network 变量中的模块
     load_module $module                                                                                                # 也一一加载
    done
     
    echo -n $" network"                                                                                                    # 输出 network 字符串,表示加载网络设备模块工作完毕
     
    # Sound
    for module in `/sbin/modprobe -c | awk '/^alias[[:space:]]+snd-card-[[:digit:]]+[[:space:]]/ { print $3 }'` $audio; do    # 和 scsi 一样,加载在 modprobe -c 和 audio 变量
     load_module $module                                                                                                                                          # 中的模块
    done
     
    echo -n $" audio"                                                                                                        # 输出 audio ,表示声卡设备驱动加载完毕,一般会有多个驱动被加载        
     
    # Everything else (duck and cover)                                                                             # 剩下的就是 other 类型的设备了
    for module in $other ; do
     load_module $module                                                                                                # 这个直接用 load_moudle 函数加载了
    done
     
    echo -n $" done"                                                                                                        # 输出 done 表示完成
    success                                                                                                                    # 调用 success 函数,记录该事件
    echo
     
    ######################################################################################################################################################## 
    # 注释 :下面是启用 Software-RAID 的检测
     
    echo "raidautorun /dev/md0" | nash --quiet       # 用 nash (小型脚本解释器)执行 "raidautorun /dev/md0" 命令,对所有paritition ID 为 0xFD 分区进行检查
    ########################################################################################################################################################  
     
    # Start the graphical boot, if necessary; /usr may not be mounted yet, so we             # 显示图形模式的启动画面,但由于 rhgb 命令在 /usr 分区上,
    # may have to do this again after mounting                                                             #  这时 /usr 分区还没有被挂载,所以在挂载后重新做一遍这部分脚本
    RHGB_STARTED=0           # 初始化 RHGB_STARTED 变量的值为 0,表示尚未启动。RHGB 是 redhat graphical boot 的含义,
    mount -n /dev/pts         # 挂载 /dev/pts ,类型是 devpts ,目的是获得 pty ,这是一种伪文件系统
     
    if strstr "$cmdline" rhgb && [ "$BOOTUP" = "color" -a "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb ]; then        # 如果内核启动参数 $cmdline 含有 rhgb ,且
                                                                                                                                                            # BOOTUP 变量的值为 color ,且 GRPAHICAL 变量的值为 yes
                                                                                                                                                            # 且 /usr/bin/rhgb 文件存在并可执行
       LC_MESSAGES= /usr/bin/rhgb                                                                                                           # 把 "/usr/sbin/rhgb" 赋予变量 LC_MESSAGES
       RHGB_STARTED=1                                                                                                                            # RHGB_STARTED 变量的值为1。
    fi
     
    ########################################################################################################################################################  
    # 注释 :下面部分是使用 sysctl 设置内核的参数
     
    # Configure kernel parameters                    
    update_boot_stage RCkernelparam             # 调用 update_boot_stage 函数,该函数由 /etc/rc.d/init.d/functions 脚本定义,主要执行 "/usr/sbin/rhgb-client --update $1"
    action $"Configuring kernel parameters: " sysctl -e -p /etc/sysctl.conf    # 调用 action 函数,执行 sysctl 命令,它读取 /etc/sysctl.conf ,同时输出 “Configuring kernel
                                                                                                           # parameters” 字符串,action 函数也是由 /etc/rc.d/init.d/functions 脚本定义
     
    # 补充 :action 函数的作用是把第一个参数 $1 赋予变量 string 并输出到屏幕,同时把该字符串也写入 /etc/rhgb/temp/rhgb-console
     
    # 然后把 $1 后面的部分作为命令交给 initlog 执行,并根据结果调用 success 或者 failure 函数。而 success 或者 failure 函数又会根据结果输出 [ OK ] 或者 [ FAILED ] 。
     
    # 除了 success 和 failure 函数外,functions 脚本还定义了 passed 和 warnning 函数,它们又分别调用 echo_passwd() 和 echo_warnning()函数,
     
    # 以输出 [ PASSED ] 和 [ WARNNING ]
     
    ########################################################################################################################################################  
    # 注释 :下面设置系统的硬件时钟
    # Set the system clock.                    # 接下来设置系统时钟
    update_boot_stage RCclock                # 同样是告诉 update_boot_stage 该事件(执行 /usr/bin/rhgb-client --update="$1" 命令,这里 $1 就是 "RCclock")
    ARC=0                                                
    SRM=0
    UTC=0
     
    if [ -f /etc/sysconfig/clock ]; then        # 如果存在 /etc/sysconfig/clock 则执行该文件
       . /etc/sysconfig/clock
     
       # convert old style clock config to new values
       if [ "${CLOCKMODE}" = "GMT" ]; then                        # 如果变量 CLOCKMODE 为旧式的 GMT 格式
          UTC=true                                                                # 则 UTC 变量的值为 ture 
       elif [ "${CLOCKMODE}" = "ARC" ]; then                     # 如果 CLOCKMODE 的值是 ARC ,则
          ARC=true                                                                # ARC 的值为 true
       fi                                                                         # 如果 CLOCKMODE 不等于 GMT 或者 UTC ,则 UTC 的值等于 /etc/sysconfig/clock 中的值,一般都是 false
    fi                                                                            # 如果 CLOCKMODE 不等于 GMT 后者 ARC ,则 UTC 的值为 0
     
    CLOCKDEF=""                                                            # CLOCKDEF 变量的值默认为空
    CLOCKFLAGS="$CLOCKFLAGS --hctosys"                       # 而 CLOCKFLAGS 变量的值为原来的值(可能为空)加上 '--hctosys' ,表示把硬件时钟写入内核时钟
     
    case "$UTC" in                                                          # 根据 UTC 变量的值进行选择                                             
        yes|true) CLOCKFLAGS="$CLOCKFLAGS --utc"             # 如果 UTC 的值为 yes 或者 true,则变量 CLOCKFLAGS 的值加上 --utc ,表示采用 UTC 时区
      CLOCKDEF="$CLOCKDEF (utc)" ;;                                     # 同时变量 CLOCKDEF 的值加上 "(utc)"
        no|false) CLOCKFLAGS="$CLOCKFLAGS --localtime"     # 如果变量 UTC 的值为 false 或者 no,则
      CLOCKDEF="$CLOCKDEF (localtime)" ;;                        # CLOCKDEF 的值加上 "(localtime)" 。由于安装 OS时没有选择 UTC,CLOCKDEF 的值一般最后就是 (localtime) 而已
    esac
    case "$ARC" in                                                          # 根据 ARC 变量的值来选择              
        yes|true) CLOCKFLAGS="$CLOCKFLAGS --arc"            # 如果是 yes 或者 true ,则 CLOCKFLAGS 的值加上 --arc ,CLOCKDEF 的值加上 '(arc)'
      CLOCKDEF="$CLOCKDEF (arc)" ;;                                # 如果 ARC 的值为 0 ,则 CLOCKFLAGS 和 CLOCKDEF 的值都不变
    esac
    case "$SRM" in                                                         # 如果 SRM 的值是 yes 或者 true ,则 CLOCKFLAGS 的值加上 "--srm" ,CLOCKDEF 加上 "(srm)"
        yes|true) CLOCKFLAGS="$CLOCKFLAGS --srm"
      CLOCKDEF="$CLOCKDEF (srm)" ;;
    esac
     
    /sbin/hwclock $CLOCKFLAGS                    # 执行 hwclock $CLOCKFLAGS 命令,一般就是 /sbin/hwclock --hctosys
     
    action $"Setting clock $CLOCKDEF: `date`" date        # 并输出 "setting clock " 和 $CLOCKDEF ,并执行 date 输出当前时间,然后输出 [ OK ]
     
    ######################################################################################################################################################## 
    # 注释 :下面部分是设置 key map  
    if [ "$CONSOLETYPE" = "vt" -a -x /bin/loadkeys ]; then            # 接下来是设置 keymap ,一般都是 us
     KEYTABLE=                                                                        #  设置 KEYTABLE 和 KEYMAP
     KEYMAP=
     if [ -f /etc/sysconfig/console/default.kmap ]; then                # 假如存在 /etc/sysconfig/console/default.kmap ,则
      KEYMAP=/etc/sysconfig/console/default.kmap                            # KEYMAP 变量的值为 /etc/sysconfig/console/default.kmap
     else                                                                                    # 否则
      if [ -f /etc/sysconfig/keyboard ]; then                                        # 如果存在 /etc/sysconfig/keyboard 文件,则执行它
        . /etc/sysconfig/keyboard                                                       # 该文件设置 KEYTABLE 变量的值,例如 KEYTABLE="/usr/lib/kbd/keytables/us.map
      fi
      if [ -n "$KEYTABLE" -a -d "/lib/kbd/keymaps" ]; then              # 如果 KEYTABLE 的值不为空,且存在 /lib/kbd/keymaps/ 目录,则
         KEYMAP="$KEYTABLE.map"                                                # KEYMAP 的值为 KEYTABLE 的值加上 ".map"
      fi
     fi
     if [ -n "$KEYMAP" ]; then                                                                 # 假如 KEYMAP 变量的值不为空(间接表示 KEYTABLE 不为空且存在 keymaps 目录)
      # Since this takes in/output from stdin/out, we can't use initlog
      if [ -n "$KEYTABLE" ]; then                                                                    # 且 KEYTABLE 的值不为空
        echo -n $"Loading default keymap ($KEYTABLE): "                                     # 则输出 "Loading default keymap xxxx"
      else
        echo -n $"Loading default keymap: "                                                    # 否则只输出 Loading default keymap
      fi    
      loadkeys $KEYMAP < /dev/tty0 > /dev/tty0 2>/dev/null && \            # 接下来用 loadkeys 加载 $KEYMAP 指定的键盘映射文件。
         success $"Loading default keymap" || failure $"Loading default keymap"        # 如果成功则调用 success 函数,否则调用 failure 函数
      echo
     fi
    fi
     
    ########################################################################################################################################################
    # 注释 :下面部分是设置主机名 
     
    # Set the hostname.                                # 接下来是设置主机名
    update_boot_stage RChostname                # 告诉 update_boot_stage 函数该事件,以执行 /usr/sbin/rhgb-client --update RChostname 命令
    action $"Setting hostname ${HOSTNAME}: " hostname ${HOSTNAME}        # 打印 "setting hostname xxxx" 字符串,并执行 hostname ${HOSTNAME}设置主机名
                                                                     #  注意,HOSTNAME 变量前面已经有过赋值的了,就是 `/bin/hostname` 命令返回的值
     
    ########################################################################################################################################################
    # 注释 :下面设置 ACPI 部分
    # Initialiaze ACPI bits
    if [ -d /proc/acpi ]; then                                                                    # 如果存在 /proc/acpi 目录,则
       for module in /lib/modules/$unamer/kernel/drivers/acpi/* ; do            # 对于在 /lib/modules/<kernel-version>/kernel/drivers/acpi/ 目录下的所有模块文件
          insmod $module >/dev/null 2>&1                                                        # 都用 insmod 命令加载到内核中
       done
    fi
     
     
    ########################################################################################################################################################
    # 注释 :下面是主要的部分,就是确认是否需要对 / 文件系统进行 fsck  
    if [ -f /fastboot ] || strstr "$cmdline" fastboot ; then      # 接下来是看对 / 系统进行 fsck 的时候了。如果不存在 /fastboot 文件则看 cmdline 变量是否含有 fastboot 字符串
     fastboot=yes                                                              # 如果有,则 fastboot 变量的值为 yes。/fastboot 是由 shutdown -f 创建的,表示重启时不作 fsck
    fi
     
    if [ -f /fsckoptions ]; then                                            # 如果存在 /fsckoptions 文件,则把文件的内容赋予变量 fsckoptions 变量
     fsckoptions=`cat /fsckoptions`
    fi
     
    if [ -f /forcefsck ] || strstr "$cmdline" forcefsck ; then        # 如果不存在 /forcefsck 文件,则检查 cmdline 变量是否含有 forcefsck 字符串,如果有
     fsckoptions="-f $fsckoptions"                                            # 则在 fsckoptions 的值前面加上 "-f" 。/forcefsck 是由 shutdown -F 创建的,强制 fsck
    elif [ -f /.autofsck ]; then                                                 # 如果存在 /.autofsck
          if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then        #  且文件 /usr/bin/rhgb-clinet 可执行,且 /usr/bin/rhgb 服务在运行(--pnig),则
               chvt 1                                                                                                #  切换到虚拟控制台 #1 (主要是显示 fsck 的信息用)
           fi
           echo $"Your system appears to have shut down uncleanly"                            # 并显示需要 fsck 的信息 “You system appears to have shut down uncleanly”
           AUTOFSCK_TIMEOUT=5                                                                              # 设置超时时间为5秒
          [ -f /etc/sysconfig/autofsck ] && . /etc/sysconfig/autofsck                           # 如果存在 /etc/sysconfig/autofsck 则执行它
          if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                 # 如果执行该文件后 AUTOFSCK_DEF_CHECK 变量的值为 yes ,则
              AUTOFSCK_OPT=-f                                                                                        # 变量 AUTOFSCK_OPT 的值为 "-f"
          fi
     
          if [ "$PROMPT" != "no" ]; then                                                                // 如果 PROMPT 的值不为 no ,则
            if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                // 且 AUTOFSCK_DEF_CHECK 的值为 yes
               if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press N within %d seconds to not force file system integrity check..." n ; then    //  执行 getkey 命令,超时5秒,并
                                                                                                                                                                                           // 提示5秒内按下 n 键可跳过 fsck
                  AUTOFSCK_OPT=        // 如果用户按下 n ,则 AUTOFSCK_OPT 的值为空,表示取消自动 fsck
               fi
            else                    // 如果 AUTOFSCK_DEF_CHECK 的值不为 yes ,则提示5秒内按y 可以强制 fsck
              if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press Y within %d seconds to force file system integrity check..." y ; then        // 同样是用 getkey 捕捉 y 键
                  AUTOFSCK_OPT=-f        // 如果用户按下了 y 键,则 AUTOFSCK_OPT 的值为 "-f"
              fi
           fi
           echo
          else         // 如果 PROMPT 的值为 "no", 这是用户无法选择是否 fsck 
            # PROMPT not allowed
            if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                # 取决于 AUTOFSCK_DEF_CHECK 变量的值,如果是 yes ,则
              echo $"Forcing file system integrity check due to default setting"     # 提示由于默认的设置强制 fsck 。可能是 mount 次数达到限制,或者多长时间没有 fsck 了
            else                                            
              echo $"Not forcing file system integrity check due to default setting"            # 如果 AUTOFSCK_DEF_CHECK 的值为 no ,则表示不做 fsck 
            fi
          fi
          fsckoptions="$AUTOFSCK_OPT $fsckoptions"
    fi
     
    # 注释 :注意!到这里为止并没有执行 fsck 操作,只是判断是否需要执行 fsck 以及 fsck 命令的选项
     
    if [ "$BOOTUP" = "color" ]; then       # 如果 BOOTUP 的值为 color ,则
     fsckoptions="-C $fsckoptions"            # -C 表示显示 fsck 进度信息
    else                                              # 否则
     fsckoptions="-V $fsckoptions"            # -V 表示显示 verbose 信息
    fi
     
    if [ -f /etc/sysconfig/readonly-root ]; then            # 如果存在 /etc/sysconfig/readonly-root ,则
        . /etc/sysconfig/readonly-root                            # 执行该文件
         
        if [ "$READONLY" = "yes" ]; then                            # 如果 READONLY 变量的值为 yes ,则
            # Call rc.readonly to set up magic stuff needed for readonly root        # 执行 /etc/rc.readonly 脚本
            . /etc/rc.readonly
        fi
    fi

    #########################################################################################################################################################
    # 注释 :下面开始判断 / 文件系统的类型(是本地还是 nfs 格式)。如果是nfs则不执行 fsck ,否则继续
     

    _RUN_QUOTACHECK=0                                            # 初始化 RUN_QUOTACHECK 的值为0.这里是作一个标记,因为后面的代码可能会导致重启。在这里做好标记
                                                                                # 如果后面没有重启,就可以把 _RUN_QUOTACHECK 设置为1
    ROOTFSTYPE=`awk '/ \/ / && ($3 !~ /rootfs/) { print $3 }' /proc/mounts`        # 从 /proc/mounts 中找出 / 文件系统的类型,并赋予变量 ROOTFSTYPE
    # 注释 :下面用到 -z "$fastboot" ,如果有 /fastboot ,则 fastboot 的值为 yes, 所以就不合 if 的条件,则跳过 fsck 。这就是 shutdown -f 快速启动的效果
     
    if [ -z "$fastboot" -a "$READONLY" != "yes" -a "X$ROOTFSTYPE" != "Xnfs" -a "X$ROOTFSTYPE" != "Xnfs4" ]; then     # 如果fastboot 变量的值为空且  READONLY 变量不为 yes
                                                                                                                                                                    # 且 / 文件系统的类型不是 nfs 或者 nfs4 ,则 
            STRING=$"Checking root filesystem"    # 初始化 string 变量并输出 "checking root filesystem" 
            echo $STRING
            rootdev=`awk '/ \/ / && ($3 !~ /rootfs/) {print $1}' /proc/mounts`        # 从 /proc/mounts 中找出 / 文件系统所在的设备,并赋予 rootdev 变量(例如 /dev/hdb2)
             if [ -b /initrd/"$rootdev" ] ; then        # 如果 /initrd/$rootdev 是一个 block 设备
                  rootdev=/initrd/"$rootdev"                    # 则 rootdev 就是 /initrd/rootdev 
             else                                                 # 否则 rootdev 就是 / ,正常情况应该是执行该步的,也就是 rootdev 最终为 /
             rootdev=/                                       # rootdev 的值为 / ,因为 fsck 支持用 LABEL、mount point、device name 来指定要检查的文件系统 
             fi
     
        
            # 注释 : 下面开始真正的 fsck 操作
             if [ "${RHGB_STARTED}" != "0" -a -w /etc/rhgb/temp/rhgb-console ]; then        # 如果 RHGB_STARTED 不为0且 /etc/rhgb/tmp/rhgb-console 文件可写,则
                  fsck -T -a $rootdev $fsckoptions > /etc/rhgb/temp/rhgb-console           # 执行 fsck 命令,-T 表示不打印标题栏,-a 表示自动修复错误,设备是 / ,后面是选项
                                                                                                                          # 且信息写入 /etc/rhgb/temp/rhgb-console
             else                                                                                                           # 否则
                  initlog -c "fsck -T -a $rootdev $fsckoptions"                                                        # 调用 initlog 执行 fsck ,信息直接输出到屏幕,fsck 命令本身都是一样的。
            fi                                                                                                                     
            rc=$?                                    # 把 fsck 的结果送给变量 rc
            if [ "$rc" -eq "0" ]; then         # 如果 rc的值为0,表示 fsck 成功通过
                  success "$STRING"            &n
  • 相关阅读:
    GRIDVIEW导出到EXCEL
    数据表示:字节 高位低位
    js学习笔记0
    12奇招,循序删除顽固的文件
    加快开关机速度
    PHP正则表达式的快速学习
    firefox下height不能自动撑开的解决办法
    给MySQL加密(也适用于Wamp5中)
    我的电脑创建资源管理器
    css 圆角7种CSS圆角框解决方案
  • 原文地址:https://www.cnblogs.com/ai616818/p/2098331.html
Copyright © 2011-2022 走看看