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
  • 相关阅读:
    ios UIWebView截获html并修改便签内容(转载)
    IOS获取系统时间 NSDate
    ios 把毫秒值转换成日期 NSDate
    iOS  如何判断当前网络连接状态  网络是否正常  网络是否可用
    IOS开发 xcode报错之has been modified since the precompiled header was built
    iOS系统下 的手机屏幕尺寸 分辨率 及系统版本 总结
    iOS 切图使用 分辨率 使用 相关总结
    整合最优雅SSM框架:SpringMVC + Spring + MyBatis 基础
    Java面试之PO,VO,TO,QO,BO
    Notes模板说明
  • 原文地址:https://www.cnblogs.com/ai616818/p/2098331.html
Copyright © 2011-2022 走看看