Linux系统脚本分析之rc.sysinit

#!/bin/bash
#
# /etc/rc.d/rc.sysinit - run once at boot time

#

# Rerun ourselves through initlog                                                // 通过 /sbin/initlog 命令重新运行自己
if [ -z "$IN_INITLOG" -a -x /sbin/initlog ]; then                            // 条件是 :如果 IN_INITLOG 变量的值不为空,且 /sbin/initlog 可执行
    exec /sbin/initlog -r /etc/rc.d/rc.sysinit                                // 调用 exec /sbin/initlog ,-r 是表示运行某个程序
fi

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

HOSTNAME=`/bin/hostname`                            # 取得主机名
HOSTTYPE=`uname -m`                                    # 取得主机类型
unamer=`uname -r`                                          # 取得内核的 release 版本(例如 2.4.9.30-8)

eval version=`echo $unamer | awk -F '.' '{ print "(" $1 " " $2 ")" }'`            # 取得版本号

if [ -f /etc/sysconfig/network ]; then                # 如果存在 /etc/sysconfig/network ,则执行该文件。

. /etc/sysconfig/network                             # network 文件主要控制是否启用网络、默认网关、主机名
fi

if [ -z "$HOSTNAME" -o "$HOSTNAME" = "(none)" ]; then            # 如果执行 network 文件后 HOSTNAME 为空或者为 "(none)" ,
    HOSTNAME=localhost                                                        # 则将主机名设置为 "localhost"
fi

# Mount /proc and /sys (done here so volume labels can work with fsck)        # 接下来是挂载 /proc 和 /sys ,这样 fsck 才能使用卷标
mount -n -t proc /proc /proc                                                                      #  -n 表示不写 /etc/mtab ,这在 /etc 所在的文件系统为只读时用。因为此时的/还是只读的
[ -d /proc/bus/usb ] && mount -n -t usbfs /proc/bus/usb /proc/bus/usb        # 如果存在 /proc/bus/usb 目录则把 /proc/bus/usb 以 usbfs 挂载到 /proc/bus/usb 下
mount -n -t sysfs /sys /sys >/dev/null 2>&1                                                    # 接下来就是把 /sys 目录以 sysfs 格式挂载到 /sys 目录下

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

. /etc/init.d/functions             # 执行 /etc/init.d/functions 文件,该文件提供了很多有用的函数,具体见 “functions 脚本提供的函数”一文

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

# Check SELinux status                                                       
selinuxfs=`awk '/ selinuxfs / { print $2 }' /proc/mounts`        
SELINUX=                                                                                                    
if [ -n "$selinuxfs" ] && [ "`cat /proc/self/attr/current`" != "kernel" ]; then            
 if [ -r $selinuxfs/enforce ] ; then
  SELINUX=`cat $selinuxfs/enforce`
 else
  # assume enforcing if you can't read it
  SELINUX=1
 fi
fi

if [ -x /sbin/restorecon ] && LC_ALL=C fgrep -q " /dev " /proc/mounts ; then
 /sbin/restorecon  -R /dev 2>/dev/null
fi

disable_selinux() {
 echo "*** Warning -- SELinux is active"
 echo "*** Disabling security enforcement for system recovery."
 echo "*** Run 'setenforce 1' to reenable."
 echo "0" > $selinuxfs/enforce
}

relabel_selinux() {
    if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
 chvt 1
    fi
    echo "
         *** Warning -- SELinux relabel is required. ***
  *** Disabling security enforcement.         ***
  *** Relabeling could take a very long time, ***
  *** depending on file system size.          ***
  "
    echo "0" > $selinuxfs/enforce
    /sbin/fixfiles -F relabel > /dev/null 2>&1
    rm -f  /.autorelabel
    echo "*** Enabling security enforcement.         ***"
    echo $SELINUX > $selinuxfs/enforce
    if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
 chvt 8
    fi
}

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

if [ "$HOSTTYPE" != "s390" -a "$HOSTTYPE" != "s390x" ]; then
  last=0
  for i in `LC_ALL=C grep '^[0-9].*respawn:/sbin/mingetty' /etc/inittab | sed 's/^.* tty\([0-9][0-9]*\).*/\1/g'`; do
        > /dev/tty$i
        last=$i
  done
  if [ $last -gt 0 ]; then
       > /dev/tty$((last+1))
       > /dev/tty$((last+2))
  fi
fi

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

if [ "$CONSOLETYPE" = "vt" -a -x /sbin/setsysfont ]; then            # 下面是设置屏幕的默认字体。如果 CONSOLETYPE 变量的值为 vt 且 /sbin/setsysfont 命令可执行
   echo -n "Setting default font ($SYSFONT): "                            # 打印 "setting deafault font xxxx " ,默认字体应该是 xxxx
   /sbin/setsysfont                                                                   # 执行 /sbin/setsysfont
   if [ $? -eq 0 ]; then                                                                # 如果上述命令执行返回的 exit status 为 0
      success                                                                                # 则调用 success 函数(来自于 functions 脚本),记录一个成功的事件
   else
      failure                                                                                 # 否则调用 failure 函数
   fi
   echo ; echo                                                
fi

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

# Print a text banner.                                                                # 下面部分是打印 "welcome to xxxxx" 的标题栏

echo -en $"\t\tWelcome to "                                                      # 打印 "<tab><tab>Welcom to" ,同时不换行
if LC_ALL=C fgrep -q "Red Hat" /etc/redhat-release ; then           # 从 /etc/redhat-release 文件中找出含有 "Red Hat" 的行,如果找到
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;31m"                        # 则变量 BOOTUP 的值为 color ,并设置输出字体输出红色 
 echo -en "Red Hat"                                                                    # 同时打印 "Red Hat" ,接下来打印发行版本(产品)
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;39m"                        #  如果变量 BOOTUP 的值为 color 则设置输出字体为白色
 PRODUCT=`sed "s/Red Hat \(.*\) release.*/\1/" /etc/redhat-release`    # 从 /etc/redhat-release 中找出含有 "Red Hat" 且后面若干字符,然后是 "release" 的行,并截取中间部分给 PRODUCT

echo " $PRODUCT"                                                                            #  输出变量 PRODUCT 的值(白色)
elif LC_ALL=C fgrep -q "Fedora" /etc/redhat-release ; then             # 如果/etc/redhat-release 中没有 Red Hat 字符串,但有 Fedora ,则执行类似过程
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;31m"
 echo -en "Fedora"
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;39m"
 PRODUCT=`sed "s/Fedora \(.*\) release.*/\1/" /etc/redhat-release`
 echo " $PRODUCT"
else                                                                                            # 如果 /etc/redhat-release 中既没有含 Red Hat 也没有含 Fedora 的行,则
 PRODUCT=`sed "s/ release.*//g" /etc/redhat-release`                    # 找到含有 ‘release' 的行,并把它前面的部分输出,作为 PRODUCT 变量的值并输出
 echo "$PRODUCT"
fi

# 补充 :实际效果是 Red Hat 两个字是红色,其他都是白色

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

if [ "$PROMPT" != "no" ]; then                                                        # 如果变量 PROMPT 的值不为 "no" (表示允许交互启动),则
 echo -en $"\t\tPress 'I' to enter interactive startup."                            # 打印提示信息“Press I to enter interactive startup”,但此时按 I 还未起作用
 echo    
fi

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

# 注释 :下面部分是设置输出到 console 的日志的详细级别

# Fix console loglevel                                                                  # 设置控制台的日志级别
if [ -n "$LOGLEVEL" ]; then                                                            # 如果 LOGLEVEL 变量的值不为空
 /bin/dmesg -n $LOGLEVEL                                                                # 则执行 dmesg ,设置打印到 consoel 的日志的级别为 $LOGLEVEL
fi

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

# 注释 :下面部分是启动 udev 并加载 ide、scsi、network、audio 以及其他类型的设备的模块的部分

[ -x /sbin/start_udev ] && /sbin/start_udev                                    # 如果 /sbin/start_udev 可执行,则执行它,会在屏幕上显示 “Starting udev ... [OK]”

# Only read this once.
cmdline=$(cat /proc/cmdline)                                                        # 读取 /proc/cmdline ,这是内核启动的时的参数,赋予变量 cmdline

# Initialize hardware                                                                     # 下面初始化硬件
if [ -f /proc/sys/kernel/modprobe ]; then                                        # 如果 /proc/sys/kernel/modprobe 文件存在(该文件告诉内核用什么命令来加载模块),则
   if ! strstr "$cmdline" nomodules && [ -f /proc/modules ] ; then            # 如果 $cmdline 变量的值含有 nomodules ,且存在 /proc/modules 文件,则
       sysctl -w kernel.modprobe="/sbin/modprobe" >/dev/null 2>&1            # 使用 sysctl 设置 kernel.modprobe 为 /sbin/modprobe 命令
       sysctl -w kernel.hotplug="/sbin/hotplug" >/dev/null 2>&1                    # 使用 sysctl 设置  kernel.hotplug 为 /sbin/hotplug 命令
   else                                                                                           # 如果不存在 /proc/sys/kernel/modprobe 文件,则                             
       # We used to set this to NULL, but that causes 'failed to exec' messages"    
       sysctl -w kernel.modprobe="/bin/true" >/dev/null 2>&1                # 使用 sysctl 设置 kernel.modprobe 为 /bin/true
       sysctl -w kernel.hotplug="/bin/true" >/dev/null 2>&1                    # kernel.hotplug 也一样
   fi
fi

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

# 注释 :下面开始真正的加载各种类型的设备的驱动

# 首先是找出那些模块需要被加载,并把模块名按类分别放到 ide、scsi、network、audio、other 5个变量中

echo -n $"Initializing hardware... "                                                    # 打印 "initalizing hardware.." (不换行),并开始初始化硬件

ide=""                                                                                              # 下面这些变量都是为了存储的型号,格式为 " <module1> <module2> <module3> ..."           
scsi=""
network=""
audio=""
other=""
eval `kmodule | while read devtype mod ; do                                # 从 kmodule 命令的输出一次读入两个变量 devtype (设备类型)和 mod(模块名)
 case "$devtype" in                                                                            # 根据 devtype 做出适当的选择 
  "IDE") ide="$ide $mod"                                                                            # 如果 devtype 的值为 IDE ,则把 mod 加入到现有的 ide 变量的值中 
     echo "ide=\"$ide"\";;                                                                            # 输出 "ide=" 然后是变量 ide 的值
  "SCSI") scsi="$scsi $mod"                                                                          # 如果 devtype 的值为 SCSI ,则把 mod 变量的值加入到 scsi 变量的值中
     echo "scsi=\"$scsi"\";;                                                                           # 输出 "scsi=" 再输出 $scsi  
  "NETWORK") network="$network $mod"                                                    # 下面的 NETWORK 和 AUDIO 也一样
     echo "network=\"$network"\";;
  "AUDIO") audio="$audio $mod"
     echo "audio=\"$audio"\";;
  *) other="$other $mod"                                                                            # 如果是属于 other 类型的则把模块名 $mod 加入到 other 变量的值列表中
     echo "other=\"$other"\";;
 esac
done`

load_module () {                    # 定义一个函数名为 load_module
 LC_ALL=C fgrep -xq "$1" /etc/hotplug/blacklist 2>/dev/null || modprobe $1 >/dev/null 2>&1        # 在 /etc/hotplug/blacklist 文件中查找是否有指定的模块,如果没有,
}                                                                                                                                            # 再用 modprobe 加载指定的模块

# IDE                                        # 下面开始加载所有 IDE 类型的设备的模块
for module in $ide ; do                 # 从 ide 变量中每次取出1个值,赋予变量 module 
 load_module $module                 # 再用 load_module 函数加载它。如果该模块存在于 /etc/hotplug/blacklist ,则不会被加载(因为存在于黑名单中)
done

# SCSI                                    # SCSI 方面的比较特殊,除了加载 scsi 变量中的模块外,还会从 modprobe -c (显示所有配置)中找出 scsi 卡(hostadapter)的模块
for module in `/sbin/modprobe -c | awk '/^alias[[:space:]]+scsi_hostadapter[[:space:]]/ { print $3 }'` $scsi; do   # 从 modprobe -c 中找出所有scsi卡的模块别名,
 load_module $module             # 并和 scsi 变量一起,通过 for 循环一一加载
done

load_module floppy                # 然后加载 floppy 模块

echo -n $" storage"                # 输出 "storage" ,表示存储方面的模块加载已经完成

# Network                            # 接下来是加载网络设备模块

pushd /etc/sysconfig/network-scripts >/dev/null 2>&1        # pushd 是一个 bash 的内建命令,把目录名放入目录堆栈的顶部,并进入指定目录
interfaces=`ls ifcfg* | LC_ALL=C egrep -v '(ifcfg-lo|:|rpmsave|rpmorig|rpmnew)' | \        # 找出 network-scripts 目录下所有 lscfg 开头的文件名,排除指定备份和loopback
            LC_ALL=C egrep -v '(~|\.bak)$' | \                                                                    # 排除以 ~ 和 .bask 结尾的文件名
            LC_ALL=C egrep 'ifcfg-[A-Za-z0-9\._-]+$' | \                                                       # 找出所有以 ifcfg- 开头,并以多个字母/数字结尾的文件名
     sed 's/^ifcfg-//g' |                                                                                                  # 把前缀 ifcfg- 去掉,只留下后面的接口名
     sed 's/[0-9]/ &/' | LC_ALL=C sort -k 1,1 -k 2n | sed 's/ //'`                                         # 在数字前面加上空格,是类型和编号分离,便于 sort 排序,然后再组合回去

# 目的是按顺序启动接口

for i in $interfaces ; do                                                                                                # 对于在 $interfaces 变量中的每个接口
 eval $(LC_ALL=C fgrep "DEVICE=" ifcfg-$i)                                                                     # 从对应的文件 ifcfg-$i 找出 DEVICE= 行
 load_module $DEVICE                                                                                                 # 然后用 load_module 加载它,注意,DEVICE= 行可以是别名,例如 eth1.n7css
done
popd >/dev/null 2>&1                                                                                                 # 把 network-scripts 从目录名堆栈中弹出,并返回原来的目录

for module in $network ; do                                                                                        #  剩下还有 network 变量中的模块
 load_module $module                                                                                                # 也一一加载
done

echo -n $" network"                                                                                                    # 输出 network 字符串,表示加载网络设备模块工作完毕

# Sound
for module in `/sbin/modprobe -c | awk '/^alias[[:space:]]+snd-card-[[:digit:]]+[[:space:]]/ { print $3 }'` $audio; do    # 和 scsi 一样,加载在 modprobe -c 和 audio 变量
 load_module $module                                                                                                                                          # 中的模块
done

echo -n $" audio"                                                                                                        # 输出 audio ,表示声卡设备驱动加载完毕,一般会有多个驱动被加载

# Everything else (duck and cover)                                                                             # 剩下的就是 other 类型的设备了
for module in $other ; do
 load_module $module                                                                                                # 这个直接用 load_moudle 函数加载了
done

echo -n $" done"                                                                                                        # 输出 done 表示完成
success                                                                                                                    # 调用 success 函数,记录该事件
echo

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

# 注释 :下面是启用 Software-RAID 的检测

echo "raidautorun /dev/md0" | nash --quiet       # 用 nash (小型脚本解释器)执行 "raidautorun /dev/md0" 命令,对所有paritition ID 为 0xFD 分区进行检查

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

# Start the graphical boot, if necessary; /usr may not be mounted yet, so we             # 显示图形模式的启动画面,但由于 rhgb 命令在 /usr 分区上,
# may have to do this again after mounting                                                             #  这时 /usr 分区还没有被挂载,所以在挂载后重新做一遍这部分脚本
RHGB_STARTED=0           # 初始化 RHGB_STARTED 变量的值为 0,表示尚未启动。RHGB 是 redhat graphical boot 的含义,
mount -n /dev/pts         # 挂载 /dev/pts ,类型是 devpts ,目的是获得 pty ,这是一种伪文件系统

if strstr "$cmdline" rhgb && [ "$BOOTUP" = "color" -a "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb ]; then        # 如果内核启动参数 $cmdline 含有 rhgb ,且

# BOOTUP 变量的值为 color ,且 GRPAHICAL 变量的值为 yes

# 且 /usr/bin/rhgb 文件存在并可执行
   LC_MESSAGES= /usr/bin/rhgb                                                                                                           # 把 "/usr/sbin/rhgb" 赋予变量 LC_MESSAGES
   RHGB_STARTED=1                                                                                                                            # RHGB_STARTED 变量的值为1。
fi

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

# 注释 :下面部分是使用 sysctl 设置内核的参数

# Configure kernel parameters                    
update_boot_stage RCkernelparam             # 调用 update_boot_stage 函数,该函数由 /etc/rc.d/init.d/functions 脚本定义,主要执行 "/usr/sbin/rhgb-client --update $1"
action $"Configuring kernel parameters: " sysctl -e -p /etc/sysctl.conf    # 调用 action 函数,执行 sysctl 命令,它读取 /etc/sysctl.conf ,同时输出 “Configuring kernel

# parameters” 字符串,action 函数也是由 /etc/rc.d/init.d/functions 脚本定义

# 补充 :action 函数的作用是把第一个参数 $1 赋予变量 string 并输出到屏幕,同时把该字符串也写入 /etc/rhgb/temp/rhgb-console

# 然后把 $1 后面的部分作为命令交给 initlog 执行,并根据结果调用 success 或者 failure 函数。而 success 或者 failure 函数又会根据结果输出 [ OK ] 或者 [ FAILED ] 。

# 除了 success 和 failure 函数外,functions 脚本还定义了 passed 和 warnning 函数,它们又分别调用 echo_passwd() 和 echo_warnning()函数,

# 以输出 [ PASSED ] 和 [ WARNNING ]

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

# 注释 :下面设置系统的硬件时钟

# Set the system clock.                    # 接下来设置系统时钟
update_boot_stage RCclock                # 同样是告诉 update_boot_stage 该事件(执行 /usr/bin/rhgb-client --update="$1" 命令,这里 $1 就是 "RCclock")
ARC=0                                                
SRM=0
UTC=0

if [ -f /etc/sysconfig/clock ]; then        # 如果存在 /etc/sysconfig/clock 则执行该文件
   . /etc/sysconfig/clock

# convert old style clock config to new values
   if [ "${CLOCKMODE}" = "GMT" ]; then                        # 如果变量 CLOCKMODE 为旧式的 GMT 格式
      UTC=true                                                                # 则 UTC 变量的值为 ture 
   elif [ "${CLOCKMODE}" = "ARC" ]; then                     # 如果 CLOCKMODE 的值是 ARC ,则
      ARC=true                                                                # ARC 的值为 true
   fi                                                                         # 如果 CLOCKMODE 不等于 GMT 或者 UTC ,则 UTC 的值等于 /etc/sysconfig/clock 中的值,一般都是 false
fi                                                                            # 如果 CLOCKMODE 不等于 GMT 后者 ARC ,则 UTC 的值为 0

CLOCKDEF=""                                                            # CLOCKDEF 变量的值默认为空
CLOCKFLAGS="$CLOCKFLAGS --hctosys"                       # 而 CLOCKFLAGS 变量的值为原来的值(可能为空)加上 '--hctosys' ,表示把硬件时钟写入内核时钟

case "$UTC" in                                                          # 根据 UTC 变量的值进行选择                                             
    yes|true) CLOCKFLAGS="$CLOCKFLAGS --utc"             # 如果 UTC 的值为 yes 或者 true,则变量 CLOCKFLAGS 的值加上 --utc ,表示采用 UTC 时区
  CLOCKDEF="$CLOCKDEF (utc)" ;;                                     # 同时变量 CLOCKDEF 的值加上 "(utc)"
    no|false) CLOCKFLAGS="$CLOCKFLAGS --localtime"     # 如果变量 UTC 的值为 false 或者 no,则
  CLOCKDEF="$CLOCKDEF (localtime)" ;;                        # CLOCKDEF 的值加上 "(localtime)" 。由于安装 OS时没有选择 UTC,CLOCKDEF 的值一般最后就是 (localtime) 而已
esac
case "$ARC" in                                                          # 根据 ARC 变量的值来选择              
    yes|true) CLOCKFLAGS="$CLOCKFLAGS --arc"            # 如果是 yes 或者 true ,则 CLOCKFLAGS 的值加上 --arc ,CLOCKDEF 的值加上 '(arc)'
  CLOCKDEF="$CLOCKDEF (arc)" ;;                                # 如果 ARC 的值为 0 ,则 CLOCKFLAGS 和 CLOCKDEF 的值都不变
esac
case "$SRM" in                                                         # 如果 SRM 的值是 yes 或者 true ,则 CLOCKFLAGS 的值加上 "--srm" ,CLOCKDEF 加上 "(srm)"
    yes|true) CLOCKFLAGS="$CLOCKFLAGS --srm"
  CLOCKDEF="$CLOCKDEF (srm)" ;;
esac

/sbin/hwclock $CLOCKFLAGS                    # 执行 hwclock $CLOCKFLAGS 命令,一般就是 /sbin/hwclock --hctosys

action $"Setting clock $CLOCKDEF: `date`" date        # 并输出 "setting clock " 和 $CLOCKDEF ,并执行 date 输出当前时间,然后输出 [ OK ]

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

# 注释 :下面部分是设置 key map

if [ "$CONSOLETYPE" = "vt" -a -x /bin/loadkeys ]; then            # 接下来是设置 keymap ,一般都是 us 
 KEYTABLE=                                                                        #  设置 KEYTABLE 和 KEYMAP 
 KEYMAP=
 if [ -f /etc/sysconfig/console/default.kmap ]; then                # 假如存在 /etc/sysconfig/console/default.kmap ,则
  KEYMAP=/etc/sysconfig/console/default.kmap                            # KEYMAP 变量的值为 /etc/sysconfig/console/default.kmap
 else                                                                                    # 否则
  if [ -f /etc/sysconfig/keyboard ]; then                                        # 如果存在 /etc/sysconfig/keyboard 文件,则执行它
    . /etc/sysconfig/keyboard                                                       # 该文件设置 KEYTABLE 变量的值,例如 KEYTABLE="/usr/lib/kbd/keytables/us.map
  fi
  if [ -n "$KEYTABLE" -a -d "/lib/kbd/keymaps" ]; then              # 如果 KEYTABLE 的值不为空,且存在 /lib/kbd/keymaps/ 目录,则
     KEYMAP="$KEYTABLE.map"                                                # KEYMAP 的值为 KEYTABLE 的值加上 ".map" 
  fi
 fi
 if [ -n "$KEYMAP" ]; then                                                                 # 假如 KEYMAP 变量的值不为空(间接表示 KEYTABLE 不为空且存在 keymaps 目录)
  # Since this takes in/output from stdin/out, we can't use initlog
  if [ -n "$KEYTABLE" ]; then                                                                    # 且 KEYTABLE 的值不为空
    echo -n $"Loading default keymap ($KEYTABLE): "                                     # 则输出 "Loading default keymap xxxx"
  else
    echo -n $"Loading default keymap: "                                                    # 否则只输出 Loading default keymap
  fi    
  loadkeys $KEYMAP < /dev/tty0 > /dev/tty0 2>/dev/null && \            # 接下来用 loadkeys 加载 $KEYMAP 指定的键盘映射文件。
     success $"Loading default keymap" || failure $"Loading default keymap"        # 如果成功则调用 success 函数,否则调用 failure 函数
  echo
 fi
fi

############################################################################################################### 注释 :下面部分是设置主机名

# Set the hostname.                                # 接下来是设置主机名
update_boot_stage RChostname                # 告诉 update_boot_stage 函数该事件,以执行 /usr/sbin/rhgb-client --update RChostname 命令
action $"Setting hostname ${HOSTNAME}: " hostname ${HOSTNAME}        # 打印 "setting hostname xxxx" 字符串,并执行 hostname ${HOSTNAME}设置主机名

#  注意,HOSTNAME 变量前面已经有过赋值的了,就是 `/bin/hostname` 命令返回的值

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

# 注释 :下面设置 ACPI 部分

# Initialiaze ACPI bits
if [ -d /proc/acpi ]; then                                                                    # 如果存在 /proc/acpi 目录,则
   for module in /lib/modules/$unamer/kernel/drivers/acpi/* ; do            # 对于在 /lib/modules/<kernel-version>/kernel/drivers/acpi/ 目录下的所有模块文件,
      insmod $module >/dev/null 2>&1                                                        # 都用 insmod 命令加载到内核中
   done
fi

############################################################################################################### 注释 :下面是主要的部分,就是确认是否需要对 / 文件系统进行 fsck

if [ -f /fastboot ] || strstr "$cmdline" fastboot ; then      # 接下来是看对 / 系统进行 fsck 的时候了。如果不存在 /fastboot 文件则看 cmdline 变量是否含有 fastboot 字符串
 fastboot=yes                                                              # 如果有,则 fastboot 变量的值为 yes。/fastboot 是由 shutdown -f 创建的,表示重启时不作 fsck
fi

if [ -f /fsckoptions ]; then                                            # 如果存在 /fsckoptions 文件,则把文件的内容赋予变量 fsckoptions 变量
 fsckoptions=`cat /fsckoptions`
fi

if [ -f /forcefsck ] || strstr "$cmdline" forcefsck ; then        # 如果不存在 /forcefsck 文件,则检查 cmdline 变量是否含有 forcefsck 字符串,如果有
 fsckoptions="-f $fsckoptions"                                            # 则在 fsckoptions 的值前面加上 "-f" 。/forcefsck 是由 shutdown -F 创建的,强制 fsck
elif [ -f /.autofsck ]; then                                                 # 如果存在 /.autofsck
      if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then        #  且文件 /usr/bin/rhgb-clinet 可执行,且 /usr/bin/rhgb 服务在运行(--pnig),则
           chvt 1                                                                                                #  切换到虚拟控制台 #1 (主要是显示 fsck 的信息用)
       fi
       echo $"Your system appears to have shut down uncleanly"                            # 并显示需要 fsck 的信息 “You system appears to have shut down uncleanly”
       AUTOFSCK_TIMEOUT=5                                                                              # 设置超时时间为5秒
      [ -f /etc/sysconfig/autofsck ] && . /etc/sysconfig/autofsck                           # 如果存在 /etc/sysconfig/autofsck 则执行它
      if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                 # 如果执行该文件后 AUTOFSCK_DEF_CHECK 变量的值为 yes ,则
          AUTOFSCK_OPT=-f                                                                                        # 变量 AUTOFSCK_OPT 的值为 "-f"
      fi

if [ "$PROMPT" != "no" ]; then                                                                // 如果 PROMPT 的值不为 no ,则
        if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                // 且 AUTOFSCK_DEF_CHECK 的值为 yes
           if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press N within %d seconds to not force file system integrity check..." n ; then    //  执行 getkey 命令,超时5秒,并

// 提示5秒内按下 n 键可跳过 fsck
              AUTOFSCK_OPT=        // 如果用户按下 n ,则 AUTOFSCK_OPT 的值为空,表示取消自动 fsck
           fi
        else                    // 如果 AUTOFSCK_DEF_CHECK 的值不为 yes ,则提示5秒内按y 可以强制 fsck
          if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press Y within %d seconds to force file system integrity check..." y ; then        // 同样是用 getkey 捕捉 y 键
              AUTOFSCK_OPT=-f        // 如果用户按下了 y 键,则 AUTOFSCK_OPT 的值为 "-f"
          fi
       fi
       echo
      else         // 如果 PROMPT 的值为 "no", 这是用户无法选择是否 fsck 
        # PROMPT not allowed
        if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                # 取决于 AUTOFSCK_DEF_CHECK 变量的值,如果是 yes ,则
          echo $"Forcing file system integrity check due to default setting"     # 提示由于默认的设置强制 fsck 。可能是 mount 次数达到限制,或者多长时间没有 fsck 了
        else                                            
          echo $"Not forcing file system integrity check due to default setting"            # 如果 AUTOFSCK_DEF_CHECK 的值为 no ,则表示不做 fsck 
        fi
      fi
      fsckoptions="$AUTOFSCK_OPT $fsckoptions"
fi

# 注释 :注意!到这里为止并没有执行 fsck 操作,只是判断是否需要执行 fsck 以及 fsck 命令的选项

if [ "$BOOTUP" = "color" ]; then       # 如果 BOOTUP 的值为 color ,则
 fsckoptions="-C $fsckoptions"            # -C 表示显示 fsck 进度信息
else                                              # 否则
 fsckoptions="-V $fsckoptions"            # -V 表示显示 verbose 信息
fi

if [ -f /etc/sysconfig/readonly-root ]; then            # 如果存在 /etc/sysconfig/readonly-root ,则
    . /etc/sysconfig/readonly-root                            # 执行该文件

if [ "$READONLY" = "yes" ]; then                            # 如果 READONLY 变量的值为 yes ,则
        # Call rc.readonly to set up magic stuff needed for readonly root        # 执行 /etc/rc.readonly 脚本
        . /etc/rc.readonly
    fi
fi

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

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

_RUN_QUOTACHECK=0                                            # 初始化 RUN_QUOTACHECK 的值为0.这里是作一个标记,因为后面的代码可能会导致重启。在这里做好标记

# 如果后面没有重启,就可以把 _RUN_QUOTACHECK 设置为1
ROOTFSTYPE=`awk '/ \/ / && ($3 !~ /rootfs/) { print $3 }' /proc/mounts`        # 从 /proc/mounts 中找出 / 文件系统的类型,并赋予变量 ROOTFSTYPE

# 注释 :下面用到 -z "$fastboot" ,如果有 /fastboot ,则 fastboot 的值为 yes, 所以就不合 if 的条件,则跳过 fsck 。这就是 shutdown -f 快速启动的效果

if [ -z "$fastboot" -a "$READONLY" != "yes" -a "X$ROOTFSTYPE" != "Xnfs" -a "X$ROOTFSTYPE" != "Xnfs4" ]; then     # 如果fastboot 变量的值为空且  READONLY 变量不为 yes

# 且 / 文件系统的类型不是 nfs 或者 nfs4 ,则

STRING=$"Checking root filesystem"    # 初始化 string 变量并输出 "checking root filesystem" 
        echo $STRING

rootdev=`awk '/ \/ / && ($3 !~ /rootfs/) {print $1}' /proc/mounts`        # 从 /proc/mounts 中找出 / 文件系统所在的设备,并赋予 rootdev 变量(例如 /dev/hdb2)
         if [ -b /initrd/"$rootdev" ] ; then        # 如果 /initrd/$rootdev 是一个 block 设备
              rootdev=/initrd/"$rootdev"                    # 则 rootdev 就是 /initrd/rootdev 
         else                                                 # 否则 rootdev 就是 / ,正常情况应该是执行该步的,也就是 rootdev 最终为 /
         rootdev=/                                       # rootdev 的值为 / ,因为 fsck 支持用 LABEL、mount point、device name 来指定要检查的文件系统 
         fi

# 注释 : 下面开始真正的 fsck 操作

if [ "${RHGB_STARTED}" != "0" -a -w /etc/rhgb/temp/rhgb-console ]; then        # 如果 RHGB_STARTED 不为0且 /etc/rhgb/tmp/rhgb-console 文件可写,则
              fsck -T -a $rootdev $fsckoptions > /etc/rhgb/temp/rhgb-console           # 执行 fsck 命令,-T 表示不打印标题栏,-a 表示自动修复错误,设备是 / ,后面是选项

# 且信息写入 /etc/rhgb/temp/rhgb-console
         else                                                                                                           # 否则
              initlog -c "fsck -T -a $rootdev $fsckoptions"                                                        # 调用 initlog 执行 fsck ,信息直接输出到屏幕,fsck 命令本身都是一样的。
        fi                                                                                                                     
        rc=$?                                    # 把 fsck 的结果送给变量 rc

if [ "$rc" -eq "0" ]; then         # 如果 rc的值为0,表示 fsck 成功通过
              success "$STRING"                    # 则调用 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

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

Linux系统启动脚本分析之rc

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

# 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启动脚本分析之functions

# -*-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
}

Linux系统开机过程详细分析

各位是否曾经对电脑整个开机的流程感到好奇呢 ? 这一次 , 我们所要讨论的 
主题 , 就是 Linux 从开机的一瞬间到 login 为止 , 到底发生了什么事情 ?

想必各位都知道 , 在刚开机时 , 由于 80x86 的特性 , CS ( Code Segment ) 
这个寄存器中全部都放着 1 , 而 IP ( Instruction Pointer ) 这个寄存器 
中全部都放着 0 , 换句话说 , CS=FFFF 而 IP=0000 , 此时 , CPU 就依据 
CS 及 IP 的值 , 到 FFFF0H 去执行那个地方所放的指令 . 这时候 , 由于 
FFFF0H 已经到了高位址的顶端 , 所以 , FFFF0H 这个地方 , 总是会放一个 
JMP 指令 , 跳到比较低的位址 . 接著 , ROM BIOS 就会作一些检查的动作 
像内存 , 键盘 等...... 并在我们俗称的 UMB ( Upper Memory Block ) 
之中扫描 , 看看是否有合法的 ROM 存在 ( 比如 SCSI 卡上的 ROM ) . 
假如有 , 就到里面去执行一些东西 , 执行完之后再继续刚才的行程 . 到了 
最后 , 读取硬盘上的第一个 sector . 在这里 , 我假设各位由硬盘启动 
因此 , 就硬盘的构造而言 , 它的第一个 sector 称为 MBR ( Master Boot 
Record ) . 因为一个 sector 是 512 bytes , 而 MBR 这 512 bytes 可分 
为两个部份 , 第一个部份为 Pre-Boot 区 , 占了 446 bytes ; 第二部份 
是 Partition Table , 占了 66 bytes . Pre-Boot 区的作用之一 , 就是 
去看看那个 Partition 被标成 Active , 然後去读那个 Partition 的 Boot 
区 .

在 Linux 的启动方面 , 一般人最常把 LILO 放在 MBR 或 Superblock 
假如你把 LILO 放在 MBR , 那很明显的 , 当读取到 MBR 的时候 , LILO 
就被执行 , 此时 , 你的屏幕上会出现 boot: 接着 , 就进行 Load Kernel 
的动作 . 在另一方面来说 , 假如你把 LILO 安装在 Superblock , 通常你 
还会有一个管理开机的程序 , 也许是放在 MBR ( 像 OSBS ) 或者是放在一 
个单独的 Partition ( 像 OS/2 的 Boot Manager ) . 再由这个管理开机 
的程式去读取 LILO , 进而做 Load Kernel 的动作 .

好了 , 到了目前为止 , 我们已经讲到 Load Kernel 的动作 . Kernel 被 
load 到 memory 中之后 , 接着进行一连串 probe 周边的动作 , 像串口 
并口 , 软盘 , 声卡 , 硬盘 , 光驱 等 ...... 接着 mount root 
partition . 在这之后 , kernel 会起动 init 这个 process . init 这 
个 process 的 PID 为 1 , 它是所有 process 的祖先 .

接下来呢 ? 系统就开始执行 /rc.d/rc.S , 在这里 , 我们暂时打住 , 
先对大概的 initialization script 执行的顺序作一个浏览 , 请看下面 
的流程 :

init[1] 
rc.S begin <--- 目前我们已经讲到这里 
rc.serial begin 
rc.serial end 
rc.S end 
init[1] enter runlevel 5 
rc.M begin 
rc.inet1 begin 
rc.inet1 end 
rc.inet2 begin 
rc.inet2 end 
rc.font begin 
rc.font end 
rc.local begin 
rc.local end 
rc.M end 
login

上面的流程清楚的指出 , 在 rc.S 这个 shell script 中 , 会去执行 rc.serial 
接着再执行 rc.M , rc.M 中又包含了 rc.inet1 , rc.inet2 , rc.font , rc.local 
最后执行 login . 在下面的内容中 , 将为各位介绍这几个 shell script 
从下面开始 , 凡是每一列之前有一个 # 的 , 为原来 shell script 中的注释 
有两个 # 的 , 为笔者加上的注释 , 当然啦 , 没有任何 # 的为 shell script 
的内容 , 而对命令或内容的解释 , 一律都写在命令或内容的前面 . 
首先由 rc.S 开始 :

***************************** rc.S **********************************

#!/bin/sh 

# /etc/rc 

# These commands are executed at boot time by init(8). 
# User customization should go in /etc/rc.local.

echo '======== rc.S is running ! System Initializing Now !!! ========' 
PATH=/sbin:/usr/sbin:/bin:/usr/bin

## 打开所有 swap ! 下面 /sbin/swapon -a 的意思是 : 使得 /etc/fstab 中被记录 
## 成 swap 的 device 全部启动 .

/sbin/swapon -a 
?

## 喔 ! 下面这个指令 update 就很重要了 , 它负责每隔一段固定的时间 , 就将 
## buffer 中的资料 , 利用 sync 写回磁盘上 , 并将 superblock 做 update 
## 的动作 . 使用 ps 这个指令看看有那些 process , 就可看到 update 还有一个 
## bdflush , 这两个 process 都是必然要存在的 , 可不要随便砍掉 , 要不然 , 
## 万一系统 crash 了 , 那磁盘里面的资料就不是最新的了 ......

/sbin/update & 
?

## 利用 echo -n >;>; 制造一个文件 , 并用 rm -f 这个档案来测试 root partition 
## 是不是 read-only 或者是可读写

READWRITE=no 
if echo -n >;>; "Testing filesystem status"; then 
rm -f "Testing filesystem status" 
READWRITE=yes 
fi 
?

## 假如 root partition 是 read-only 就作 fsck 的动作 , 假如不是 read-only 
## 而是 read-write 的话 , 就做下面 else 之后的动作

if [ ! $READWRITE = yes ]; then 
## 利用 fsck 做检查及修复文件系统的工作 , 后面接的两个参数 -A , -a . 
## -A 的意思是 : fsck 会依据 /etc/fstab 中的记录 , 去检查所有的文件 
## 系统 ; 而 -a 就是 auto 的意思 , 当 fsck 有修复的动作时 , 它不会问 
## 你问题 , 而直接修复 .

/sbin/fsck -A -a 
?

## 假如 fsck 有 error , [ $? -gt 1 ] 括号里面的意思是 : 若上个命令的 
## 传回值大于 1 , 而上个命令就是 fsck . 让我们看看 fsck 的传回值 : 
## 0 - No errors 
## 1 - File system errors corrected 
## 2 - File system errors corrected, system should 
## be rebooted if file system was mounted 
## 4 - File system errors left uncorrected 
## 8 - Operational error 
## 16 - Usage or syntax error 
## 128 - Shared library error 
## 很明显的 , 若有任何错误产生的话 , 那 fsck 的传回值都大于 1 . 其实 
## 就我的观点认为 , 应该写成 if [ $? -ge 1 ] 比较好 . 既然有错呢 , 系统 
## 应该就要跳至单用户模式 , 在单用户模式中主要就是 /etc/rc.d/rc.K 
## 中的两件事 : 关掉 swap 及 卸下所有的文件系统 , 而最后重新 login . 
## 一般正常的情况下 , if 下面这一大段是不会执行的 , 而会跳至下面 
## 标有 ************************* Normal 1 ************************* 处

if [ $? -gt 1 ] ; then 
echo 
echo 
echo "**************************************" 
echo "fsck returned error code - REBOOT NOW!" 
echo "**************************************" 
echo 
echo 
/bin/login 
fi

## ****************************** Normal 1 ************************** 
## 当 fsck 检查没有错误之后 , 就把 root partition 重新 mount 上来 
## 下面指令的参数有三个 , -w 代表mount 成可读写 , -n 代表把一个 file- 
## system mount 上来 , 但不会把记录写到 /etc/mtab 中 , 在上次对 /etc/mtab 
## 介绍时有提到 , 当我们使用 mount 这个指令把一个 filesystem mount 上来 
## 的时候 , /etc/mtab 就会记录 ! 利用 -n 这个 option 可使得做 mount 的动 
## 作 , 却不会记录 . -o 后面可以接许多的选项 , 在这里 , 我们给它的选项是 
## remount . remount 的意思是 : 重新 mount 一个已经被 mount 的 filesystem 
## 这个选项通常被用来改变该 filesystem 的读写标志 ,尤其是把 filesystem 
## 从 read-only 的状态 , 改变成 read-write 的状态 .

echo "Remounting root device with read-write enabled." 
/sbin/mount -w -n -o remount / 
?

## 在前面的情况中 , 都是 root partition 为 read-only 的状态下 , 所做的一些 
## 工作 , 到了最后一个指令 /sbin/mount -w -n -o remount / , 才把 root 
## partition mount 成 read-write . 各位有没有看到前面那行 : 
## if [ ! $READWRITE = yes ]; then ..... 下面这个 else 就是与这个 if 对应 
## 也就是说 , 前面那个 if 的区块中 , 所作的工作都是在 root partition 为 
## read-only 的条件成立下所作的事 , 那很明显的 , 下面这个 else 就是 root 
## partition 为 read-write 的条件下所作的工作 . 假如你的 root partition 
## 为 read-writeable 的话 , 那么系统就会显示下面的信息 . cat << EOF 所作的 
## 事 , 就是把 EOF 之前的信息全部显示在屏幕上 : 
## 我想 , 下面的信息写得很明显了 , 它说 : 你的 root partition 被 mount 成 
## read-write , 没有办法检查 , 要使检查的动作能够顺利的进行 , 你必须把 
## root partition mount 成 read-only ! 那要怎么做呢 ? 很容易 , 只要利用 
## rdev -R / 1 就可以了 ......

else 
cat << EOF

*** Root partition has already been mounted read-write. Cannot check! 
For filesystem checking to work properly, your system must initially mount 
the root partition as read only. Please modify your kernel with 'rdev' so 
that 
it does this. If you're booting with LILO, type: 
rdev -R /vmlinuz 1 
(^^^^^^^^ ... or whatever your kernel name is.)

If you boot from a kernel on a floppy disk, put it in the drive and type: 
rdev -R /dev/fd0 1

This will fix the problem *AND* eliminate this annoying message. :^)

EOF 
?

## 下面这个指令没什么好说的 , 就是暂停 10 秒钟 , 让 user 能够有充足的 
## 时间看完上面的信息

sleep 10 
fi 
?

## 删除 /etc/mtab /etc/nologin /etc/utmp

/bin/rm -f /etc/mtab* /etc/nologin /etc/utmp 
?

## 制造 /etc/utmp , 这是一个很典型制造空文件的写法 . /dev/null 这个 node 
## 蛮有趣的 , 在某一方面来说 , 它有点像是一个 " 黑洞 " . 怎么说呢 ? 
## 各位可以试试看下面的指令 ls >;>; /dev/null , 当你使用这个指令之后会 
## 发生什么事呢 ? 什么也没发生 , 而且 ls 的输出就好像被丢到黑洞里 , 无 
## 影无踪了 . 那也许你会想 : 那这有什么用 ? 我的回答是 : 的确没有什么 
## 很大的用处 , 但当你想抑制输出的信息时 , 你就会用得到了 .

cat /dev/null >;>; /etc/utmp 
?

## 依据 fstab ( filesystem table ) 中的描述 , 自动的挂上文件系统 
## 但此时因为 TCP/IP 还没有设定 , 故不用 NFS

echo 'Mount Filesystem !!!' 
/sbin/mount -avt nonfs 
?

## 设定系统的时钟 . 下面这几行所做的事就是 : 看看 /sbin/clock 这个文件是 
## 不是可执行的 , 假如可以执行 , 就把 CMOS 中的时间设定为系统的时间 .

if [ -x /sbin/clock ]; then 
echo 'Set System Clock' 
/sbin/clock -s 
fi 
?

## 下面的四行若没有 mark , 则每次开机 issue 及 motd 都会被改变 , 这应该 
## 可算是 FAQ 级的问题了 ...... 因为我有自己设计的 issue 及 motd , 所以 
## 下面的四行前面都有 # , 被注释掉 . 
## 假如你要有自己的设定 , 下面一定都要 mark 起来

#echo >; /etc/issue 
#echo Welcome to Linux /bin/uname -a | /bin/cut -d\ -f3. >;>; /etc/issue 
#echo >;>; /etc/issue 
#echo "/bin/uname -a | /bin/cut -d\ -f1,3. (Posix)." >; /etc/motd 
?

## 接下来 , 将执行 rc.serial , 顾名思义 , rc.serial 是作串口设定的工作 
## 在 rc.serial 中 , 内容虽然也是很简单 , 但并不像 rc.S 那样直接 . 换句话 
## 说 , 读者至少要 " 稍微 " 懂一点 shell programming , 所以说呢 , 假如 
## 还不会 shell programming 的读者呢 , 都应该赶快去找一本书来看一下 , 在 
## 这篇文章的结尾 , 我会提出一些书单 , 各位可以去找找这几本书 ......

/bin/sh /etc/rc.d/rc.serial 
echo '================= rc.S is finish NOW !!! =========================' 

?

到了这里 , rc.S 的最後一步 , 是去执行 rc.serial . 大家可以看一看 
/rc.d/rc.serial . 好像很长的样子 , 但实际上呢 , 各位必然发现到了 , 这个 
shell script 大部份指令的前面都有一个 '#' 号 , 这代表着 , 这些指令完全 
不会被执行 . 所以呢 , 真正有用的只不过寥寥十几行吧 ! 在另一方面来说 , 
假如你是用网卡连上网络 , 那 rc.serial 对你并没有什么大用处 . 
?

**************************** rc.serial ******************************

#!/bin/sh 

# /etc/rc.serial 
# Initializes the serial ports on your system 

# Version 2.01

echo '======================= rc.serial is begin !!! =====================' 
cd /dev

## 下面三行中的前两行是设定一些变量 , 由于在这个 shell script 中 , 需要 
## 用到 /bin/setserial -b 这个指令 , 或是需要用到所有以 cua 开头的 node 
## 的次数太多了 , 因此 , 把它们设定为一个变量 , 是一个不错的方法 . 尤其 
## PORTS=`echo cua? cua??` 这是一个聪明的写法 , 那为什么不写成 PORT= 
## `echo cua*` 呢 ? 各位可以在 /dev 下分别使用这两个指令 , 观察输出到底 
## 有什么不同 ......

SETSERIAL="/bin/setserial -b" 
PORTS=`echo cua? cua??` 
echo -n "Configuring serial ports...." 
?

## 下面这行 , 没有学过 shell programming 的人很可能会看不懂 , 不过没有 
## 关系 , 这行中的 ${SETSERIAL} 会被换成 /bin/setserial -b , 而 ${PORTS} 
## 会被换成 cua0 cua1 cua2 ....... cua31 , 所以整句翻译就是 : 
## /bin/setserial -b -W cua0 cua1 cua2 cua3 cua4 cua5 cua6 ...... cua31 
## 那这行指令到底在做什么呢 ? 其实只是在做中断侦测的工作 .

${SETSERIAL} -W ${PORTS} 
?

## 各位看到下面原来的注释了吧 . 当你有一些特殊的卡时 , 你可以把相对应部 
## 份前面的 '#' 去掉 , 以便能做自动设定的工作 . 其实呢 , 这种情况实在 
## 不多 , 大部份人的设备都差不了多少 , 说到关于串口 , 差异就更少了 . 

# AUTOMATIC CONFIGURATION 

# Uncomment the appropriate lines below to enable auto-configuration 
# of a particular board. Or comment them out to disable them.... 

?

## 好了 , 这下我们又多了一个变量 : AUTO_IRQ , 这在下面会用到 .

AUTO_IRQ=auto_irq 
?

## 下面几行非常整齐 , 它们可以分别被换成 : 
## /bin/setserial -b /dev/cua? auto_irq skip_test autoconfig 
## setserial 说穿了也没什么 , 这个指令可以让你对 serial port 做设定及回报 
## 的动作 , 像 IRQ , I/O port 啦等等的事情 . 一般的情况下 , 大家的电脑中 
## 通常只有 COM1-COM4 , 但假如你想增加新的 port , 那 setserial 就派上用 
## 场了 .

# These are the standard COM1 through COM4 devices 

# If you have an internal modeme with a Rockwell Chipset, add a "skip_test" 
# to the /dev/cua3 line below. (It's not added by default because it will 
# screw up people with 8514 displays). 

${SETSERIAL} /dev/cua0 ${AUTO_IRQ} skip_test autoconfig 
${SETSERIAL} /dev/cua1 ${AUTO_IRQ} skip_test autoconfig 
${SETSERIAL} /dev/cua2 ${AUTO_IRQ} skip_test autoconfig 
${SETSERIAL} /dev/cua3 ${AUTO_IRQ} autoconfig

# These are for the first AST Fourport board (base address 0x1A0) 

${SETSERIAL} /dev/cua4 ${AUTO_IRQ} autoconfig 
${SETSERIAL} /dev/cua5 ${AUTO_IRQ} autoconfig 
${SETSERIAL} /dev/cua6 ${AUTO_IRQ} autoconfig 
${SETSERIAL} /dev/cua7 ${AUTO_IRQ} autoconfig

# These are for the second AST Fourport board (base address 0x2A0) 

${SETSERIAL} /dev/cua8 ${AUTO_IRQ} autoconfig 
${SETSERIAL} /dev/cua9 ${AUTO_IRQ} autoconfig 
${SETSERIAL} /dev/cua10 ${AUTO_IRQ} autoconfig 
${SETSERIAL} /dev/cua11 ${AUTO_IRQ} autoconfig 
?

## 从这里以下 , 我省略了一大段 , 因为这一大段都是支持特殊的卡

# These are the 3rd and 4th ports on the Accent Async board. 

#${SETSERIAL} /dev/cua12 ${AUTO_IRQ} autoconfig 
#${SETSERIAL} /dev/cua13 ${AUTO_IRQ} autoconfig 

?








?

## 好了 , 我们跳掉了一大段 , 直到这里 . 各位看到下面的注解了 . 
## 假如你想用手动的方法指定 IRQ , I/O port , 及指定 chip 的型号 
## 那你可以拿掉下面对应那行前面的 '#' 并作适当的修改 , 比如说 
## 你用的是比较新的 16550 或 16550A , 而不是 16450 , 那你就可以 
## 在下面用手动的方法指定 . 实际上 , 用 autoconfig 及 autoirq 
## 的选项就可以了 , 没有必要用手动的方式 . 除非侦测不到 .

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

# MANUAL CONFIGURATION 

# If you want to do manual configuration of one or more of your 
# serial ports, uncomment and modify the relevant lines. 

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

# These are the standard COM1 through COM4 devices 

#${SETSERIAL} /dev/cua0 uart 16450 port 0x3F8 irq 4 
#${SETSERIAL} /dev/cua1 uart 16450 port 0x2F8 irq 3 
#${SETSERIAL} /dev/cua2 uart 16450 port 0x3E8 irq 4 
#${SETSERIAL} /dev/cua3 uart 16450 port 0x2E8 irq 3








.

## Ok , 到此 , rc.S 及 rc.serial 已经结束 , 因为截稿时间的关系 , rc.M 
## rc.inet1 , rc.inet2 , rc.font , rc.local 将在以后为各位介绍 .

echo "done." 
${SETSERIAL} -bg ${PORTS}

echo ' ====================== rc.serial is complete !!! ===================' 

?

* 关于 Shell Programming 的书单 :

Title: The Unix C Shell Field Guide 
Authors: Gail Anderson and Paul Anderson 
Publisher: Prentice Hall 
Edition: 1986 
ISBN: 0-13-937468-X

这本是 C-Shell 的 Bible , 想学 C-Shell 的人 , 可以去看这本书 . 
?

Title: Unix Shell Programming 
Authors: Stephen Kochan and Patrick Wood 
Publisher: Hayden 
Edition: 1990 
ISBN: 0-672-48448-X

喔 ! 这本书以 Bourne Shell 为主 , 内容深入浅出 , 读者很容易就可以了解 
这本书的内容 , 进而掌握 Bourne Shell 的精髓 . 此外 , 这本书也有提到 
Korn Shell , 大体上来说 , 是一本值得看的好书 . 
?

*如何联系作者 :

E-Mail Address :jhhsu@csie.nctu.edu.tw u8217017@cc.nctu.edu.tw

Dormitory : 交通大学十舍 317R

.. 

?

Linux 开机程序之研讨 
CCCA 资工86 许景华

在上次的介绍中 , 我们已经看完了 rc.S 及 rc.serial 这两个 shell script . 
现在 , 我们将把剩下的 shell script 再作一个介绍 . 
首先还是看看全部的流程 : 
?

init[1] 
rc.S begin 
rc.serial begin 
rc.serial end 
rc.S end <-- 上一次我们说明到这里 
init[1] enter runlevel 5 
rc.M begin 
rc.inet1 begin 
rc.inet1 end 
rc.inet2 begin 
rc.inet2 end 
rc.font begin 
rc.font end 
rc.local begin 
rc.local end 
rc.M end 
login 
?

这次主要的部份可分为两项 : 因为 init 将会去读取 inittab , 所以 inittab 
将被列为第一部份的重点 , 而第二部份就是 rc.M , rc.font , rc.local 
这几个 shell script 的说明 . ( rc.inet1 , rc.inet2 这两个关于网络的 
shell script 将在以后单独说明 ) 
好了 , 我们先从 inittab 看起吧 ! 看看上面的流程 , 在第一行 : init[1] 
也就是 init 这个 process 被启动之后 , 它会去读取 /etc/inittab 这个文件 
以完成系统的启动 . 从这里 , 我们看到了 LINUX 的确融合了 SVR4 及 SunOS 
的一些特性 , inittab 这个文件 , 在 SunOS 系统中是不存在的 , 但是它却是 
SVR4 典型的文件 . init 这个 process 会依据 /etc/inittab 中所记载的内容 
进入不同的 run-level 并启动不同的 process . 所以 inittab 的重要性 
可见一斑 . 那什么叫 run-level 呢 ? 所谓 run-level 就是系统中定义了许多 
不同的 level , 而系统会随著 level 的不同而去启动不同的资源 . 
现在就让我们来看一下 /etc/inittab 中的内容 : 
在 /etc/inittab 这个文件中 , 每一列是一个进入点 , 假如我们仔细观察每一列 
的话 , 那我们就会很容易的发现 , /etc/inittab 的每一列可以被 " : " 这个 
字符分成好几个栏位 . 这几个栏位的格式如下 :

id:runlevels:action:process

而它们代表的意义分别如下 :

id : 由两个独特的字符所组成的辨示符号 , 在比较新的 UNIX 系统中 , 已不 
受只能有两个字符的限制 .

runlevels : 指出下面一个栏位中的 action 以及 下下个栏位中的 process 
会在那些 runlevel 中被执行 . 这一栏的合法值有 0,1,2...6 
s 以及 S . 而在正常的启动程序之后 , Superuser 可以使用 
telinit 这个指令来改变系统的 runlevel . 又因为在 LINUX 中 
, runlevel 的预设值是 5 ( 等一下就会看到 ) 所以 , 只有 
那些每一列中 runlevel 那栏有 5 这个值的 , 后面的 process 
才会被启动 . 所以 , 我们就可以想像的到 : " 由于系统的 
runlevel 不同 , 所起动的 process 也不尽相同 , 所以系统 
起动的资源在每个不同的 runlevel 就会有差异存在 .

action : 这个栏位有一点复杂 , 在这个栏位中记录着 init 在启动相对应的 
process 时 , 对 process 所采取的动作 , 而合法的动作有下面几项:

initdefault : 指出系统在启动时 , 预设进入的 run-level 值 , 
比如说 , 我们可以在 /etc/inittab 中找到下面这 
一列 : id:3:initdefault: 
很明显的 , 系统将在启动时 , 进入 runlevel 为 3 
的模式 . 当然 , 你可以把 3 改成 4 试试看 , 如 
果你改成了 4 , 那将会执行 /etc/rc.d/rc.4 , 也 
就是 run xdm . xdm 在以后有机会的话 , 将为各 
位介绍 ......

sysinit : 在系统起动时 , 这个 process 会被执行 . 而所有 process 
前的 action 中有 boot 及 bootwait 的 process , 必 
须等到这些 action 为 sysinit 的 process 执行完之后 
它们才能够执行 .

wait : 在起动一个 process 之后 , 若要再起动另一个 process , 则 
必需等到这个 process 结束之后才能继续 .

respawn : 代表这个 process 即使在结束之后 , 也会重新被启动 , 
最典型的例子就是 getty ( 在 LINUX 中为 agetty ) . 
看看下面的循环 :

-- getty -->; login -->; shell -->; logout -- 
^ |??????????????????????????????????????? | 
? |<---------------------------------------|

即使在 getty 结束之后 , 它也会重新被启动 .

ctrlaltdel : 想必有人会以键盘上的 Ctrl , Alt , 及 Del 这三个 
键来达到使系统 shutdown 的目的 , 现在我们果然在 
/etc/inittab 中看到了这一列 :

ca::ctrlaltdel:/sbin/shutdown -t3 -rf now

所以说 , 当我们按下这三个键的时候 , init 会收到 
SIGINT 这个 signal , 接着就执行 shutdown 的动作 
不过 , 我们最好不要养成按 Ctrl-Alt-Del 来使系统 
shutdown 的习惯 , 尤其在单用户多任务的操作系统 , 像 
OS/2 , 甚至 Windows 95 , shutdown 几乎都是标准 
的关机程序了 , 更何况是多用户多任务的 LINUX , 所以 , 
shutdown 这个指令是一定要熟悉的 .

除了上面的几个 action 之外 , 另外还有一些合法的 action , 但这 
些 action 并不需要太注意 , 要用的时候再利用 man init 去查询就 
可以了 .

process : 这一栏中可以是 shell script 或是可执行的程序 .

好了 , 当我们了解 /etc/inittab 中每一栏的意义之后 , 要看懂 /etc/inittab 
就是一件轻松愉快的工作了 . 在 /etc/inittab 档中 , 我们可以看到下面这一段

c1:12345:respawn:/sbin/agetty 38400 tty1 
c2:12345:respawn:/sbin/agetty 38400 tty2 
c3:45:respawn:/sbin/agetty 38400 tty3 
c4:45:respawn:/sbin/agetty 38400 tty4 
c5:45:respawn:/sbin/agetty 38400 tty5 
c6:456:respawn:/sbin/agetty 38400 tty6

简单来说 , 系统在起动之后会制造出六个虚拟的 console . 我想大家应该有试过 
用 Ctrl-Alt + F1 - F6 可在这六个 console 之间切换 ; 若你使用 XWindows 时 
想暂时回到 console 下时 , 可用 Ctrl-Alt + F1 - F6 这三个键来选择 , 若想 
回到 XWindows 下时 , 只要以 Ctrl-Alt-F7 就可以回到 XWindows 下了 . 基本 
上 , 对於 memory 比较少的人 , 可以不要开那么多的虚拟 console , 那么就可 
以去掉上面的几列 . 还有 , 在前面我们也提过 , 可以把预设的 runlevel 从 5 
改成 6 , 对於 beginner 来说 , 系统一启动完就直接进入 XWindows 也许是一个 
不错的设定方法 ......

介绍完 /etc/inittab 之后 , 我们接着看 rc.M ! 由前面的流程当中 , 我们看到 
rc.M 中又包含了四个 shell script , 其中 rc.inet1 及 rc.inet2 是有关于网络 
的设定 ; rc.font 是作字体的设定 , 而 rc.local 中可以放一些想要起动的 
daemon .

我们现在就来看看 rc.M , 依照往例 , 前面有两个 "#" 的为加上去的注释 . 
只有一个 "#" 的为原来的注释 :

#!/bin/sh 

# rc.M This file is executed by init(8) when the system is being 
# initialized for one of the "multi user" run levels (i.e. 
# levels 1 through 6). It usually does mounting of file 
# systems et al. 

# Version: @(#)/etc/rc.d/rc.M 2.02 02/26/93 

# Author: Fred N. van Kempen, 
# Heavily modified by Patrick Volkerding 
#

## 显示进入多用户模式

echo "Going multiuser..." 
?

## 下面一列的意思是 : 假如你在文字模式的 console 下 , 在15分钟内都没有动作 
## 的话 , 屏幕就会自动暗下来 , 简单的说 , 就是 screen saver 的功能 .

/bin/setterm -blank 15 
?

## 执行 crond 这个 daemon . 不用说 , crond 在系统中扮演了很重要的角色 , 
## 它负责每过一段时间后 , 就去看看 /var/spool/cron/crontabs 中有那些 file 
## 要运行 , 这些 file 往往有一个固定的时间 , 比如说 : 每个月的 1 号 , 每 
## 天凌晨等 ...... 我们可以用平常的编辑器编好一个文件 , 里面的格式如下 : 
## 
## 分 时 日 月 星期 命令 
## 
## 举例来说 , 59 23 31 12 * /etc/wall happy_new_year 
## 在每年的 12 月 31 号晚上 11 点 59 分 会对每个系统上的 user 送出 
## happy_new_year 中的内容 
## 
## 接着我们可以利用 crontab <文件名>; 这个指令来把此文件放到 
## /var/spool/cron/crontabs中. 我们可以看看 /var/spool/cron/crontab 下 
## 有一个 root 的文件 , 看看里面的内容 : 
## 
## 0,5,10,15,20,25,30,35,40,45,50,55 * * * * /usr/lib/atrun 
## 
## 所以各位看到了 , 在前两期提到的 at 命令是五分钟才被 run 一次的 
## 
## 再举一个简单的例子好了 : 我们先用一般的文本编辑器造出一个名为 crontest 
## 的档案 , 内容如下 : 
## 
## 5 * * * * ls -la ~/ >;>; ~/hehehaha 
## 
## 接着 , 我们键入下面的命令 : crontab crontest 
## 此时 , 从内容得知 , 每五分钟 crond 就会执行 ls -la , 把你 home directory 
## 的内容加入 hehehaha 这个文件中 . 
## 
## 当然啦 ! 这个例子简直是毫无意义可言 :) 但是 , 大家既然知道了基本原理 , 
## 利用 crontab , at 这些指令 , 就可以简化一些系统管理的动作 , 同时在执行 
## 一些工作时 , 也会比较有弹性 .

/usr/sbin/crond -l10 >;>;/var/adm/cron 2>;&1 
?

## 假如 /etc/HOSTNAME 不能读取的话 , 就把 darkstar.frop.org 当成 HOSTNAME 
## 中的内容 . 老实说 , 下面这三列去掉也不打紧 ......

if [ ! -r /etc/HOSTNAME ]; then 
echo "darkstar.frop.org" >; /etc/HOSTNAME 
fi 
?

## 下面从 if 到 fi 夹起来的部份 , 主要就是在执行 rc.inet1 , rc.inet2 . 这 
## 些都是网络设定的工作 , 尤其是 rc.inet2 , 启动了一大堆 daemon , 这部份 
## 要牵扯到的东西太多了 . 像 subnet 与 netmask 等 ...... 类似这种观念 , 
## 都不是三言两语就可以玩完的 , 所以就留待以后再说 .

if [ -x /etc/rc.d/rc.inet1 ]; 
then 
/bin/hostname `cat /etc/HOSTNAME | cut -f1 -d .` 
/bin/sh /etc/rc.d/rc.inet1 
/bin/sh /etc/rc.d/rc.inet2 
else 
/sbin/hostname_notcp `cat /etc/HOSTNAME | cut -f1 -d .` 
/usr/sbin/syslogd 
/usr/sbin/klogd 
/usr/sbin/lpd 
fi 
?

## 在某些资源独占的情况下 , 一些应用程序往往会制造出 lock 文件 . 假如这些 
## lock 文件在重新开机以后还是存在的话 , 那就很不好了 . 所以 , 下面就是在 
## 作这些删除 lock 文件的动作 , 并把一些输出的信息丢到 /dev/null 去 . 
## 在上一期的内容中 , 我们就有提到 /dev/null 了 , 也有提到抑制信息输出的 
## 方法 . 现在我们果然看到了一个实例 ......

/bin/rm -f /var/spool/locks/* /var/spool/uucp/LCK..* /tmp/.X*lock 1>; /dev/null 2>; /dev/null 
?

## 假如你有玩 hunt 这个 game 的话 , 那在 /tmp 下会有一个 socket 类型的文件 
## 我们要把它删除之后才能开始另一个 game ......

if [ -r /tmp/hunt -o -r /tmp/hunt.stats ]; then 
echo "Removing your stale hunt sockets from /tmp..." 
/bin/rm -f /tmp/hunt* 
fi 
?

## 设定 share library 的 link 及 cache . 这个指令只有 Superuser 才能使用 
## 的 , 它也相当的重要 . 万一你的 /etc/ld.so.cache 很不幸的 corrupt 了 , 
## 那我们也可以利用这个指令来让它重新 link , 先删除 /etc/ld.so.cache , 
## 再以 ldconfig -v 重新制造就可以了 .

/sbin/ldconfig 
?

## 起动 sendmail daemon , 并且让它 15 分钟就去看一看 spool , 处理收发信件

if [ -x /usr/sbin/sendmail ]; then 
echo "Starting sendmail daemon (/usr/sbin/sendmail -bd -q 15m)..." 
/usr/sbin/sendmail -bd -q 15m 
fi 
?

## 假如 /etc/rc.d/rc.font 是可读的话 , 就执行 rc.font 这个 shell script , 
## 而这个 shell script 主要是设定 text mode 下屏幕的字体

if [ -r /etc/rc.d/rc.font ]; then 
/etc/rc.d/rc.font 
fi 
?

## 在系统管理中 , 我们常常把一些 local 的东西另外放在一个地方 , 这样才不 
## 会与原来的东西混淆 . 同时 , 因为 local 的东西更新版本的速度总是也比较 
## 快 , 在这种情况下 , 常常会变动的东西也可以放在 local 的区域中 , 这样 
## 管理起来比较方便 . 也许各位也注意到了 : 为什么会有 /usr/bin 及 
## /usr/local/bin 之分 . 就个人认为 , 像自己 compile 出来的东西 , 假如 
## 觉得还不错 , 就可以把它放在 /usr/local/bin , 因为它是新增的 , 所以我 
## 把它放在 /usr/local/bin . 当然啦 , 这只是个人喜好罢了 , 你要放那里 
## 都是可以的 , 只要找得到 , 易于使用及管理就好 . 
## 同样的 , 若我们要起动一些新增的 daemon 或 shell script , 那放在 
## 是不错的选择 . 
## 下面一列就是去执行 rc.local 中的设定 , 通常是一些 daemon 或是 shell 
## script

/etc/rc.d/rc.local 
?

# All done. 
?

到这里 , rc.M 已经结束了 , 我们来看看从 rc.M 之中执行的 rc.font 及

rc.local ......

下面是 rc.font 的内容 :

#!/bin/sh 

# This selects your default screen font from among the ones in 
# /usr/lib/kbd/consolefonts. 
#

## 我想下面这一列的命令非常明显了 , 就是设定 console 中的字体 , 你可以 
## 改成自己喜欢的字体 . 或者你也可以利用 fontconfig 这个指令来改变 .

setfont /usr/lib/kbd/consolefonts/default8x16 

?

看完了 rc.font 后 , 我们来看看 rc.local 的内容 . 我所要说的是 : rc.local

毕竟是自己设定的区域 , 所以每个人的可能都不一样 , 就我而言 , 因为我多 run

了一些 daemon , 所以与大家的可能不太相同 . 所以 , rc.local 作参考就可以了.

下面是我的 rc.local :

#! /bin/sh 
# Put any local setup commands in here 
# Running selection 
?

## lpd 是控制打印机的 daemon , 要想在 LINUX 下用打印机 , 这个 daemon 必需 
## 要被起动 , 此外还要修改 /etc/printcap . 详细的情况要去看 PRINT-HOWTO

echo -n "lpd" 
/etc/lpd 
?

## httpd 就是 WWW server 的 daemon . 想必大家都用过 Mosaic , Netscape 等 
## 的浏览器 . 但假如我们想建立自己的 WWW server , httpd 必须要执行 .

echo -n " httpd" 
/usr/local/etc/httpd/src/httpd 
?

## 在 WWW 的时代还没来临以前 , gopher 可说是具有最方便的信息索引功能 , 即使 
## 到了现在 , gopher 仍然占有一席之地 , 在这里 , 因为我有建立自己的 gopher 
## server , 所以 gopherd 必需被起动 .

echo -n " gopherd" 
/usr/local/sbin/gopherd -u nobody 
?

## 下面这个指令是 mouse 在 console 下做 cut & paste

echo -n "Running selection..." 
selection -t ms & 
echo ' '

本文出自 51CTO.COM技术博客

Linux系统开机过程解释笔记

说实话,偶以前一直看不起开机启动的,按下电源然后出去溜一圈回来就可以用,何必考虑这个过程呢.不过,后来发觉学习linux如果没过这个的话,对以后的学习会产生很大的障碍.因此,赶紧多学习几遍.网上开机描述linux开机过程的比较多.不过,自己不默写一遍,在脑海里过一遍,往往也只能了解个大概,处于难得糊涂的状态.为了达到剑既是我,我既是剑的人剑合一的境界.赶紧温习一遍.

总结一下,linux的开机整个流程.

1.       1: 启动电源后,主机第一步先做的就是查询BIOS(全称:basic input/output system 基本输入输出系统)信息.了解整个系统的硬件状态,如CPU,内存,显卡,网卡等.嗯,这一步windows算和它是一家.不分彼此.

2.       2: 接下来,就是主机读取MBR(硬盘的第一个扇区)里的boot loader了.这个可是重点哦,据说troubleshooting里就会考这点,给个坏了的loader,叫你修正.windows不支持linux的分区格式.所以,用windows的boot.ini是查不到linux的系统的.一般我装系统都是先装windows再装linux,然后用grub来做boot loader.两个字:省心!因为linux不像windows那么小气.grub可是支持windows分区格式的哦.

3.      3:  接上一步,主机读取boot loader后,会读取里面的信息,知道谁跟谁是待在哪,假如主机想进入linux系统,读取到linux核心是在/boot文件目录中后,将此核心加载到内存中.开始了接下来的分析启动之旅.

4.        4: OK,第一个运行程序是谁?就是/sbin/init程序.不信,就用top程序看下,是不是PID为1的就是这个东东,它,可是万物之祖啊,我简称它是女娲娘娘(不喜欢亚当夏娃).

5.       5:  init首先查找启动等级(run-level).因为启动等级不同,其运行脚本(也就是服务)会不同.默认的等级有以下几项:
0 - halt (系统直接关机)
1 - single user mode (单人模式,用于系统维护时使用)
2 - Multi-user, without NFS (类似3模式,不过少了NFS服务)
3 - Full multi-user mode (完整模式,不过,是文本模式)
4 - unused (系统保留功能)
5 - X11 (与3模式类似,不过,是X终端显示)
6 - reboot (重新开机)
(不要选择0或4,6 否则,进步了系统的)

6.       6:  OK.系统知道自己的启动等级后,接下来,不是去启动服务,而是,先设置好主机运行环境.读取的文件是/etc/rc.d/rc.sysinit文件.那究竟要设置哪些环境呢?

1. 设置网络环境/etc/sysconfig/network,如主机名,网关,IP,DNS等.

2. 挂载/proc.此文件是个特殊文件,大小为0,因为它是在内存当中.里面东东最好别删.

3. 根据内核在开机时的结果/proc/sys/kernel/modprobe.开始进行周边设备的侦测.

4. 载入用户自定义的模块/etc/sysconfig/modules/*.modules

5. 读取/etc/sysctl.conf文件对内核进行设定.

6. 设定时间,终端字体,硬盘LVM或RAID功能,以fsck进行磁盘检测.

7. 将开机状况记录到/var/log/dmesg中.(可以用命令dmesg查看结果)

7.       7:  OK,接下来,就是启动系统服务了,不同的run-level会有不同的服务启动.到/etc/rc.d目录中,不同的level会有不同的目录.如启动3模式,会有个rc3.d目录,里面就保存着服务.其中,S(start)开头的表明开机启动,K(kill)开头的表明开机不启动.数字表示启动顺序.数字越小,启动越早.
注意,他们都是连接到etc/rc.d/init.d/目录中的相关文件.所以,想手工启动某一服务,可以用"/etc/rc.d/init.d/某个服务 start"启动哦.相反,我们也可以把某个服务ln(链接命令)到不同run-level的目录中.记得打上S或者K+数字哦.

8.       8:  读取服务后,主机会读取/etc/rc.d/rc.local文件.所以,如果需要什么开机启动的话,可以写个脚本或命令到这里面来.就不用像上面那么麻烦.以后删除也方便.

OK,经过一番长途跋涉后,系统终于可以安心的开启shell了.把控制权交到我们手上了.我们可以为所欲为了.最好,养成好习惯,平时不要用root用户登陆.上次我就一不小心错输了poweroff.那可是网关服务器啊.全公司人都上不了网啊.还好跟他们已经热乎了.只是

linux系统脚本的常见启动顺序

由于相关变量定义不同, 所以以下启动顺序仅供参考

在Redhat Redflag centos fc linux系统里面脚本的启动

先后:
第一步:通过/boot/vm进行启动 vmlinuz
第二步:init /etc/inittab
第三步:启动相应的脚本,并且打开终端
rc.sysinit
rc.d(里面的脚本)
rc.local
第四步:启动login登录界面 login
第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的
/etc/profile.d/file
/etc/profile
/etc/bashrc
/root/.bashrc
/root/.bash_profile

在Suse Linux (sles server or Desktop 10)

第一步:通过/boot/vm进行启动 vmlinuz
第二步:init /etc/inittab
第三步:启动相应的脚本,并且打开终端 /etc/init.d/boot 里面包括:
. /etc/rc.status
./etc/sysconfig/boot
./etc/init.d/boot.d下面的脚本
./etc/init.d/boot.local
rc X.d(里面的脚本)
第四步:启动login登录界面 login
第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的
/etc/profile.d/file
/etc/profile
/root/.bashrc
/root/.profile
先后:
第一步:通过/boot/vm进行启动 vmlinuz
第二步:init /etc/inittab
第三步:启动相应的脚本,并且打开终端
rc.sysinit
rc.d(里面的脚本)
rc.local
第四步:启动login登录界面 login
第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的
/etc/profile.d/file
/etc/profile
/etc/bashrc
/root/.bashrc
/root/.bash_profile

在Suse Linux (sles server or Desktop 10)

第一步:通过/boot/vm进行启动 vmlinuz
第二步:init /etc/inittab
第三步:启动相应的脚本,并且打开终端 /etc/init.d/boot 里面包括:
. /etc/rc.status
./etc/sysconfig/boot
./etc/init.d/boot.d下面的脚本
./etc/init.d/boot.local
rc X.d(里面的脚本)
第四步:启动login登录界面 login
第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的
/etc/profile.d/file
/etc/profile
/root/.bashrc
/root/.profile
先后:
第一步:通过/boot/vm进行启动 vmlinuz
第二步:init /etc/inittab
第三步:启动相应的脚本,并且打开终端
rc.sysinit
rc.d(里面的脚本)
rc.local
第四步:启动login登录界面 login
第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的
/etc/profile.d/file
/etc/profile
/etc/bashrc
/root/.bashrc
/root/.bash_profile

在Suse Linux (sles server or Desktop 10)

第一步:通过/boot/vm进行启动 vmlinuz
第二步:init /etc/inittab
第三步:启动相应的脚本,并且打开终端 /etc/init.d/boot 里面包括:
. /etc/rc.status
./etc/sysconfig/boot
./etc/init.d/boot.d下面的脚本
./etc/init.d/boot.local
rc X.d(里面的脚本)
第四步:启动login登录界面 login
第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的
/etc/profile.d/file
/etc/profile
/root/.bashrc
/root/.profile

本文出自 51CTO.COM技术博客

第一步:通过/boot/vm进行启动 vmlinuz
第二步:init /etc/inittab
第三步:启动相应的脚本,并且打开终端
rc.sysinit
rc.d(里面的脚本)
rc.local
第四步:启动login登录界面 login
第五步:在用户登录的时候执行sh脚本的顺序:每次登录的时候都会完全执行的
/etc/profile.d/file
/etc/profile
/etc/bashrc
/root/.bashrc
/root/.bash_profile

Linux系统脚本分析之rc.sysinit相关推荐

  1. 转-linux系统脚本 环境变量 的启动顺序

    linux系统脚本的常见启动顺序 由于相关变量定义不同, 所以以下启动顺序仅供参考 在Redhat Redflag centos fc linux系统里面脚本的启动 先后: 第一步:通过/boot/v ...

  2. Linux系统IO分析工具之iotop常用参数介绍

      Linux系统IO分析工具之iotop常用参数介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在一般运维工作中经常会遇到这么一个场景,服务器的IO负载很高(iostat中的 ...

  3. 操作系统笔记——Linux系统实例分析、Windows系统实例分析

    文章目录 传送门 Linux进程管理 Linux进程组成 Linux进程链表 Linux进程控制 用户进程创建与撤销 0,1,2号进程 Linux进程切换 Linux进程调度 内核同步 Linux储存 ...

  4. Linux系统编程 / 分析开源软件Triggerhappy

    哈喽,我是老吴,继续记录我的学习心得 自制力太强导致低效专注 自制力很强的人的成功之道在于,在别人都放弃的情况下仍坚持不懈. 但是这反而会让他们难以关闭专注模式,导致无法进入解决难题所必要的发散模式. ...

  5. 分析linux系统的运行性能,Linux系统如何分析CPU的性能瓶颈

    以下内容来自转载和自己的初用体验. vmstat [root@master ~]# vmstat -n 3 procs   ---memory--    --swap--    --io--  --s ...

  6. Linux 系统内存分析

    1. 内存基本介绍 1.计算机基本结构: 电脑之父--冯·诺伊曼提出了计算机的五大部件:输入设备.输出设备.存储器.运算器和控制器 如图: 输入设备:键盘鼠标等 CPU:是计算机的运算核心和控制核心, ...

  7. Linux系统开机后/etc/rc.d/rc.local文件中的命令没有执行(已解决)

    今天想将linux系统上的svn服务添加到开机自启动,结果重启机器后发现svn服务根本没有起来,检查了添加到/etc/rc.d/rc.local文件的命令没有问题,如下: 看了文件的注释部分才知道,原 ...

  8. linux系统脚本安装失败,ubuntu16.04下vim安装失败的原因分析及解决方案

    先给大家说下问题描述? 重装了ubuntu系统,安装vim出现了以下问题: sudo apt-get install vim 正在读取软件包列表... 完成 正在分析软件包的依赖关系树 正在读取状态信 ...

  9. 【ARM Linux 系统稳定性分析入门及渐进 1 -- Crash 工具简介】

    文章目录 1.1 Kexec 和 Kdump 1.1.1 Kexec 加载优点 1.1.2 kdump 功能 1.1.3 kdump原理 1.2 Crash tool 1.2.1 ramdump 机制 ...

最新文章

  1. qq满屏飞吻代码_教你用微信隐藏代码表白!各种微信技巧
  2. mysql创建用户,并赋予权限:只能查某个数据库中的某张表(只读)
  3. PADS VX2.x安装教程
  4. python如何改颜色_python和networkx:如何更改节点的颜色
  5. SAP Spartacus 2.1.0 加载homepage的逻辑
  6. JeecgBoot 常见问题QA
  7. 人脸识别时一定要穿衣服!小心被看光光......
  8. Struts2执行流程
  9. 第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛——F题 成绩查询ing
  10. Redis高可用之持久化
  11. FPGA基础知识之主要的FPGA生产厂商介绍
  12. 这次面试就差不多了,你有什么问题需要问我呢?
  13. sql按照字符串格式拼接
  14. torch的CNN案例,mnist数据集下载缓慢的解决方案
  15. Cisco 模拟器rstp生成树
  16. python数据分析与应用pdf_看了Python在金融行业中的应用,大数据分析实在太重要了!...
  17. RAID的几种工作模式
  18. python 数据分析师 考试_数据分析师证书怎么考?
  19. ant如何形成时间轴和图库_安利会员如何办理?办理安利会员的好处是什么?
  20. 淮北师范大学计算机科学与技术专业属于,2015年淮北师范大学计算机科学与技术专业最低分是多少?...

热门文章

  1. android 权重的通俗易懂理解
  2. 使用asp.net开发钉钉群机器人全过程
  3. 用c++和SFML实现简易的界面版贪吃蛇
  4. 如何写出Android框架,结合android当前著名框架写的一个项目--学Android
  5. 戴尔笔记本买 1 赠 1 送同款,单台5折2500元!
  6. 深入分析微信小程序带来的机遇
  7. 认识K8s容器存储接口CSI
  8. 数据操作日志记录表设计、可以查询、恢复历史数据记录
  9. 小米android核心应用程序,小米系统 应用入口之争的另一种思路
  10. hive中标准偏差函数stddev()详细讲解