Linux启动脚本分析

#!/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

 

代码
   
     
在分析Linux系统启动过程时,想观察rc.sysinit何时启动何时结束。于是,在rc.sysinit的开头和末尾分别加了句
echo $
0 Start

echo $
0 Finish
结果在启动时发现:
紧接在“INIT:version
2.85 booting”之后
/ etc / rc.d / rc.sysinit Start”连着出现了2次
而最后的“
/ etc / rc.d / rc.sysinit Finish”只出现一次,很奇怪。于是查看rc.sysinit,发现开头有这么几句:
if [ - z " $IN_INITLOG " - a - x / sbin / initlog ]; then
exec
/ sbin / initlog - r / etc / rc.d / rc.sysinit
fi
恍然大悟,原来为了将rc.sysinit的运行信息记到syslog里(
/ var / log / messages),使用了进程替代:
exec
/ sbin / initlog - r / etc / rc.d / rc.sysinit
man了一下initlog,是这么说的:
initlog logs messages and events to the system logger. It
is mainly designed for use in init scripts.
- r, -- run = [program]
Execute the specified program, with an open file descriptor so that the program can pass back commands to initlog.

还有个
- c参数
- c, -- cmd = [program]
Execute the specified program, logging anything output to stdout or stderr.

可以用
- c参数做实验:
[root@linux
~ ]# initlog - c ' ls / '
bin
boot
dev
etc
home
iptables
lib
mnt
opt
proc
root
sbin
sys
tmp
usr
var
[root@linux
~ ]# tail - 20 / var / log / messages
Sep
8 09 : 30 : 01 linux crond(pam_unix)[ 26258 ]: session closed for user root
Sep
8 09 : 40 : 01 linux crond(pam_unix)[ 26282 ]: session opened for user root by (uid = 0 )
Sep
8 09 : 40 : 01 linux crond[ 26283 ]: (root) CMD ( / usr / lib / sa / sa1 1 1 )
Sep
8 09 : 40 : 02 linux crond(pam_unix)[ 26282 ]: session closed for user root
Sep
8 09 : 45 : 12 linux ls: bin
Sep
8 09 : 45 : 12 linux ls: boot
Sep
8 09 : 45 : 12 linux ls: dev
Sep
8 09 : 45 : 12 linux ls: etc
Sep
8 09 : 45 : 12 linux ls: home
Sep
8 09 : 45 : 12 linux ls: iptables
Sep
8 09 : 45 : 12 linux ls: lib
Sep
8 09 : 45 : 12 linux ls: mnt
Sep
8 09 : 45 : 12 linux ls: opt
Sep
8 09 : 45 : 12 linux ls: proc
Sep
8 09 : 45 : 12 linux ls: root
Sep
8 09 : 45 : 12 linux ls: sbin
Sep
8 09 : 45 : 12 linux ls: sys
Sep
8 09 : 45 : 12 linux ls: tmp
Sep
8 09 : 45 : 12 linux ls: usr
Sep
8 09 : 45 : 12 linux ls: var

再观察rc.sysinit记在messages中的信息
[root@rhel
~ ]# grep rc.sysinit / var / log / messages
Sep
8 16 : 41 : 19 rhel rc.sysinit: - e
Sep
8 16 : 41 : 32 rhel rc.sysinit: - e
Sep
8 16 : 41 : 46 rhel rc.sysinit: Configuring
文章出处:飞诺网(www.firnow.com):http:
// dev.firnow.com/course/6_system/linux/Linuxjs/2008912/142270.html

 

 

######################################################################################################################################################

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"                    # 则调用 success 打印 "checking root fileysyes      [OK]"
          echo
        elif [ "$rc" -eq "1" ]; then        # 如果 rc 为1 ,则打印 passed ,表示有错误但修复
             passed "$STRING"                # 这时调用的是 passed 函数(由 /etc/rc.d/init.d/functions 脚本定义,输出 "checking root filesystems     [ PASSED ]")
             echo
         elif [ "$rc" -eq "2" -o "$rc" -eq "3" ]; then         # 如果 rc 为 2 (表示系统应该重启)或者为 3(为 1+2 ,表示系统错误已修复,但需要重启)
              echo $"Unmounting file systems"                        # 提示卸载 / 文件系统
              umount -a                                                        # 把所有卸载,当然 / 除外
              mount -n -o remount,ro /                                  # 把 / 设备已 ro 模式重新 mount
              echo $"Automatic reboot in progress."                # 然后提示将要自动重启了
              reboot -f                                                         # 在这里执行 reboot -f ,强制重启
          fi     # 这个 fi 是结束 "[ "$rc" -eq "0" ]; " 的
          # A return of 4 or higher means there were serious problems.    # 如果 fsck 返回的值大于4(不含4,4表示错误未修复),则表示 / 文件系统出现重大错误,无法继续
         if [ $rc -gt 1 ]; then    # 这里之所以用 -gt 1 ,是因为如果前面返回2或者3就会自动重启了,如果执行到这里说明 rc 的值必须至少大于等于4
             if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then    # 如果 rhgb-client 可执行且 rhgb 服务在运行
                  chvt 1                 # 强制切换到 1# 虚拟控制台
             fi

            failure "$STRING"        # 调用 failure 函数,打印 "checking root filesystem     [ FAILURE ]"
            echo
            echo
            echo $"*** An error occurred during the file system check."        # 提示 fsck 过程出错
            echo $"*** Dropping you to a shell; the system will reboot"         # 提示将进入紧急模式的 shell 
            echo $"*** when you leave the shell."                                       # 当你退出该 shell 时会自动重启

            str=$"(Repair filesystem)"        # 设置紧急模式下的 shell 提示符
            PS1="$str \# # "; export PS1     #  设置提示符变量 PS1 为 "(Repair filesystem) <N> # " ,<N> 表示当前是第几个命令
            [ "$SELINUX" = "1" ] && disable_selinux
            sulogin                                    # 自动执行 sulogin ,这不是 su ,而是 single-user login 的意思,自动进入单用户模式的 shell

# 注意这里将进入一个交互式的 shell 。只有你执行 exit 命令,才会执行下面的代码,否则下面的代码根本不会被执行

            echo $"Unmounting file systems"    # 提示卸载文件系统
            umount -a                                    # 卸载所有文件系统,/ 除外
            mount -n -o remount,ro /              # / 以 ro 模式挂载
            echo $"Automatic reboot in progress."        # 提示将自动重启
            reboot -f                                     # 立即重启

         elif [ "$rc" -eq "1" ]; then                     # 如果 rc 的值等于1 ,则表示错误修复完毕
            _RUN_QUOTACHECK=1                    # 如果上面的 fsck 没有出错,自然就不会重启,所以这里把 _RUN_QUOTACHECK 的值设置为1,表示可以进行 quotacheck 了

         fi   # 这个 fi 是结束 " [ $rc -gt 1 ]; " 的

         if [ -f /.autofsck -a -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then    #  如果 rhgb-clinet 可运行且 rhgb 服务在运行
          chvt 8                                                                                                                    # 则切换换 8# 控制台,也就是图形
         fi
fi        # 这个 fi 是结束前面的 ”[ -z "$fastboot" -a "$READONLY" != "yes" -a "X$ROOTFSTYPE" != "Xnfs" -a "X$ROOTFSTYPE" != "Xnfs4" ]; “ 的

   # 所以如果存在 /fastboot 文件,就直接跳转到这里

########################################################################################################################################################
# 注释 :在 initrd 的 /linuxrc 或者 /init 脚本中会把初始/文件系统卸载掉,如果没有卸载完全,则在这里会重新做一次

# Unmount the initrd, if necessary                    # 接下来是卸载 initrd 初始/ 文件系统,一般 /init 都会在结尾 umount /initrd/dev 的
if LC_ALL=C fgrep -q /initrd /proc/mounts && ! LC_ALL=C fgrep -q /initrd/loopfs /proc/mounts ; then    # 如果 /proc/mounts 文件还有 /initrd/proc 条目,

# 且不存在 /initrd/loops 条目,则
   if [ -e /initrd/dev/.devfsd ]; then                                                                                                     # 如果存在 /initrd/dev/.devfsd 文件,则
      umount /initrd/dev                                                                                                                            # 卸载 /initrd/dev 目录
   fi
   umount /initrd                                            # 最后把整个 /initrd 卸载
   /sbin/blockdev --flushbufs /dev/ram0 >/dev/null 2>&1    # 最后用 blockdev --flushbs 命令清除 /dev/ram0 的内容,该社备就是被挂载为初始/的
fi
########################################################################################################################################################                                                                                # 注释 :下面是对 / 进行 quota 检查,其他文件系统暂时不执行 quotacheck

# Possibly update quotas if fsck was run on /.                    # 如果 fsck 已经对 / 进行了检查,则执行 quotacheck 更新 quota 情况
LC_ALL=C grep -E '[[:space:]]+/[[:space:]]+' /etc/fstab | \        # 在 /etc/fstab 中找出 / 文件系统所在的行,并打印第4个字段(属性)
    awk '{ print $4 }' | \                                                           # 这是属性字段
    LC_ALL=C fgrep -q quota                                                    # 找出是否有 quota 字符串,不管是 usrquota 还是 grpquota
_ROOT_HAS_QUOTA=$?                                                    # 把结果赋予 _ROOT_HAS_QUOTA 变量
if [ "X$_RUN_QUOTACHECK" = "X1" -a \ #  如果 x$_RUN_QUOTACHECK 返回 X1 ,且
    "X$_ROOT_HAS_QUOTA" = "X0" -a \                                #   X$_ROOT_HAS_QUOTA 返回 X0 ,则表示当前可以执行 quotacheck ,且 / 也启用了 quota 
-x /sbin/quotacheck ]; then                                        #  如果存在 quotacheck 命令   
if [ -x /sbin/convertquota ]; then                                    # 如果存在 convertquota 命令
     if [ -f /quota.user ]; then                                                # 且存在 /quota.user 则
          action $"Converting old user quota files: " \                          # 并调用 action 函数,打印提示信息,用 convertquota 转换成 v2 格式的 aquota.user 文件
          /sbin/convertquota -u / && rm -f /quota.user                # 然后删除 v1格式的 /quota.user
     fi
     if [ -f /quota.group ]; then                                            # 同样对 /quota.group 转换成 v2 格式的 aquota.group,并删除 /quota.group 文件
          action $"Converting old group quota files: " \
          /sbin/convertquota -g / && rm -f /quota.group            # 同样在转换后删除旧的 v1 的 group quota 文件
     fi
fi
action $"Checking root filesystem quotas: " /sbin/quotacheck -nug /        # 然后执行 quotacheck -nug / 对 / 文件系统进行检查,并打印提示信息
fi    # 这个 fi 是结束 "[ "X$_RUN_QUOTACHECK" = "X1" -a "X$_ROOT_HAS_QUOTA" = "X0" -a -x /sbin/quotacheck ]; " 的

########################################################################################################################################################

# 注释 :下面这个部分是设置 ISA 设备的,不过由于现在基本没有 ISA 设备了,所以可以跳过该部分,甚至可以在内核中指定不支持 ISA

if [ -x /sbin/isapnp -a -f /etc/isapnp.conf -a ! -f /proc/isapnp ]; then        
    # check for arguments passed from kernel
    if ! strstr "$cmdline" nopnp ; then
PNP=yes
    fi
    if [ -n "$PNP" ]; then
action $"Setting up ISA PNP devices: " /sbin/isapnp /etc/isapnp.conf
    else
action $"Skipping ISA PNP configuration at users request: " /bin/true
    fi
fi

########################################################################################################################################################

# Remount the root filesystem read-write.                                    # 现在 / 文件系统 fsck 过了,可以按照 read-write 的模式挂载了
update_boot_stage RCmountfs                                                      # 告诉 rhgb 服务器更新 RCmountfs 服务的状态
state=`awk '/ \/ / && ($3 !~ /rootfs/) { print $4 }' /proc/mounts`    # 从 /proc/mounts 中找出 ' / ' 字符串并且第3个字段不等于 'rootfs',则打印其第4个字段,赋予 state
[ "$state" != "rw" -a "$READONLY" != "yes" ] && \                              # 如果 state 变量的值不为 'rw' 且 READONLY 变量的值为 yes ,则
  action $"Remounting root filesystem in read-write mode: " mount -n -o remount,rw /        # 用 -o remount -o rw 重新挂载 / 为 rw 模式,并打印提示信息

########################################################################################################################################################

# 注释 :下面开始是 LVM2 的部分了

# LVM2 initialization                                                                                        # 下面部分是 LVM2  的初始化部分
if [ -x /sbin/lvm.static ]; then                                                                          # 如果存在 /sbin/lvm.static 且可执行
    if ! LC_ALL=C fgrep -q "device-mapper" /proc/devices 2>/dev/null ; then        # 如果 /proc/devices 文件不含有 device-mapper ,则
         modprobe dm-mod >/dev/null 2>&1                                                                        # 调用 modprobe 加载 dm-mod
    fi
    echo "mkdmnod" | /sbin/nash --quiet >/dev/null 2>&1                                    # 并调用 nash 执行 mkdmmod ,不过是以 quiet 模式执行的
    [ -n "$SELINUX" ] && restorecon /dev/mapper/control >/dev/null 2>&1
    if [ -c /dev/mapper/control -a -x /sbin/lvm.static ]; then                                # 假如存在 /dev/mapper/control 这个字符设备且 /sbin/lvm.static 可执行,则
         if /sbin/lvm.static vgscan --mknodes --ignorelockingfailure > /dev/null 2>&1 ; then        # 调用 lvm.static 执行 vgscan 扫描所有卷组(忽略锁错误)
             action $"Setting up Logical Volume Management:" /sbin/lvm.static vgchange -a y --ignorelockingfailure        # 如果扫描到卷组,则调用 action 函数,
         fi                                                                                                                                                      # 打印提示信息,并执行 vgchange -ay 激活全部卷组
    fi
fi

# LVM initialization                 # 这是 LVM1 的部分,因为系统可能同时存在 lVM1 和 lvm2 的 pv
if [ -f /etc/lvmtab ]; then        # 如果存在 /etc/lvmtab 文件
    [ -e /proc/lvm ] || modprobe lvm-mod > /dev/null 2>&1    # 且 /proc/lvm 文件存在,如果没有则调用 modprobe lvm-mod 加载 lvm-mod ,注意模块名的不同
        if [ -e /proc/lvm -a -x /sbin/vgchange ]; then                # 如果现在存在 /proc/lvm 文件且 /sbin/vgchange 可执行
             action $"Setting up Logical Volume Management:" /sbin/vgscan && /sbin/vgchange -a y    # 则调用 action 打印提示信息,并执行 vgchange -ay 激活它们
        fi
fi

########################################################################################################################################################

# Clean up SELinux labels
if [ -n "$SELINUX" ]; then
   for file in /etc/mtab /etc/ld.so.cache ; do
    [ -r $file ] && restorecon $file  >/dev/null 2>&1
   done
fi

########################################################################################################################################################

# Clear mtab                                                            # 由于之前的 /etc/mtab 并不准确,所以现在把它的内容清掉
(> /etc/mtab) &> /dev/null                                              # 用 >/etc/mtab 清空其内容

# Remove stale backups
rm -f /etc/mtab~ /etc/mtab~~                                    # 删除 /etc/mtab 的备份文件

# Enter root, /proc and (potentially) /proc/bus/usb and devfs into mtab.        # 把 root ,/proc ,/proc/bus/usb,devpts 加入到 /etc/mtab 中
mount -f /                                                                    #  -f 表示只更新 /etc/mtab ,但不实际mount,因为这些文件系统在之前都挂载了,没有必要再挂载一次
mount -f /proc
mount -f /sys >/dev/null 2>&1
mount -f /dev/pts
[ -f /proc/bus/usb/devices ] && mount -f -t usbfs usbfs /proc/bus/usb  # 如果存在 /proc/bus/usb/devices 文件,则用 mount -f -t usbfs 挂载到 /proc/bus/usb 下
[ -e /dev/.devfsd ] && mount -f -t devfs devfs /dev         # 如果存在 /dev/.devfsd ,则同样挂载 devfs 到 /dev 下

########################################################################################################################################################

# configure all zfcp (scsi over fibrechannel) devices before trying to mount them
# zfcpconf.sh exists only on mainframe
[ -x /sbin/zfcpconf.sh ] && /sbin/zfcpconf.sh

########################################################################################################################################################

# The root filesystem is now read-write, so we can now log        # 由于之前的 / 是只读的,所以通过 initlog 来记录信息,现在可以通过 syslog 了
# via syslog() directly..
if [ -n "$IN_INITLOG" ]; then
    IN_INITLOG=                                                                            # 如果 IN_INITLOG 的值不为空则清空它
fi

if ! strstr "$cmdline" nomodules && [ -f /proc/modules ] ; then        # 如果内核启动参数含有 "nomodules" ,且存在 /proc/modules 文件,则
    USEMODULES=y                                                                            # 把 USEMODULES 设置为 y
fi

# Load modules (for backward compatibility with VARs)                
if [ -f /etc/rc.modules ]; then
/etc/rc.modules
fi

########################################################################################################################################################

update_boot_stage RCraid                                        # 下面部分是激活 RAID 的
if [ -f /etc/mdadm.conf ]; then                                # 如果存在 /etc/mdadm.conf 则
    /sbin/mdadm -A -s                                                # mdadm -A 表示 Assemble 模式,-s 则表示搜索 /etc/mdadm.conf
fi

# 注释 :下面解决旧式的用 raid-tools 建立的 software-raid

if [ -f /etc/raidtab ]; then                                        # 如果存在旧式的 raidtools 的 /etc/raidtab 则
    # Add raid devices
    [ -f /proc/mdstat ] || modprobe md >/dev/null 2>&1        # 如果不存在 /proc/mdstat 文件,则用 modprobe md 先加载 md 模块

    if [ -f /proc/mdstat ]; then
         echo -n $"Starting up RAID devices: "                             # 并输出 "Starting up RAID devices:"

         rc=0                                                                           # 先把 rc 的值设置为0,如果有某个 raid 激活失败,则 rc 为 1

# rc  和下面的 RESULT 不同,RESULT 是每个 raid 的激活状态,而 rc 是全局的激活状态
         for i in `awk '{if ($1=="raiddev") print $2}' /etc/raidtab`     # 用 awk 从 /etc/raidtab 中找出所有 raiddev 行打印第2个字段也就是 raid 的设备名

         do                                                                                    # 针对每个 Software-raid 执行下面的代码
                RAIDDEV=`basename $i`                                                # 把每个 raid 设备的设备名(出去前面目录部分)赋予变量 RAIDDEV
                RAIDSTAT=`LC_ALL=C grep "^$RAIDDEV : active" /proc/mdstat`            # 如果 /proc/mdstat 中表明该 RAID 已经是 active 的状态
                if [ -z "$RAIDSTAT" ]; then                                                                # 如果状态为空,表明该 RAID 并没有激活
                    # First scan the /etc/fstab for the "noauto"-flag
                    # for this device. If found, skip the initialization
                    # for it to avoid dropping to a shell on errors.
                    # If not, try raidstart...if that fails then
                    # fall back to raidadd, raidrun.  If that
                    # also fails, then we drop to a shell
                    RESULT=1                                                                                    # 先把 RESULT 设置为 1,表示 false
                    INFSTAB=`LC_ALL=C grep -c "^$i" /etc/fstab`                                 # 然后检查 /etc/fstab 中是否有以指定的 md 设备开头的行,结果赋予 INFSTAB
                    if [ $INFSTAB -eq 0 ] ; then                                                           # 如果 INFSTAB 为0,表示没有找到,也就是不希望自动我你挂载该 raid 设备
                           RESULT=0                                                                                # 则把 RESULT 设置为0
                           RAIDDEV="$RAIDDEV(skipped)"                                                    # 并在 RAIDDEV 后面加上 (skipped) ,表示该设备跳过不激活它
                    fi
                    NOAUTO=`LC_ALL=C grep "^$i" /etc/fstab | LC_ALL=C fgrep -c "noauto"`    # 如果该设备在 /etc/fstab 中但它的属性含有 noauto
                    if [ $NOAUTO -gt 0 ]; then                                                                            # 则和上面不在 /etc/fstab 一样处理
                           RESULT=0
                           RAIDDEV="$RAIDDEV(skipped)"
                    fi
                    if [ $RESULT -gt 0 -a -x /sbin/mdadm ]; then                                            # 如果 /sbin/mdadm 文件可执行,且尝试用 mdadm 来激活它们
                            /sbin/mdadm -Ac partitions $i -m dev                                                    # mdadm 将从 /proc/partitions 文件读取并状态指定 raid($i) ,-m 表示 minor

# number 就等于它刚才装配的 raid 设备名的数字部分
                            RESULT=$?                                                                                           # 并把 mdadm 的结果赋予 RESULT 变量
                    fi
                    if [ $RESULT -gt 0 -a -x /sbin/raidstart ]; then                                            # 如果 RESULT 大于0,表示 madm 命令失败,则尝试用 /sbin/raidstart 来
                        /sbin/raidstart $i
                        RESULT=$?                                                                                    
                    fi
                    if [ $RESULT -gt 0 -a -x /sbin/raid0run ]; then                                             # 如果 raidstart 还是失败,则尝试用 raid0run 来
                        /sbin/raid0run $i
                        RESULT=$?
                    fi
                    if [ $RESULT -gt 0 -a -x /sbin/raidadd -a -x /sbin/raidrun ]; then                  # 如果 raid0run 失败则尝试用 raidadd 然后用 raidrun
                        /sbin/raidadd $i
                        /sbin/raidrun $i
                        RESULT=$?
                    fi
                    if [ $RESULT -gt 0 ]; then                                                                         # 如果还是失败,则 rc 的值最终为1
                        rc=1
                   fi
                    echo -n "$RAIDDEV "                                                                                # 输出当前操作的 raid 设备名
                else            # 这个 else 是对应 ”if [ -z "$RAIDSTAT" ]; “的,因为可能内核集成了 raid support ,自动激活 raid
                       echo -n "$RAIDDEV "                                                                             # 如果 RAID 的状态本来就是 active ,则直接输出 raid 的名称
                fi
         done
echo

# A non-zero return means there were problems.                    # 如果只要有一个 raid 激活失败,则 rc 的值就为1
if [ $rc -gt 0 ]; then                                                                # 如果 rc 的值大于0,则表示有 raid 激活失败
         if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then    # 则按照前面 fsck 失败的情况,进入紧急模式
      chvt 1
  fi
  echo
  echo
  echo $"*** An error occurred during the RAID startup"
  echo $"*** Dropping you to a shell; the system will reboot"
  echo $"*** when you leave the shell."

  str=$"(RAID Repair)"                                                                            # 只不过提示符有所不同而已,变成了 (RAID Repair) <N> #
  PS1="$str \# # "; export PS1
  [ "$SELINUX" = "1" ] && disable_selinux
  sulogin

  echo $"Unmounting file systems"
  umount -a
  mount -n -o remount,ro /
  echo $"Automatic reboot in progress."
  reboot -f

fi        # 注释 ;这个 fi 是结束 "if [ -f /etc/raidtab ];" 的,如果不存在 /etc/raidtab ,则直接跳到这里

########################################################################################################################################################
# LVM2 initialization, take 2                                                        # 由于 LVM 的 pv 可能是建立在 RAID 之上的,所以再次尝试扫描 vg 并激活它们
if [ -c /dev/mapper/control -a -x /sbin/lvm.static ]; then             # 步骤和前面的一样
  if /sbin/lvm.static vgscan > /dev/null 2>&1 ; then
   action $"Setting up Logical Volume Management:" /sbin/lvm.static vgscan --mknodes --ignorelockingfailure && /sbin/lvm.static vgchange -a y --ignorelockingfailure
  fi
fi
# LVM initialization, take 2 (it could be on top of RAID)
if [ -e /proc/lvm -a -x /sbin/vgchange -a -f /etc/lvmtab ]; then
  action $"Setting up Logical Volume Management:" /sbin/vgscan && /sbin/vgchange -a y
fi
    fi
fi

########################################################################################################################################################

# 注释 :下面对其他文件系统(/ 除外)的文件系统进行 fsck

_RUN_QUOTACHECK=0        # 还是把 _RUN_QUOTACHECK 变量的值设置为0
# Check filesystems            # 检查其他文件系统
if [ -z "$fastboot" ]; then        # 如果不存在 /etc/fastboot ,则执行下面的脚本。可以看到 shutdown -r 不仅影响 / 的 fsck ,也影响其他文件系统的 fsck
        STRING=$"Checking filesystems"        # 打印提示信息
        echo $STRING
         if [ "${RHGB_STARTED}" != "0" -a -w /etc/rhgb/temp/rhgb-console ]; then
                  fsck -T -R -A -a $fsckoptions > /etc/rhgb/temp/rhgb-console                # -R 表示跳过 / 文件系统,其他选项和之前的一样
         else
                  initlog -c "fsck -T -R -A -a $fsckoptions"
         fi
rc=$?
         if [ "$rc" -eq "0" ]; then                                    #  这部分和之前处理/文件系统的 fsck 结果一样
           success "$STRING"
           echo
        elif [ "$rc" -eq "1" ]; then
           passed "$STRING"
           echo
        elif [ "$rc" -eq "2" -o "$rc" -eq "3" ]; then 
           echo $"Unmounting file systems"
           umount -a
           mount -n -o remount,ro /
           echo $"Automatic reboot in progress."
           reboot -f
        fi

         # A return of 4 or higher means there were serious problems.
         if [ $rc -gt 1 ]; then
             if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
                  chvt 1
             fi

             failure "$STRING"
             echo
             echo
             echo $"*** An error occurred during the file system check."
             echo $"*** Dropping you to a shell; the system will reboot"
             echo $"*** when you leave the shell."

             str=$"(Repair filesystem)"
             PS1="$str \# # "; export PS1
             [ "$SELINUX" = "1" ] && disable_selinux
             sulogin

             echo $"Unmounting file systems"
             umount -a
             mount -n -o remount,ro /
             echo $"Automatic reboot in progress."
             reboot -f
         elif [ "$rc" -eq "1" -a -x /sbin/quotacheck ]; then
             _RUN_QUOTACHECK=1                                        # 如果   fsck 修复成功,则 _RUN_QUOTACHECK 的值修改为1,表示可以进行其他文件系统的 quotacheck 了
         fi        #  这个 fi 是结束  "if [ $rc -gt 1 ]; " 的
fi                # 这个 fi 是结束 ”if [ -z "$fastboot" ];“ 的

########################################################################################################################################################

# Mount all other filesystems (except for NFS and /proc, which is already            # 挂载除了 /,/proc,/proc/bus/usb,devpts 之外的文件系统
# mounted). Contrary to standard usage,
# filesystems are NOT unmounted in single user mode.
action $"Mounting local filesystems: " mount -a -t nonfs,nfs4,smbfs,ncpfs,cifs,gfs -O no_netdev    # 挂载类型为 nonfs、nfs4、smbfs、ncptfs、cifs、gfs 的文件系统

# 但条件是这些文件系统对应在 /etc/fstab 中的属性字段没有 netdev 属性

######################################################################################################################################################## 

# Start the graphical boot, if necessary and not done yet.                # 现在又再次检查是否可以启动图形启动界面
if strstr "$cmdline" rhgb && [ "$RHGB_STARTED" -eq 0 -a "$BOOTUP" = "color" -a "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb ]; then
   LC_MESSAGES= /usr/bin/rhgb
   RHGB_STARTED=1
fi

########################################################################################################################################################

# check remaining quotas other than root                                                            # 对 / 系统之外的文件系统进行 quota 检查
if [ X"$_RUN_QUOTACHECK" = X1 -a -x /sbin/quotacheck ]; then                            # 方法和之前对 / 进行 quotachec 一样
     if [ -x /sbin/convertquota ]; then
         # try to convert old quotas
         for mountpt in `awk '$4 ~ /quota/{print $2}' /etc/mtab` ; do
              if [ -f "$mountpt/quota.user" ]; then
                  action $"Converting old user quota files: " \
                  /sbin/convertquota -u $mountpt && \
                   rm -f $mountpt/quota.user
              fi
              if [ -f "$mountpt/quota.group" ]; then
                  action $"Converting old group quota files: " \
                  /sbin/convertquota -g $mountpt && \
                   rm -f $mountpt/quota.group
              fi
     done
fi

action $"Checking local filesystem quotas: " /sbin/quotacheck -aRnug                # quotachk -augRn 表示对 /etc/fstab 中除了 / 之外,所有启用了 usrquota、grpquota 的
fi                                                                                                                 # 的文件系统都检查

if [ -x /sbin/quotaon ]; then
    action $"Enabling local filesystem quotas: " /sbin/quotaon -aug                       # 执行 quotaon 启动 quota 功能
fi

########################################################################################################################################################

#
# Check to see if SELinux requires a relabel
#
[ -n "$SELINUX" ] && [ -f /.autorelabel ] && relabel_selinux

########################################################################################################################################################

# Initialize pseudo-random number generator                                                    # 初始化随机数生成器
if [ -f "/var/lib/random-seed" ]; then                                                                # 如果存在 /var/lib/random-seed 文件,则
cat /var/lib/random-seed > /dev/urandom                                                            # 把它的内容送给 /dev/urandom
else                                                                                                                # 否则
touch /var/lib/random-seed                                                                                # 创建该文件
fi
chmod 600 /var/lib/random-seed                                                                     # 修改该文件的权限为 600 (rw-------)
dd if=/dev/urandom of=/var/lib/random-seed count=1 bs=512 2>/dev/null           # 从 /dev/urandom 读入 512 kB 并送给 /var/lib/random-seed

# Use the hardware RNG to seed the entropy pool, if available
[ -x /sbin/rngd -a -f /dev/hw_random ] && rngd                                                # 如果 /sbin/rngd 可执行且存在 /dev/hw_random 文件,则执行 rngd 为加密池生成种子

########################################################################################################################################################

# Configure machine if necessary.                                                                # 这部分允许你在启动时作一些配置(手工的)
if [ -f /.unconfigured ]; then                                                                        # 如果存在 /.unconfigured 文件
    if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then                    # 且 rhgb-client 可执行,rhgb 服务在运行,则
         chvt 1                                                                                                                    # 切换回 1# 控制台
    fi

    if [ -x /usr/bin/system-config-keyboard ]; then                                               # 如果 /usr/bin/sysconfig-keyboard 可执行
         /usr/bin/system-config-keyboard                                                                    # 则执行该命令
    fi
    if [ -x /usr/bin/passwd ]; then                                                                        # 如果存在 /usr/bin/passwd ,则
        /usr/bin/passwd root                                                                                        # 执行 passwd root ,修改 root 密码
    fi
    if [ -x /usr/sbin/netconfig ]; then                                                                    # 如果存在 /usr/sbin/netconfig ,则
         /usr/sbin/netconfig                                                                                            #  执行 /usr/sbin/netconfig ,重新配置网络
    fi
    if [ -x /usr/sbin/timeconfig ]; then                                                                    # 如果存在 /usr/sbin/timeconfig ,则
         /usr/sbin/timeconfig                                                                                            # 执行 /usr/sbin/timeconfig 重新配置时间和时区
    fi
    if [ -x /usr/sbin/authconfig ]; then                                                                    # 如果存在 /usr/sbin/authconfig ,则
         /usr/sbin/authconfig --nostart                                                                                # 执行 /usr/sbin/authconfig --nostart 重新配置 shadow、LDAP 、kerberos 等
    fi
    if [ -x /usr/sbin/ntsysv ]; then                                                                            # 如果存在 /usr/sbin/ntsysv ,则
         /usr/sbin/ntsysv --level 35                                                                                    # 执行 /usr/sbin/ntsysv --level 35 对运行级别3,5下的服务启动进行配置
    fi

    # Reread in network configuration data.                                                              # 重新读取 /etc/sysconfig/network 文件
    if [ -f /etc/sysconfig/network ]; then                                                                  #  如果存在该文件就执行它
         . /etc/sysconfig/network

# Reset the hostname.
         action $"Resetting hostname ${HOSTNAME}: " hostname ${HOSTNAME}        # 重新设置主机名,因为上面的 /etc/sysconfig/network 被重新执行了
    fi

    rm -f /.unconfigured                                                                          # 删除 /.unconfigured

    if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then        # 切换回 8# 控制台
         chvt 8
    fi
fi

# Clean out /.                                                                                        # 下面把 / 下的一些文件删除,包括 /fastboot、/fsckoptions、/forcefsck、/.autofsck
rm -f /fastboot /fsckoptions /forcefsck /.autofsck /halt /poweroff &> /dev/null        # /halt、/poweroff ,这样下次重启就会再次检测是否需要 fsck 了

# Do we need (w|u)tmpx files? We don't set them up, but the sysadmin might...        # 正常情况下是需要 /var/run/utmpx 和 /var/log/wtmpx 文件
_NEED_XFILES=                                                                                                        #  _NEED_XFILES 的值为空
[ -f /var/run/utmpx -o -f /var/log/wtmpx ] && _NEED_XFILES=1                                  # 如果存在其中之1个文件,就把 _NEED_XFILES 的值设置为1

########################################################################################################################################################

# Clean up /var.  I'd use find, but /usr may not be mounted.            # 现在清理 /var 目录。
for afile in /var/lock/* /var/run/* ; do                                          # 对于在 /var/lock/ 和 /var/run 下的每个文件,执行下面的操作
if [ -d "$afile" ]; then                                                                        # 如果 $afile 是一个目录,则
    case "$afile" in                                                                                    # 根据 $afile 的值进行选择
  */news|*/mon) ;;                                                                                    # 如果该目录的路径名是以 '/news' 或者 '/mon' 结尾则跳过它们不处理
  */sudo)  rm -f $afile/*/* ;;                                                                        # 如果是以 '/sudo' 结尾,则用 rm -rf 把该目录下面的”所有子目录“下的全部东西删除
  */vmware) rm -rf $afile/*/* ;;                                                                   # 如果是以 '/vmware' 结尾,则用 rm -fr 把它下面的”所有子目录“下的内容全部删除
  */samba) rm -rf $afile/*/* ;;                                                                      # 如果是以 '/samba' 结尾,也是同样操作
  *)  rm -f $afile/* ;;                                                                                   # 如果不是上面4种类型,则把该目录下的所有内容都删除,不仅包括子目录,也包括文件
    esac
else                                                                                                 # 如果 $afile 是一个文件,不是目录,则
    rm -f $afile                                                                                            # 删除该文件
fi
done
rm -f /var/lib/rpm/__db* &> /dev/null                                            # 删除 /var/lib/rpm/_db*

# 补充 :可以看到不要在 /var/run 和 /var/lock 下面放置有用的文件,否则启动时会被删除。

# 正常情况下 /var/run 有如下内容 :

[root@mail run]# ls
acpid.socket=           dbus/           iptraf/         mysqld/     ptal-mlcd/    rpc.statd.pid  syslogd.pid
atd.pid                 dovecot/        klogd.pid       named/      ptal-printd/  saslauthd/     usb/
console/                dovecot-login/  mailman/        netreport/  pvm3/         sendmail.pid   utmp
crond.pid               gpm.pid         mdadm/          news/       quagga/       sm-client.pid  winbindd/
cups-config-daemon.pid  haldaemon.pid   mdmpd/          nscd/       radiusd/      sshd.pid       xfs.pid
cupsd.pid               iiim/           messagebus.pid  ppp/        radvd/        sudo/          xinetd.pid
[root@mail run]#

########################################################################################################################################################

# Reset pam_console permissions                                                                    # 这里重置 pam_console 的权限
[ -x /sbin/pam_console_apply ] && /sbin/pam_console_apply -r                        # 如果 /sbin/pam_console_apply 存在且可以执行,则执行 /sbin/pam_console_apply -r

# -r 是表示 reset 的意思,具体见 man pam_console_apply

########################################################################################################################################################

# 注意下面这段话,一直到后面的 "kill -TERM `/sbin/pidof getkey` >/dev/null 2>&1" 才算结束,这一段放在 {} 中的代码在 bash 中被成为 command block

# 它可以把放在 {} 中的多个命令当成1个命令来执行,这样可以对多个命令一次使用 IO 重定向。

# 实际上,这部分是被放在后台运行的 ,因为它是以  {xxxxx}& 的形式出现的

# 我们可以也可以用函数的形式来把下面的代码做成一个函数,不过 command blocks 是一种简便的方式

{                                                                                                        # command block 开始
# Clean up utmp/wtmp                                                                        # 首先清空 utmp 和 wtmp 文件的内容
> /var/run/utmp                                                                                  # 首先清空 /var/run/utmp 文件
touch /var/log/wtmp                                                                           # 再用 touch 创建 /var/log/wtmp 文件
chgrp utmp /var/run/utmp /var/log/wtmp                                             # 把这两个文件的组都改为 utmp 组
chmod 0664 /var/run/utmp /var/log/wtmp                                            # 把这两个文件的权限改为 0664 (rw-rw-r--)
if [ -n "$_NEED_XFILES" ]; then                                                               #  如果 _NEED_XFILES 的值不为空
  > /var/run/utmpx                                                                                    # 则清空  /var/log/umptx
  touch /var/log/wtmpx                                                                             # 创建 /var/log/wtmpx
  chgrp utmp /var/run/utmpx /var/log/wtmpx                                              # 同样是修改 group 和 permission
  chmod 0664 /var/run/utmpx /var/log/wtmpx
fi

# Clean up various /tmp bits                                                                        # 解析来是清理 /tmp 目录了
rm -f /tmp/.X*-lock /tmp/.lock.* /tmp/.gdm_socket /tmp/.s.PGSQL.*            # 删除 /tmp 下的一些隐藏文件
rm -rf /tmp/.X*-unix /tmp/.ICE-unix /tmp/.font-unix /tmp/hsperfdata_* \
       /tmp/kde-* /tmp/ksocket-* /tmp/mc-* /tmp/mcop-* /tmp/orbit-*  \
       /tmp/scrollkeeper-*  /tmp/ssh-*

# Make ICE directory                                                                                     # 下面创建 ICE 目录
mkdir -m 1777 -p /tmp/.ICE-unix >/dev/null 2>&1                                            # 创建 /tmp/.ICE-unix/ 目录,权限为 1777(sticky)
chown root:root /tmp/.ICE-unix                                                                     # 改为属于 root用户、root 组
[ -n "$SELINUX" ] && restorecon /tmp/.ICE-unix >/dev/null 2>&1       

# Start up swapping.                                               # 下面是启动 swap 空间
update_boot_stage RCswap                                     # 执行 rhgb-client --update RCswap
action $"Enabling swap space: " swapon -a -e           # 启用所有 swap 分区,并跳过那些不存在的 swap 设备

# Set up binfmt_misc                                                                                    # 设置 binfmt_misc
/bin/mount -t binfmt_misc none /proc/sys/fs/binfmt_misc > /dev/null 2>&1     # 挂载 binfmt_misc 文件系统

# Initialize the serial ports.                                    # 初始化串口
if [ -f /etc/rc.serial ]; then                                    # 如果存在 /etc/rc.serial 则执行该文件
. /etc/rc.serial
fi

# If they asked for ide-scsi, load it
if strstr "$cmdline" ide-scsi ; then
modprobe ide-cd >/dev/null 2>&1
modprobe ide-scsi >/dev/null 2>&1
fi

# Turn on harddisk optimization                                                                                        # 下面部分是用 hdparm 命令对硬盘进行优化
# There is only one file /etc/sysconfig/harddisks for all disks                                                # 默认是使用统一的优化参数文件 /etc/sysconfig/harddisks,
# after installing the hdparm-RPM. If you need different hdparm parameters                          # 如果你想对针对不同的硬盘进行优化,把该文件拷贝并重新命名为
# for each of your disks, copy /etc/sysconfig/harddisks to                                                    # /etc/sysconfig/harddisk<hda~hdt> ,并修改该文件
# /etc/sysconfig/harddiskhda (hdb, hdc...) and modify it.
# Each disk which has no special parameters will use the defaults.                                        # 如果某个选项没有指定,则默认使用默认值
# Each non-disk which has no special parameters will be ignored.                                         

# 下面定义一个数组,名为 disk ,共有21个元素
disk[0]=s;
disk[1]=hda;  disk[2]=hdb;  disk[3]=hdc;  disk[4]=hdd;
disk[5]=hde;  disk[6]=hdf;  disk[7]=hdg;  disk[8]=hdh;
disk[9]=hdi;  disk[10]=hdj; disk[11]=hdk; disk[12]=hdl;
disk[13]=hdm; disk[14]=hdn; disk[15]=hdo; disk[16]=hdp;
disk[17]=hdq; disk[18]=hdr; disk[19]=hds; disk[20]=hdt;
if [ -x /sbin/hdparm ]; then                                                                                     # 如果存在 /sbin/hdparm 且可执行,则
   for device in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do                            # 从0-20 循环,每次把一个数字赋予 device 变量
        unset MULTIPLE_IO USE_DMA EIDE_32BIT LOOKAHEAD EXTRA_PARAMS                         # 首先把 MULTIPLE_IO、USE_DMA、EIDE_32BIT、LOOKAHEAD、

# EXTRA_PARAMS 的值清空
        if [ -f /etc/sysconfig/harddisk${disk[$device]} ]; then                                              # 如果存在 /etc/sysconfig/harddisk<hda~hdt> 文件,则
                . /etc/sysconfig/harddisk${disk[$device]}                                                                # 执行该文件
                HDFLAGS[$device]=                                                                                                # 把 HDFLAGS 数组对应 $device 的元素的值清空
                if [ -n "$MULTIPLE_IO" ]; then                                                                                  # 如果 MULTIPLE_IO 的值不为空,则
                    HDFLAGS[$device]="-q -m$MULTIPLE_IO"                                                                    # HDFLAGS 数组对应该 deivce 值的元素的值为 -q -m$MULTIPE_IO
                fi
                if [ -n "$USE_DMA" ]; then                                                                                        # 如果 USE_DMA 的值不为空,则
                    HDFLAGS[$device]="${HDFLAGS[$device]} -q -d$USE_DMA"                                        # HDFLAGS 对应该 deivce 值的元素的值再加上  -q -d$USE_DMA
                fi
                if [ -n "$EIDE_32BIT" ]; then                                                                                      # 如果 EIDE_32BIT 变量的值不为空,则
                    HDFLAGS[$device]="${HDFLAGS[$device]} -q -c$EIDE_32BIT"                                        # HDFLAGS 对应 device 值的元素的值再加上 -q -c$EIDE_32BIT
                fi    
                if [ -n "$LOOKAHEAD" ]; then                                                                                    # 如果 LOOKAHEAD 变量的值不为空,则
                    HDFLAGS[$device]="${HDFLAGS[$device]} -q -A$LOOKAHEAD"                                        # HDFLGAS 对应 device 值的元素的值加上 -q -A$LOOKAHEAD
                fi
                if [ -n "$EXTRA_PARAMS" ]; then                                                                                # 如果 EXTRA_PARAMS 变量的值不为空,则
                    HDFLAGS[$device]="${HDFLAGS[$device]} $EXTRA_PARAMS"                                           # HDFLAGS 对应 device 值的元素的值加上 -q $EXTRA_PARAMS
                fi
        else                                                                                                                        # 如果不存在 /etc/sysconfig/harddisk<hda~hdt> 文件,则
                HDFLAGS[$device]="${HDFLAGS[0]}"                                                                            # 统一使用 HDFLAGS[0] 的值作为每个硬盘的优化参数值
        fi
        if [ -e "/proc/ide/${disk[$device]}/media" ]; then                                                      # 如果存在 /proc/ide/<hda~hdt>/media 文件,则
             hdmedia=`cat /proc/ide/${disk[$device]}/media`                                                    # 则找出它的 media 类型并赋予变俩功能 hdmedia
             if [ "$hdmedia" = "disk" -o -f "/etc/sysconfig/harddisk${disk[$device]}" ]; then        # 如果 hdmedia 的值是 "disk" 或者存在 /etc/sysconfig/harddisk<hda-hdt> 文件
                  if [ -n "${HDFLAGS[$device]}" ]; then                                                                    # 且对应硬盘的参数值不为空,则
                      action $"Setting hard drive parameters for ${disk[$device]}: "  /sbin/hdparm ${HDFLAGS[$device]} /dev/${disk[$device]}    # 调用 action 函数,
                  fi                                                                                                                                                                                    # 执行 /sbin/hdparm 命令,
             fi                                                                                                                                                                                         # 根据给定优化参数值进行优化
        fi
   done
fi            # 注释 :这个 fi 是结束 "if [ -x /sbin/hdparm ];" 的

# Boot time profiles. Yes, this should be somewhere else.                        
if [ -x /usr/sbin/system-config-network-cmd ]; then
  if strstr "$cmdline" netprofile= ; then
    for arg in $cmdline ; do
        if [ "${arg##netprofile=}" != "${arg}" ]; then
     /usr/sbin/system-config-network-cmd --profile ${arg##netprofile=}
        fi
    done
  fi
fi

# Now that we have all of our basic modules loaded and the kernel going,        # 现在所有基础的模块都已经被加载,内核已经开始运行了。可以把这些信息导出了
# let's dump the syslog ring somewhere so we can find it later                        # 这样在启动后可以重新查阅.

# 补充 :在第 125 行,我们执行了 dmesg -n  $LOGLEVEL 命令,把信息都写入 syslog 了。

# 现在把它们从 syslog 中导出来到文件中

dmesg -s 131072 > /var/log/dmesg       # 执行 dmesg -s 131072 > /var/log/dmesg 文件,导出的内容是 131072 字节。

# 默认是 16392 字节

# create the crash indicator flag to warn on crashes, offer fsck with timeout        # 在这里创建 /.autofsck ,如果系统在这里崩溃了,下次重启就会出现 fsck 提示
touch /.autofsck &> /dev/null                    # 用 touch 命令创建 /.autofsck 。不过可以看到前面的第 785 -786行 把 /.autofsck 连同其他 /fastboot 等都删除了

kill -TERM `/sbin/pidof getkey` >/dev/null 2>&1        # 在这里才把 getkey 命令杀死
} &                                                                        # command block 结束

############################################################################################################################################################

if strstr "$cmdline" confirm ; then                                                # 如果内核启动参数含有 "comfirm" ,则
touch /var/run/confirm                                                                 # 创建 /var/run/confirm 文件
fi    
if [ "$PROMPT" != "no" ]; then                                                      # 如果 PROMPT 变量的值不为 no ,则
/sbin/getkey i && touch /var/run/confirm                                    # 调用 getkey 等待用户输入 i ,如果按下 i 则创建 /var/run/confirm
fi
wait                                                                                            # 一旦用户按下任意键,则跳过该行,继续执行下面的代码

if [ -x /sbin/redhat-support-check -a -f /var/lib/supportinfo ]; then        #  如果存在 /sbin/redhat-support-check 且可执行,且存在 /var/lib/supportinfo  文件,则
/sbin/redhat-support-check || {                                                                # 执行该命令,如果不成功则输出”Normal startup will continue in 10 seconds“ ,
   echo $"Normal startup will continue in 10 seconds."                                    # 然后睡眠10秒钟
   sleep 10
}
fi

########################################################################################################################################################

# Let rhgb know that we're leaving rc.sysinit                                            # 到这里 rc.sysinit 就结束了。
if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then             # 执行 rhgb-client --sysinit ,告诉 rhgb 服务器已经完成 rc.sysinit 了
    /usr/bin/rhgb-client --sysinit
fi

########################################################################################################################################################

# -*-Shell-script-*-
# functions This file contains functions to be used by most or all            # 注释 :该脚本几乎被 /etc/init.d/ 下的所有脚本所调用,因为它包含了大量的
#  shell scripts in the /etc/init.d directory.                                           # 的基础函数。同时也被 /etc/rc.d/rc.sysinit ,例如 success、action、failure 等函数
#

TEXTDOMAIN=initscripts                # 设置 TEXTDOMAIN 变量

##########################################################################################################################################################

# Make sure umask is sane            # 确保 root 用户的 umask 是正确的 022 (也就是 rwxr-xr-x)
umask 022

# Set up a default search path.                                        # 设置默认的 PATH 变量
PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin"         # 默认为 /sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin
export PATH                                                                  # 导出为环境变量

# Get a sane screen width                                                # 设置正确的屏幕宽度
[ -z "${COLUMNS:-}" ] && COLUMNS=80                                # 如果 COLUMNS 变量的值为空,则设置为 80 (列)

[ -z "${CONSOLETYPE:-}" ] && CONSOLETYPE="`/sbin/consoletype`"            # 如果 CONSOLETYPE 为空则设置 CONSOLETYPE 为 /sbin/consoletype 命令返回的值

# 一般是 vt 或者 pty 、serial

##########################################################################################################################################################

if [ -f /etc/sysconfig/i18n -a -z "${NOLOCALE:-}" ] ; then                            # 如果存在 /etc/sysconfig/i18n 且 NOLOCALE 变量的值为空,则
      . /etc/sysconfig/i18n                                                                                        # 执行 /etc/sysconfig/i18n 文件,取得 LANG 变量的值
      if [ "$CONSOLETYPE" != "pty" ]; then                                                                    # 如果当前 console 类型不是 pty(远程登录),而是 vt 或者 serial ,则
          case "${LANG:-}" in                                                                                                    # 根据 LANG 的值作出选择
              ja_JP*|ko_KR*|zh_CN*|zh_TW*|bn_*|bd_*|pa_*|hi_*|ta_*|gu_*)                                        # 如果 LANG 是 日文、中文简体、中文繁体、韩文等,则
                   export LC_MESSAGES=en_US                                                                                                # 把 LC_MESSAGES 设置为 en_US
                   export LANG                                                                                                                       # 同时导出为环境变量
                   ;;
              *)
                   export LANG                                                                                                                # 如果是其他类型的语言,则直接导出 LANG
               ;    ;
         esac
      else                                                                                                                    # 如果当前 consle 是 pty
       [ -n "$LC_MESSAGES" ] && export LC_MESSAGES                                                        # 且如果 LC_MESSAGES 不为空,则直接导出 LC_MESSAGES
       export LANG
  fi
fi

##########################################################################################################################################################

# 下面是设置 success、failure、passed、warning 4种情况下的字体颜色的

# Read in our configuration
if [ -z "${BOOTUP:-}" ]; then                                                # 首先如果 BOOTUP 变量为空,则
  if [ -f /etc/sysconfig/init ]; then                                            # 如果存在 /etc/sysconfig/init 文件,执行 /etc/sysconfig/init 文件
      . /etc/sysconfig/init
  else                                                                                    # 否则我们就手工设置
    # This all seem confusing? Look in /etc/sysconfig/init,
    # or in /usr/doc/initscripts-*/sysconfig.txt
    BOOTUP=color                                                                        # 第一设置 BOOTUP 变量,默认就是 color
    RES_COL=60                                                                           # 第二设置设置在屏幕的第几列输出后面的 "[ xxx ]" ,默认是第60列
    MOVE_TO_COL="echo -en \\033[${RES_COL}G"                           # MOVE_TO_COL 是用于打印 "OK" 或者 "FAILED" ,或者 "PASSED" ,或者 "WARNING" 之前的部分,不含 "["
    SETCOLOR_SUCCESS="echo -en \\033[1;32m"                             # SETCOLOR_SUCCESS 设置后面的字体都为绿色
    SETCOLOR_FAILURE="echo -en \\033[1;31m"                              # SETCOLOR_FAILURE 设置后面将要输出的字体都为红色
    SETCOLOR_WARNING="echo -en \\033[1;33m"                            # SETCOLOR_WARNING 设置后面将要输出的字体都为黄色
    SETCOLOR_NORMAL="echo -en \\033[0;39m"                              # SETCOLOR_NORMAL 设置后面输出的字体都为白色(默认)
    LOGLEVEL=1
  fi

  if [ "$CONSOLETYPE" = "serial" ]; then                                        # 如果是通过串口登录的,则全部取消彩色输出
      BOOTUP=serial
      MOVE_TO_COL=
      SETCOLOR_SUCCESS=
      SETCOLOR_FAILURE=
      SETCOLOR_WARNING=
      SETCOLOR_NORMAL=
  fi
fi

##########################################################################################################################################################

if [ "${BOOTUP:-}" != "verbose" ]; then                    # 如果 BOOTUP 变量的值不为 verbose ,则
   INITLOG_ARGS="-q"                                                    # 把 INITLOG_ARGS 的值设置为 -q (安静模式)
else                                                                     # 否则
   INITLOG_ARGS=                                                         # 把 INITLOG_ARGS 的值请空
fi

##########################################################################################################################################################

# Check if $pid (could be plural) are running            # 下面定义一个函数 checkpid (),目的是检查 /proc 下是否存在指定的目录(例如 /proc/1/)
checkpid() {                                                         # 如果有任意一个存在,则返回0;
local i

for i in $* ; do
  [ -d "/proc/$i" ] && return 0
done
return 1                                                             # 如果给出的参数全部不存在对应的目录,则返回1
}

##########################################################################################################################################################

# A function to start a program.                            # 下面定义最重要的一个函数,daemon 函数,它的作用是启动某项服务。/etc/init.d/ 下的脚本的 start 部分都会用到它
daemon() {
# Test syntax.
local gotbase= force=
local base= user= nice= bg= pid=
nicelevel=0
while [ "$1" != "${1##[-+]}" ]; do                                        # daemon 函数本身可以指定多个选项,例如 --check <value> ,--check=<value> ,
   case $1 in
     '')    echo $"$0: Usage: daemon [+/-nicelevel] {program}"    # 也可以指定 nice 值
            return 1;;
     --check)
     base=$2
     gotbase="yes"
     shift 2
     ;;
     --check=?*)
         base=${1#--check=}
     gotbase="yes"
     shift
     ;;
     --user)                                                                        # 也可以指定要以什么用户身份运行(--user <usr> , --user=<usr>)
     user=$2
     shift 2
     ;;
     --user=?*)
            user=${1#--user=}
     shift
     ;;
     --force)
         force="force"                                                            # --force 表示强制运行
     shift
     ;;
     [-+][0-9]*)
         nice="nice -n $1"                                                        # 如果 daemon 的第一个参数是数字,则认为是 nice 值
            shift
     ;;
     *)     echo $"$0: Usage: daemon [+/-nicelevel] {program}"
            return 1;;
   esac
done

        # Save basename.                                                        # basename 就是从服务器的二进制程序的 full path 中取出最后的部分
        [ -z "$gotbase" ] && base=${1##*/}                                       

        # See if it's already running. Look *only* at the pid file.    # 检查该服务是否已经在运行。不过 daemon 函数只查看 pid 文件而已
if [ -f /var/run/${base}.pid ]; then                                         # 如果 /var/run 下存在该服务的 pid 文件,则
  local line p
  read line < /var/run/${base}.pid                                               # 从该 pid 文件每次读取一行,送给变量 line 。注意 pid 文件可能有多行,且不一定都是数字
  for p in $line ; do                                                                    # 对于 line 变量的每个 word 进行检查
   [ -z "${p//[0-9]/}" -a -d "/proc/$p" ] && pid="$pid $p"                     # 如果 p 全部是数字,且存在 /proc/$p/ 目录,则认为该数字是一个 pid ,把它加入到 pid 变量
  done                                                                                       # 到最后 pid 变量的值可能是有多个由空格分隔的数字组成
fi
[ -n "${pid:-}" -a -z "${force:-}" ] && return                                  # 如果 pid 变量最终为空,则 force 变量为空(不强制启动),则返回

# make sure it doesn't core dump anywhere unless requested        # 下面对该服务使用的资源作一些设置
ulimit -S -c ${DAEMON_COREFILE_LIMIT:-0} >/dev/null 2>&1          # ulimit 是控制由该 shell 启动的进程能够使用的资源,-S 是 soft control 的意思,-c 是指最大的 core 

# dump 文件大小,如果 DEAMON_COREFILE_LIMIT 为空,则默认为 0
# if they set NICELEVEL in /etc/sysconfig/foo, honor it                # 如果存在 /etc/sysconfi/foo 文件,且其中有 NICELEVEL 变量则用它代替  daemon 后面的那个 nice 值
[ -n "$NICELEVEL" ] && nice="nice -n $NICELEVEL"                        # 注意,这里的 nice 赋值是用 nice -n <value> 的格式,因为 nice 本身可以启动命令,用这个格式较方便
# Echo daemon                                                                         # 如果 BOOTUP 的值为 verbose ,则打印一个服务名
        [ "${BOOTUP:-}" = "verbose" -a -z "$LSB" ] && echo -n " $base"

# And start it up.                                                                                        # 下面是开始启动它了
if [ -z "$user" ]; then                                                                                  # 如果 user 变量为空,则默认使用 root 启动它
    $nice initlog $INITLOG_ARGS -c "$*"                                                                # 执行 nice -n <nice_value> initlog -q -c "$*"
else                                                                                                         # 如果指定了用户,则
    $nice initlog $INITLOG_ARGS -c "runuser -s /bin/bash - $user -c \"$*\""                # 执行 nice -n <nice_value> initlog -q -c "runuser -s /bin/bash - <user> -c "$*"
fi
[ "$?" -eq 0 ] && success $"$base startup" || failure $"$base startup"                # 如果上面的命令成功,则显示一个绿色的 [ OK ] ,否则显示 [ FAILURE ]
}

##########################################################################################################################################################

# A function to stop a program.                                # 下面定义另外一个很重要的函数 killproc ,/etc/init.d/ 下面的脚本的 stop 部分都会用到它
killproc() {
RC=0                                                                    # RC 是最终返回的值,初始化为 0
# Test syntax.
if [ "$#" -eq 0 ]; then                                            # killproc 函数的语法格式是 killproc <service> [<signal>] ,例如 killproc sm-client 9
  echo $"Usage: killproc {program} [signal]"              
  return 1
fi

notset=0                                                             # noset 是用于检查用户是否指定了 kill 要使用的信号
# check for second arg to be kill level                  
if [ -n "$2" ]; then                                                # 如果 $2 不为空,则表示用户有设定信号,则
  killlevel=$2                                                            # 把 $2 的值赋予 killlevel 变量
else                                                                    # 否则
  notset=1                                                                # notset 变量的值为1,同时 killlevel 为 '-9' (KILL 信号)
  killlevel="-9"
fi

# 补充 :注意,并不是说用户没有指定信号地停止某项服务时,就会立即用 kill -9 这样的方式强制杀死,而是先用 TERM 信号,然后再用 KILL

        # Save basename.
        base=${1##*/}                                                # basename 就是得出服务的名称

        # Find pid.
pid=                                                                    # 把 pid 变量的值清空。注意,不是指 pid 变量的值等于下面脚本的执行结果,要看清楚
if [ -f /var/run/${base}.pid ]; then                         # 下面和上面的 daemon 函数一样找出 pid 
    local line p
    read line < /var/run/${base}.pid
    for p in $line ; do
       [ -z "${p//[0-9]/}" -a -d "/proc/$p" ] && pid="$pid $p"
    done
fi
if [ -z "$pid" ]; then                                                # 不过和 daemon 不同的是,一旦 pid 为空不会直接 return 而是尝试用 pid 命令再次查找
  pid=`pidof -o $$ -o $PPID -o %PPID -x $1 || \            # -o 是用于忽略某个 pid ,-o $$ 是忽略当前 shell 的 pid、-o $PPID 是忽略 shell 的 pid
   pidof -o $$ -o $PPID -o %PPID -x $base`                   # -o %PPID 是忽略 pidof 命令的父进程,要查询的进程是 $1 (fullpath) 或者 $base
fi

# Kill it.                                                            
  if [ -n "${pid:-}" ] ; then                                        # 如果 pid 的值最终不为空,则
    [ "$BOOTUP" = "verbose" -a -z "$LSB" ] && echo -n "$base "    # 且 BOOTUP 的值为 verbose ,且 LSB 变量不为空,则打印一个服务名

    if [ "$notset" -eq "1" ] ; then                                        # 如果 notset 变量不为1,表示用户没有指定信号,则
         if checkpid $pid 2>&1; then                                        # 调用 checkpid  $pid 检查是否在 /proc/ 下存在进程目录,如果有
              # TERM first, then KILL if not dead                            # 先尝试用 TERM 信息,不行再用 KILL 信号
              kill -TERM $pid >/dev/null 2>&1                                # 执行 kill -TERM $pid
              usleep 100000                                                          # usleep 和 sleep 一样,不过单位是百万分之1秒。这里休眠1秒
              if checkpid $pid && sleep 1 &&                                # 如果 checkpid $pid 还是查到有 /proc/<pid>/ 目录存在,则表示还没有杀死,继续等待1秒
                 checkpid $pid && sleep 3 &&                                # 如果1秒后用 checkpid 检查还是有,则再等待3秒;
                 checkpid $pid ; then                                            # 如果还是没有杀死,则用 KILL 信号
                        kill -KILL $pid >/dev/null 2>&1                            # 执行 kill -KILL 杀死它
                        usleep 100000                                                    # 等待1秒种
              fi
    fi
    checkpid $pid                                                             # 再次检查 pid 目录
    RC=$?                                                                         # 并把结果返回给 RC ,这就算是 killproc 的最后状态了
    [ "$RC" -eq 0 ] && failure $"$base shutdown" || success $"$base shutdown"    # 如果 RC 的值为0,则表示kill -9 没有杀死了进程,则调用 failure 函数,否则调用 success 
    RC=$((! $RC))

    # use specified level only                                            # 上面都是在没有指定信号的情况的,下面是用户指定了信号的。例如 restart)或者 reload)部分
    else    # 这个 else 是针对 if [ "$notset" -eq "1" ]  的
       if checkpid $pid; then                                             # 如果检查到进程存在,则
            kill $killlevel $pid >/dev/null 2>&1                            # 执行kill命令,但使用指定的信号 $killlevel
            RC=$?                                                                     # 并把状态值返回给变量 RC
            [ "$RC" -eq 0 ] && success $"$base $killlevel" || failure $"$base $killlevel"    # 如果 RC 为0则表示成功,调用 success;否则调用 failure 函数
       fi
  fi
else      # 这个 else 是针对 if [ -n "${pid:-}" ]  的,也就是说没有 pid 文件,pidof 命令也没有找到 pid ,则
     failure $"$base shutdown"                # 调用 failure 函数,表示停止服务失败
     RC=1                                             # 同时 RC 的值为1
fi

# Remove pid file if any.                    # 根据具体情况可能需要删除 pid 文件
if [ "$notset" = "1" ]; then                    # 如果 notset 不为1 ,也就是用户没有指定信号的情况
            rm -f /var/run/$base.pid            # 自动删除 /var/run 下的 pid 文件
fi
return $RC                                        # 并把 RC 作为 exit status 返回
}

# 补充 :自所以删除 pid 文件只针对 notset 为1 的情况,是因为 -HUP 信号(重读配置),并不杀死进程,所以不能删除它的 pid 文件

# 例如下面 :

[root@mail init.d]# ps -ef |grep xinetd
root      2635     1  0 12:25 ?        00:00:00 xinetd -stayalive -pidfile /var/run/xinetd.pid
[root@mail init.d]# ./xinetd reload
Reloading configuration:                                   [  OK  ]
[root@mail init.d]# ps -ef |grep xinetd
root      2635     1  0 12:25 ?        00:00:00 xinetd -stayalive -pidfile /var/run/xinetd.pid
root      3927  3412  0 16:43 pts/0    00:00:00 grep xinetd
[root@mail init.d]#

可以看到 pid 在 reload 后并没有变

##########################################################################################################################################################

# A function to find the pid of a program. Looks *only* at the pidfile        # 下面的 pidfileofproc 函数和 checkpid 类似,但不执行 pidof 命令,只查询 pid 文件
pidfileofproc() {
local base=${1##*/}
# Test syntax.
if [ "$#" = 0 ] ; then
  echo $"Usage: pidfileofproc {program}"
  return 1
fi

# First try "/var/run/*.pid" files
if [ -f /var/run/$base.pid ] ; then
         local line p pid=
    read line < /var/run/$base.pid
    for p in $line ; do
         [ -z "${p//[0-9]/}" -a -d /proc/$p ] && pid="$pid $p"
    done
         if [ -n "$pid" ]; then
                 echo $pid
                 return 0
         fi
fi
}

##########################################################################################################################################################

# A function to find the pid of a program.                        # 下面的 pidofproc 函数和上面的 pidfileofproc 函数类似,但多了一步 pidof 命令
pidofproc() {
base=${1##*/}

# Test syntax.
if [ "$#" = 0 ]; then
  echo $"Usage: pidofproc {program}"
  return 1
fi

# First try "/var/run/*.pid" files
if [ -f /var/run/$base.pid ]; then
         local line p pid=
  read line < /var/run/$base.pid
  for p in $line ; do
         [ -z "${p//[0-9]/}" -a -d /proc/$p ] && pid="$pid $p"
  done
         if [ -n "$pid" ]; then
                 echo $pid
                 return 0
         fi
fi
pidof -o $$ -o $PPID -o %PPID -x $1 || \
  pidof -o $$ -o $PPID -o %PPID -x $base
}

##########################################################################################################################################################

status() {                                                                  # 注释 :下面的 status 函数是判断服务的状态,总共有4种
local base=${1##*/}
local pid

# Test syntax.
if [ "$#" = 0 ] ; then
  echo $"Usage: status {program}"
  return 1
fi

# First try "pidof"                                                        # 同样是查找 pid 先。直接使用 pidof 命令
pid=`pidof -o $$ -o $PPID -o %PPID -x $1 || \                
      pidof -o $$ -o $PPID -o %PPID -x ${base}`
if [ -n "$pid" ]; then                                                    # 如果 pid 变量的值不为空,则表示找到进程,
         echo $"${base} (pid $pid) is running..."                 # 则打印 "xxx (pid nnn) is running " ,
         return 0                                                             # 并返回 0
fi

# Next try "/var/run/*.pid" files                                    # 如果 pidof 命令没有找到,则尝试从 pid 文件找
if [ -f /var/run/${base}.pid ] ; then
         read pid < /var/run/${base}.pid
         if [ -n "$pid" ]; then                                                # 如果 pidof 命令找不到,但从 pid 文件找到了 pid ,则
                 echo $"${base} dead but pid file exists"                # 打印 "xxx dead but pid file exists",
                 return 1                                                             # 并返回 1
         fi
fi
# See if /var/lock/subsys/${base} exists                            # 如果 pidof 命令和 pid 文件都没有找到 pid ,则
if [ -f /var/lock/subsys/${base} ]; then                              # 如果在 /var/lock/subsys 下存在对应的文件,则
  echo $"${base} dead but subsys locked"                                # 打印 “xxxx dead but subsys locked”,
  return 2                                                                            # 并返回 2
fi
echo $"${base} is stopped"                                                # 如果 pidof 命令、pidf 文件都没有找到pid ,且没有别锁,则打印 “xxx is stopped”
return 3                                                                           # 并返回3
}

##########################################################################################################################################################

# 注释 :下面的 echo_xxx 函数就是真正在屏幕上打印 [ ok ] 、[ PASSED ]、[ FAILURE ]、[ WARNING ] 的部分了

echo_success() {                                                                    # 下面是 echo_success 部分
  [ "$BOOTUP" = "color" ] && $MOVE_TO_COL                            # 首先是打印 “[” 之前的空格
  echo -n "[  "                                                                         # 然后打印 "["
  [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS                    # 设置字体为红色
  echo -n $"OK"                                                                       # 打印 OK
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL                     # 返回字体为白色
  echo -n "  ]"                                                                         # 打印 "]"
  echo -ne "\r"                                                                        # 换行。
  return 0                                                                               # 返回 0,其他一律返回 1

echo_failure() {
  [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
  echo -n "["
  [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
  echo -n $"FAILED"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  echo -n "]"
  echo -ne "\r"
  return 1
}

echo_passed() {
  [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
  echo -n "["
  [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
  echo -n $"PASSED"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  echo -n "]"
  echo -ne "\r"
  return 1
}

echo_warning() {
  [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
  echo -n "["
  [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
  echo -n $"WARNING"
  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
  echo -n "]"
  echo -ne "\r"
  return 1
}

##########################################################################################################################################################

# Inform the graphical boot of our current state
update_boot_stage() {
  if [ "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb-client ]; then
    /usr/bin/rhgb-client --update="$1"
  fi
  return 0
}

##########################################################################################################################################################

# Log that something succeeded                                                
success() {                                                                                                # success 函数除了打印 [ xxx ] 之外,还会使用 initlog 记录信息
  if [ -z "${IN_INITLOG:-}" ]; then
     initlog $INITLOG_ARGS -n $0 -s "$1" -e 1                                                   # -n 是 --name 的意思,-s 是 --string ,-e 是 --event ,1 表示完全成功
  else
     # silly hack to avoid EPIPE killing rc.sysinit
     trap "" SIGPIPE
     echo "$INITLOG_ARGS -n $0 -s \"$1\" -e 1" >&21
     trap - SIGPIPE
  fi
  [ "$BOOTUP" != "verbose" -a -z "$LSB" ] && echo_success
  return 0
}

# Log that something failed
failure() {
  rc=$?
  if [ -z "${IN_INITLOG:-}" ]; then
     initlog $INITLOG_ARGS -n $0 -s "$1" -e 2                                                    # failure 的话 --event 是 2 是失败
  else
     trap "" SIGPIPE
     echo "$INITLOG_ARGS -n $0 -s \"$1\" -e 2" >&21
     trap - SIGPIPE
  fi
  [ "$BOOTUP" != "verbose" -a -z "$LSB" ] && echo_failure
  [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
  return $rc
}

# Log that something passed, but may have had errors. Useful for fsck
passed() {
  rc=$?
  if [ -z "${IN_INITLOG:-}" ]; then
     initlog $INITLOG_ARGS -n $0 -s "$1" -e 1                                                 # passed 的话 --event 还是1
  else
     trap "" SIGPIPE
     echo "$INITLOG_ARGS -n $0 -s \"$1\" -e 1" >&21
     trap - SIGPIPE
  fi
  [ "$BOOTUP" != "verbose" -a -z "$LSB" ] && echo_passed
  return $rc

# Log a warning
warning() {
  rc=$?
  if [ -z "${IN_INITLOG:-}" ]; then
     initlog $INITLOG_ARGS -n $0 -s "$1" -e 1                                                # warning 的话 --event 也是 1
  else
     trap "" SIGPIPE
     echo "$INITLOG_ARGS -n $0 -s \"$1\" -e 1" >&21
     trap - SIGPIPE
  fi
  [ "$BOOTUP" != "verbose" -a -z "$LSB" ] && echo_warning
  return $rc

##########################################################################################################################################################

# Run some action. Log its output.                                                        # action 函数是另外一个最重要的函数,它的作用是打印某个提示信息并执行给定命令
tion() {
  STRING=$1
  echo -n "$STRING "
  if [ "${RHGB_STARTED}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
      echo -n "$STRING " > /etc/rhgb/temp/rhgb-console
  fi
  shift
  initlog $INITLOG_ARGS -c "$*" && success $"$STRING" || failure $"$STRING"
  rc=$?
  echo
  if [ "${RHGB_STARTED}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
      if [ "$rc" = "0" ]; then
       echo_success > /etc/rhgb/temp/rhgb-console
      else
        echo_failed > /etc/rhgb/temp/rhgb-console
[ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
      fi
      echo
  fi
  return $rc
}

##########################################################################################################################################################

# returns OK if $1 contains $2                        # strstr 函数是判断 $1 字符串是否含有 $2 字符串,是则返回0,否则返回1
() {
  [ "${1#*$2*}" = "$1" ] && return 1
  return 0
}

##########################################################################################################################################################

# Confirm whether we really want to run this service                                       # confirm 函数是用于交互式的启动服务
nfirm() {
  [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
  while : ; do 
    echo -n $"Start service $1 (Y)es/(N)o/(C)ontinue? [Y] "                                   # 会打印一个提示信息
    read answer
    if strstr $"yY" "$answer" || [ "$answer" = "" ] ; then                                          # 如果 answer 变量是 y 或者 Y 则
        return 0                                                                                                                # 返回 0(但未真正启动)
    elif strstr $"cC" "$answer" ; then                                                                    # 如果 answer 是 c 或者 C ,则
        rm -f /var/run/confirm                                                                                          # 删除 /var/run/confirm 文件
        [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=no
        return 2                                                                                                                # 返回2
   elif strstr $"nN" "$answer" ; then                                                                      # 如果 answer 是 n 或者 N,则
  return 1                                                                                                                       # 直接返回1
     fi
  done
}

 

#! /bin/bash
#
# rc            This file is responsible for starting/stopping                            # 该脚本主要是用于在切换运行级别时启动/停止服务
#               services when the runlevel changes.
#
# Original Author:      
#               Miquel van Smoorenburg, <[email protected]>
#

# check a file to be a correct runlevel script                # 下面的 check_runlevel 不是检查当前运行级别的意思,而是检查符号连接对应的 target 是否真正存在

#  避免出现无效链接的情况
 

check_runlevel ()
{
# Check if the file exists at all.                                # -x 可以测试该 symbol link ($1)的 target 是否可执行,如果不是则返回1
[ -x "$1" ] || return 1

# Reject backup files and files generated by rpm.      # 如果可以执行,还要检查该脚本是否是备份或者升级 rpm 后留下来的。
case "$1" in                                                             # 根据 $1 的值进行选择
  *.rpmsave|*.rpmorig|*.rpmnew|*~|*.orig)                    # 如果是以 .rpmsave ,。rpmorig、.rpmnew ,~ ,.orig 结尾的,则
   return 1                                                                            # 返回 1
   ;;
esac
return 0                                                                 # 如果 target 存在且可执行,又不是备份文件,则返回 0
}

# Now find out what the current and what the previous runlevel are.            # 找出当前和前一个运行级别
argv1="$1"                                                                                                    # 把目标新运行级别 $1 赋予给 argv1 变量
set `/sbin/runlevel`                                                                                       # 运行 runlevel 命令,把值送给 set 命令,set 默认会把 N 赋予 $1 ,3 赋予 $2
runlevel=$2                                                                                                    # 把 $2 送给 runlevel 变量
previous=$1                                                                                                   # 把 $1 送给 previous 变量
export runlevel previous                                                                                 # 导出这两个变量

# 补充 :注意不要混淆这里的 两个 $1 ,前者是保存新的运行级别,后者是由 set $(runlevel)设置的 $1

. /etc/init.d/functions                                                                               # 重要部分 :执行 /etc/init.d/functions

##########################################################################################################################################################

# 注释 :下面根据是否进入交互来打印不同的标题栏,后面还会用到 /var/run/confirm 文件的。

# See if we want to be in user confirmation mode                                            # 确认是否处于 confirm 模式,也就是要求用户确认是否启动服务
if [ "$previous" = "N" ]; then                                                                             # 如果前一个运行级别是 N ,表示没有改变过运行级别
if [ -f /var/run/confirm ]; then                                                                            # 如果存在 /var/run/confirm 文件(由 rc.sysinit 创建,前提是 $cmdline 有 confirm

                                                                                                                          # 或者用户按下了 i 键
  echo $"Entering interactive startup"                                                                         # 则打印 "Enterinig interactive setup" 提示信息
else                                                                                                                    # 如果不存在 /var/run/confirm 文件,则
echo $"Entering non-interactive startup"                                                                    # 打印 "Entering non-interactive startup" 表示用户在下来的过程中无法干涉
fi
fi

# Get first argument. Set new runlevel to this argument.                                            # 将新运行级别变量赋予变量 runlevel 。

[ -n "$argv1" ] && runlevel="$argv1" # 补充 :这里实际上可以在上面直接把 $1 赋予 runlevel ,因为后面并不

# 需要知道当前的运行级别

# 补充 :一旦机器重启,previous level 总是为 N ,除非你执行了类似 telinit 1 这样的命令,previous level 才会是3或者5

# Is there an rc directory for this new runlevel?                                                         # 检查指定的新运行级别是否有对应的 rc 目录?
[ -d /etc/rc$runlevel.d ] || exit 0                                                                                # 如果存在这个目录就继续,否则返回 0

###########################################################################################################################################################

# 注释 :下面开始执行新 runlevel 的 rc 目录中那些以 K 开头的脚本

# First, run the KILL scripts.
for i in /etc/rc$runlevel.d/K* ; do                                                                                    # 对于在 /etc/rc<new-level>.d/ 目录下的每个以 K 开头的文件
    check_runlevel "$i" || continue                                                                                        # 首先用 check_runlevel 检查符号连接的有效性

# 如果是无效地,则跳过它继续处理下一个符号链接(服务)

     # Check if the subsystem is already up.                                                                            # 下面检查某项服务 $(i) 是否是已经激活的
     subsys=${i#/etc/rc$runlevel.d/K??}                                                                                        # 从符号连接名中取出真正的服务名,也就是K<0-9><0-9> 后面的部分
     [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \                                         # 如果在 /var/lock/subsys 中有同样名称的文件或者 <prog>.init 文件,
          || continue                                                                                                                    # 则执行后面的脚本,如果没有则表示服务停止,无须执行下面的代码

# 补充 :从上面 subsys 变量的赋值中可以看出系统最多只能有 99 个服务,如果是100的话,则 subsys 的值将不正确(会留有一个数字)

# 例如 K100nfs ,经过上面的赋值后,subsys 会变成 0nfs ,而不是 nfs ,这样可能就无法杀死当前运行的 nfsd 进程了

     # Bring the subsystem down.                                       #  把服务停止
    if egrep -q "(killproc |action )" $i ; then                        # 如果脚本中含有 killprog 或者 action 
      $i stop                                                                        # 直接用 <prog> stop 的方式停止它们
    else                                                                            # 如果找不到这两个字符串,则
      action $"Stopping $subsys: " $i stop                                # 用 action 函数,打印 "Stopping  <prog> :" ,然后执行 <prog> stop 
     fi
done                                                                               # 这样先把每个以 K 开头的脚本都执行完

# 补充 :上面搜索 killproc 或者 action 字符串的目的可能是因为 :如果某个脚本不使用 /etc/init.d/functions ,则在停止服务时可能不会

# 显示相关信息,所以先用 action 打印该字符串(stopping xxx),然后再执行 <prog> stop (可能在 <prog> 脚本中的 stop 部分不一定给出

# 相关的提示信息;如果 <prog> 脚本是含有 killproc 或者 action ,则 killproc 自动会调用 success()或者 failure()函数输出提示信息

# Now run the START scripts.                                            # 在上面的 Kill 脚本都执行完后,就执行以 S 开头的脚本了。
for i in /etc/rc$runlevel.d/S* ; do                                    # 首先对于 /etc/rc<new-level>.d/ 目录下的 S 开头的每个文件,都执行下面的代码
check_runlevel "$i" || continue                                            # check_runlevel 确保存在 target ,否则跳过该服务

# Check if the subsystem is already up.                                # 检查该服务是否已经启动,如果启动就跳过下面代码处理下一个服务
subsys=${i#/etc/rc$runlevel.d/S??}
[ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
  && continue
# If we're in confirmation mode, get user confirmation          # 启动服务可以是 confirm 模式,也就是按y启动、按n不启动,按c恢复到非交互模式  
if [ -f /var/run/confirm ]; then                                            # 首先查询是否有 /var/run/confirm 文件存在。
  confirm $subsys                                                                        # 如果存在,就使用 /etc/init.d/functions 的 confirm 函数来处理该服务
  test $? = 1 && continue                                                             # 按y返回0,按n返回1,按c返回2;如果是1的话表示不启动该服务,所以跳过后面代码
fi

# 如果是y或者c则启动。但 c 会取消 continue 模式

update_boot_stage "$subsys"                                                #  调用 update_boot_stage ,执行 rhgb-clinet --update "$subsys"
# Bring the subsystem up.                                                    # 现在可以激活该服务了。注意,上面 confirm $subsys 只是确认是否启动而已,并为真正启动
if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then                 # 如果该服务 $sbusys 是 halt 或者 reboot 服务,则
  export LC_ALL=C                                                                      #  把 LC_ALL 设置为 C
  exec $i start                                                                             # 执行 exec $i start ,也就是用 halt start 或者 reboot start 代替当前进程,注意 exec 的使用
fi
if egrep -q "(daemon |action |success |failure )" $i 2>/dev/null \    # 如果不是上面两个服务之一,且该服务脚本中有 daemon、action、success、failure 等字符串,则
   || [ "$subsys" = "single" -o "$subsys" = "local" ]; then                      # 或者服务为 single 或者 local 服务,则
  $i start                                                                                        # 直接执行 <prog> start ,这次不用 exec 方式了
else                                                                                           # 如果都不是上面的 halt、reboot、single、local 服务,则
  action $"Starting $subsys: " $i start                                                 # 用 action 打印一个 "starting xxx" 的信息,然后执行 $i start 启动该服务
fi
done                                                                                    # 就用这样的方式对每个 S 开头的脚本都处理一次

###########################################################################################################################################################

rm -f /var/run/confirm                                                        # 最后删除 /var/run/confirm 文件,实际上如果上面在启动某个某个服务按下c,则该文件就被删除了
###########################################################################################################################################################

if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then        
  /usr/bin/rhgb-client --quit                                                                # 执行 rhgb-client --quit ,--quit 就是告诉 rhgb 服务器(/usr/sbin/rhgb)退出
fi

你可能感兴趣的:(linux)