学习啦>学习电脑>操作系统>Linux教程>

分析Linux系统rc.sysinit脚本

若木分享

  欢迎来到学习啦,本文介绍分析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 "(" class="con">

分析Linux系统rc.sysinit脚本

若木分享

  欢迎来到学习啦,本文介绍分析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 脚本提供的函数”一文

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

  #selinux段相关注释,邹立巍(mini.jerry@gmail.com)

  # Check SELinux status #检察selinux状态

  selinuxfs=`awk ‘/ selinuxfs / { print $2 }‘ /proc/mounts` #找到selinux的状态显示目录,默认为/selinux

  SELINUX= #将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语句显示,当变量selinuxfs的值不为空,且/proc/self/attr/current文件内容不是kernel时(此时说明selinux的状态被设置为打开),

  #检查/selinux/enforce是否可读,若可读,则设置SELINUX变量状态为/selinux/enforce文件内容。如果不可读,设置为1。

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

  /sbin/restorecon -R /dev 2>/dev/null

  fi

  #用restorecon命令重新标记/dev目录的安全上下文

  disable_selinux() {

  echo "*** Warning -- SELinux is active"

  echo "*** Disabling security enforcement for system recovery."

  echo "*** Run ‘setenforce 1‘ to reenable."

  echo "0" > $selinuxfs/enforce

  }

  #定义函数disable_selinux(),此函数用来将selinux设置为Permissive状态,即selinux的调试状态

  relabel_selinux() {

  if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then

  chvt 1

  fi

  #检查主机是否活动,如果活动则切换到tty1上

  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 #将selinux设置为Permissive状态,以便重新标记文件的安全上下文

  /sbin/fixfiles -F relabel > /dev/null 2>&1 #用fixfile命令重新标记安全上下文

  rm -f /.autorelabel #删除标记时产生的临时文件

  echo "*** Enabling security enforcement. ***"

  echo $SELINUX > $selinuxfs/enforce #将selinux设置为enforce状态

  if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then

  chvt 8

  fi

  #如过主机处于活动状态,切换到tty8

  }

  #定义函数relabel_selinux(),此函数用来重新标记所有文件的安全上下文

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

  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 " # 打印 "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="" # 下面这些变量都是为了存储的型号,格式为 " ..."

  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"\";; &nb, sp; # 输出 "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" &nbs, p; # 输出 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 &nbs, p; # 如果 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 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}设置主机名

  &, nbsp; # 注意,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/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." # 然后提示将要自动重启了

  &, nbsp; 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) # " , 表示当前是第几个命令

  [ "$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

")" }‘` # 取得版本号

  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 脚本提供的函数”一文

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

  #selinux段相关注释,邹立巍(mini.jerry@gmail.com)

  # Check SELinux status #检察selinux状态

  selinuxfs=`awk ‘/ selinuxfs / { print

  欢迎来到学习啦,本文介绍分析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 脚本提供的函数”一文

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

  #selinux段相关注释,邹立巍(mini.jerry@gmail.com)

  # Check SELinux status #检察selinux状态

  selinuxfs=`awk ‘/ selinuxfs / { print $2 }‘ /proc/mounts` #找到selinux的状态显示目录,默认为/selinux

  SELINUX= #将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语句显示,当变量selinuxfs的值不为空,且/proc/self/attr/current文件内容不是kernel时(此时说明selinux的状态被设置为打开),

  #检查/selinux/enforce是否可读,若可读,则设置SELINUX变量状态为/selinux/enforce文件内容。如果不可读,设置为1。

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

  /sbin/restorecon -R /dev 2>/dev/null

  fi

  #用restorecon命令重新标记/dev目录的安全上下文

  disable_selinux() {

  echo "*** Warning -- SELinux is active"

  echo "*** Disabling security enforcement for system recovery."

  echo "*** Run ‘setenforce 1‘ to reenable."

  echo "0" > $selinuxfs/enforce

  }

  #定义函数disable_selinux(),此函数用来将selinux设置为Permissive状态,即selinux的调试状态

  relabel_selinux() {

  if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then

  chvt 1

  fi

  #检查主机是否活动,如果活动则切换到tty1上

  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 #将selinux设置为Permissive状态,以便重新标记文件的安全上下文

  /sbin/fixfiles -F relabel > /dev/null 2>&1 #用fixfile命令重新标记安全上下文

  rm -f /.autorelabel #删除标记时产生的临时文件

  echo "*** Enabling security enforcement. ***"

  echo $SELINUX > $selinuxfs/enforce #将selinux设置为enforce状态

  if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then

  chvt 8

  fi

  #如过主机处于活动状态,切换到tty8

  }

  #定义函数relabel_selinux(),此函数用来重新标记所有文件的安全上下文

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

  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 " # 打印 "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="" # 下面这些变量都是为了存储的型号,格式为 " ..."

  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"\";; &nb, sp; # 输出 "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" &nbs, p; # 输出 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 &nbs, p; # 如果 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 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}设置主机名

  &, nbsp; # 注意,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/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." # 然后提示将要自动重启了

  &, nbsp; 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) # " , 表示当前是第几个命令

  [ "$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

}‘ /proc/mounts` #找到selinux的状态显示目录,默认为/selinux

  SELINUX= #将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语句显示,当变量selinuxfs的值不为空,且/proc/self/attr/current文件内容不是kernel时(此时说明selinux的状态被设置为打开),

  #检查/selinux/enforce是否可读,若可读,则设置SELINUX变量状态为/selinux/enforce文件内容。如果不可读,设置为1。

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

  /sbin/restorecon -R /dev 2>/dev/null

  fi

  #用restorecon命令重新标记/dev目录的安全上下文

  disable_selinux() {

  echo "*** Warning -- SELinux is active"

  echo "*** Disabling security enforcement for system recovery."

  echo "*** Run ‘setenforce 1‘ to reenable."

  echo "0" > $selinuxfs/enforce

  }

  #定义函数disable_selinux(),此函数用来将selinux设置为Permissive状态,即selinux的调试状态

  relabel_selinux() {

  if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then

  chvt 1

  fi

  #检查主机是否活动,如果活动则切换到tty1上

  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 #将selinux设置为Permissive状态,以便重新标记文件的安全上下文

  /sbin/fixfiles -F relabel > /dev/null 2>&1 #用fixfile命令重新标记安全上下文

  rm -f /.autorelabel #删除标记时产生的临时文件

  echo "*** Enabling security enforcement. ***"

  echo $SELINUX > $selinuxfs/enforce #将selinux设置为enforce状态

  if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then

  chvt 8

  fi

  #如过主机处于活动状态,切换到tty8

  }

  #定义函数relabel_selinux(),此函数用来重新标记所有文件的安全上下文

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

  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]*\).*/ class="con">

分析Linux系统rc.sysinit脚本

若木分享

  > /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 " # 打印 "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.*/ class="con">

分析Linux系统rc.sysinit脚本

若木分享

  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.*/ class="con">

分析Linux系统rc.sysinit脚本

若木分享

  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="" # 下面这些变量都是为了存储的型号,格式为 " ..."

  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"\";; &nb, sp; # 输出 "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 " class="con">

分析Linux系统rc.sysinit脚本

若木分享

分析Linux系统rc.sysinit脚本

若木分享

  } # 再用 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 }‘` $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 }‘` $audio; do # 和 scsi 一样,加载在 modprobe -c 和 audio 变量

  load_module $module # 中的模块

  done

  echo -n $" audio" &nbs, p; # 输出 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 class="con">

分析Linux系统rc.sysinit脚本

若木分享

  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 函数的作用是把第一个参数 class="con">

分析Linux系统rc.sysinit脚本

若木分享

  # 然后把 class="con">

分析Linux系统rc.sysinit脚本

若木分享

  # 除了 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=" class="con">

分析Linux系统rc.sysinit脚本

若木分享

分析Linux系统rc.sysinit脚本

若木分享

  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 &nbs, p; # 如果 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 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}设置主机名

  &, nbsp; # 注意,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/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 ‘/ \/ / && ( !~ /rootfs/) { print }‘ /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 ‘/ \/ / && ( !~ /rootfs/) {print class="con">

分析Linux系统rc.sysinit脚本

若木分享

  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." # 然后提示将要自动重启了

  &, nbsp; 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) # " , 表示当前是第几个命令

  [ "$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 }‘ | \ # 这是属性字段

  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 ‘/ \/ / && ( !~ /rootfs/) { print }‘ /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

    117033