当kernel启动成功之后
我们知道,kernel成功启动之后,在init/main.c中调用execve执行程序/sbin/init。init进程被称为初始化进程,因为它负责系统的启动。
我以我的slackware10为例(不光因为它是我用的套件,而且启动脚本十分清晰明了),讲一讲启动的具体过程:
(史前时期)loadlin,grub,lilo把内核参数传给kernel,如(BOOT_IMAGE=Linux ro root=307),,存储在/proc/cmdline中。
内核启动成功后,把它的参数传给1号进程(init),因为也许有一些参数应被用户程序解释。
init进程将从上到下的读取/etc/inittab文件,只要状态符合当前运行级就会去执行脚本。
inittab由几行组成,每行被三个冒号分隔成四个部分,每个部分具有不同的含义。格式如下:
行标识符:状态:动作:命令
行标识符是你的运行级脚本的名字,不能使用重复的行标识符。
状态是表示运行级脚本何时应该执行的数字。状态由0,1,2,3,4,5,6和S一个或多个数字字母组成。如果状态为空,就是系统启动必须执行的脚本。
下面是slackware的状态定义:
0 = halt
1 = single user mode
2 = unused (but configured the same as runlevel 3)
3 = multiuser mode (default Slackware runlevel)
4 = X11 with KDM/GDM/XDM (session managers)
5 = unused (but configured the same as runlevel 3)
6 = reboot
S同状态1相同。
动作有once, wait, respawn, sysinit, crtlaltdel, initdefault组成,说明了init执行脚本的方式。
once:init在进入后只执行一次。init不等待命令的结束。
wait:和once不同的是,init等待命令的结束。
respawn:命令结束后会被重起。
sysinit:init最先执行的运行级脚本,状态被忽略。说穿了就是无论何是都优先执行的脚本,应指向系统初始化脚本。
ctrlaltdel:当“三指禅”被按下时,该运行级被启动,一般是指向重启脚本。
initdefault:指定系统启动时的缺省运行级。在sysinit后执行。
对于slackware的inittab:
id:3:initdefault:
id指出缺省为多用户字符界面(3),不要把它设置成0或6!注意没有命令。
si:S:sysinit:/etc/rc.d/rc.S
si指出系统初始化运行级,其中S等同于状态1,它指向/etc/rc.d/rc.S,也就是说init第一个去执行的shell脚本。
rc:2345:wait:/etc/rc.d/rc.M
rc指出多用户启动运行级,当状态为2,3,4,5时被执行,init等待命令的结束,这也是为什么启动时没有shell可用的原因。
c1:1235:respawn:/sbin/agetty 38400 tty1 linux
c1指出控制台1,当它被杀死时init将重启它。它打开一个终端tty1供你使用。
x1:4:wait:/etc/rc.d/rc.4
x1指出多用户GUI运行级,指向脚本rc.4。
rc.S(系统初始化运行级脚本)做的事:slackware的启动过程(2)之rc.S
上节我们介绍了init进程执行的inittab脚本,这节我们分析在inittab中出现的 /etc/rc.d目录中的 “rc.X”脚本.
首先是系统初始化时执行的“rc.S”脚本,inittab中的对应行为si:S:sysinit:/etc/rc.d/rc.S
rc.S文件内容有380行之多,我们尽可能的逐行命令一一解释.
1、设置path环境变量
PATH=/sbin:/usr/sbin:/bin:/usr/bin
2、挂载proc文件系统
/sbin/mount -v proc /proc -n -t proc 2> /dev/null
proc文件系统是一个伪文件系统,它只存在于内存当中,它以文件系统的方式为访问系统内核数据的操作提供接口.例如我们查看cpu信息时经常使用”cat /proc/cpuinfo“ 命令.
3、挂载sysfs文件系统
if [ -d /sys ]; then
if grep -wq sysfs /proc/filesystems ; then
if ! grep -wq sysfs /proc/mounts ; then
/sbin/mount -v sysfs /sys -n -t sysfs
fi
fi
fi
sysfs文件系统是用来对系统的设备进行管理的,它把实际连接到系统上的设备和总线组织成一个分级的文件,用户空间的程序同样可以利用这些信息以实现和内核的交互.这种文件系统只存在于2.6内核中.
4、为2.6内核启动执行udev脚本.
if grep -wq sysfs /proc/mounts && grep -q tmpfs /proc/filesystems ; then
if ! grep -wq nohotplug /proc/cmdline ; then
if [ -x /etc/rc.d/rc.udev ]; then
/bin/sh /etc/rc.d/rc.udev start
fi
fi
fi
udev是一种工具,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等.设备文件通常放在/dev目录下.使用udev后,在/dev目录下就只包含系统中真正存在的设备.
#安装proc文件系统。
#决定是否有需要使用Hotplug自动检测硬件。
#启动devfsd。
#安装devfs。
#为2.6内核启动udev。
#打开交换分区,其中要读取/etc/fstab中获取分区信息。
#检查根分区是否为只读,因为只有只读时才能检查硬盘。
#如有需要检查根分区。
#安装sysfs到/proc/sys。
#设置硬件时钟。
#配置isa设备。
#执行/etc/rc.d/rc.modules (这个文件将剩下的内核模块装入内核,稍后再讲)。
#初始化lvm卷,不要问我为什么。
#检查非根分区。
#安装本地硬盘分区。
#删除临时文件。
#initrd被安装在/initrd中,它用来在内核启动第一时间载入一些内核模块和必需程序(如fsck)等。现在卸载它。
#创建utmp。
#如果你是用的zipslack(一个工作在vfat上的linux套件),配置umsdos。
#把Linux 2.4.27写入你的mtod文件
#执行rc.sysvinit。(不像rh,slackware是一个“叛徒”。它的init脚本结构不像大多数linux套件(基于SVR4),而像BSD,所以只是一个假文件。)
#执行rc.serical。(串口)
#安装随机数种子
rc.modules:
#决定内核版本,到目录”/lib/modules/你的内核版本号/“去寻找模块
#更新内核模块依赖关系。
#装入APM高级管理,它被注释掉了,建议你启用它以正常关机。
#一大堆被注释掉了的硬件模块,如果hotplug找不到你的硬件就到这里来。
rc.S执行完成后,slackware将执行缺省的运行级(3),也就是先执行rc.M(多用户进程级)。
#设置黑屏时间。
#设置主机名,主机名存储在/etc/HOSTNAME里,缺省为darkstar.example.net。
#设置dmesg缓冲区的大小。越多越好。
#执行rc.syslog,打开syslog和klogd。
#执行rc.pcmcia,初始化PCMCIA卡,我不懂。
#执行rc.inet1,设置网络。重点。
#执行rc.hotplug,即插即用。
#执行rc.inet2,网络守护进程。重点。
#把所有的锁文件删掉。
#把黑洞设备和临时目录设成777。
#运行ldconfig,更新共享库,我喜欢关掉。
#更新X字体缓存,关掉吧。不过安了新字体后自己要手动运行一次”fc-cache"而已。
#执行rc.CUPS,UNIX打印守护进程。
#打开appletalk。关了吧。
#打开用户限额。请看/usr/doc/Linux-HowTOs/Quota。
#执行rc.acpid。高级能源管理。
#执行rc.alsa。alsa声音系统。
#执行rc.font。用户自己的字体。
#执行rc.keymap。用户自己的keymap。
#把你的一大堆网络standalone进程打开。
#执行rc.gpm。字符界面上用鼠标。
#又执行rc.sysinit一次。BUG?
#执行rc.local。最后,执行用户你的自己的配置文件。
rc.inet1:
#读入另一个脚本文件rc.inet1.conf的变量,里面有网络的基本配置。
#打开lo环回接口。
#打开eth0 eth1 eth3 eth4, 请看eth_up函数,很长。
#如有需要使用无线网卡。
#如有需要使用DHCP。
rc.inet2
#启动rc.portmap,准备安装NFS文件系统
#安装所有smb文件系统
#执行rc.firewall,防火墙脚本。
#执行rc.ip_forward,IP转发脚本,如果你要共享上网,改它。
#一大堆网络服务程序(NFS,BIND,SSH,INETD)
rc.M执行完了,init按照inittab的配置打开终端,它创建若干个agetty进程,这些进程通过系统调用执行login程序,用户密码验证成功后,再执行shell。一天的工作开始了。在这之后,init除了监视运行级的改变外,还干一些副业:收养孤儿进程。
如果你是用的运行级4(多用户GUI运行级),init就会去执行rc.4,并打开gdm或kdm(Gnome和Kde的登录界面)。
经验:
1。init脚本里面请使用绝对路径,且限制PATH。
2。可以适当增减脚本的内容来充实功能或加快启动速度。不过你一旦挂了的话,请用init=/bin/sh或single模式开机。
3。就像看Linux内核源代码能增强C语言功底一样,看init脚本能提高Bash功底。
4。一个清晰明了的结构胜过一切描述,这也是我选择Slackware的原因。
5。如果说Windows是所见即所得的操作系统,那么说Unix/Linux是所要即所得的操作系统。(因为Unix/Linux在能彻底定制系统,要什么有什么)
#
# /etc/rc.d/rc.sysinit - run once at boot time
#
#
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
HOSTTYPE=`uname -m` # 取得主机类型
unamer=`uname -r` # 取得内核的 release 版本(例如 2.4.9.30-8)
eval version=`echo $unamer | awk -F '.' '{ print "(" $1 " " $2 ")" }'` # 取得版本号
fi
HOSTNAME=localhost # 则将主机名设置为 "localhost"
fi
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 目录下
selinuxfs=`awk '/ selinuxfs / { print $2 }' /proc/mounts`
SELINUX=
if [ -n "$selinuxfs" ] && [ "`cat /proc/self/attr/current`" != "kernel" ]; then
if [ -r $selinuxfs/enforce ] ; then
SELINUX=`cat $selinuxfs/enforce`
else
# assume enforcing if you can't read it
SELINUX=1
fi
fi
/sbin/restorecon -R /dev 2>/dev/null
fi
echo "*** Warning -- SELinux is active"
echo "*** Disabling security enforcement for system recovery."
echo "*** Run 'setenforce 1' to reenable."
echo "0" > $selinuxfs/enforce
}
if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
chvt 1
fi
echo "
*** Warning -- SELinux relabel is required. ***
*** Disabling security enforcement. ***
*** Relabeling could take a very long time, ***
*** depending on file system size. ***
"
echo "0" > $selinuxfs/enforce
/sbin/fixfiles -F relabel > /dev/null 2>&1
rm -f /.autorelabel
echo "*** Enabling security enforcement. ***"
echo $SELINUX > $selinuxfs/enforce
if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
chvt 8
fi
}
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
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
echo -en $"\t\tWelcome to " # 打印 "<tab><tab>Welcom to" ,同时不换行
if LC_ALL=C fgrep -q "Red Hat" /etc/redhat-release ; then # 从 /etc/redhat-release 文件中找出含有 "Red Hat" 的行,如果找到
[ "$BOOTUP" = "color" ] && echo -en "\\033[0;31m" # 则变量 BOOTUP 的值为 color ,并设置输出字体输出红色
echo -en "Red Hat" # 同时打印 "Red Hat" ,接下来打印发行版本(产品)
[ "$BOOTUP" = "color" ] && echo -en "\\033[0;39m" # 如果变量 BOOTUP 的值为 color 则设置输出字体为白色
PRODUCT=`sed "s/Red Hat \(.*\) release.*/\1/" /etc/redhat-release` # 从 /etc/redhat-release 中找出含有 "Red Hat" 且后面若干字符,然后是 "release" 的行,并截取中间部分给 PRODUCT
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
if [ "$PROMPT" != "no" ]; then # 如果变量 PROMPT 的值不为 "no" (表示允许交互启动),则
echo -en $"\t\tPress 'I' to enter interactive startup." # 打印提示信息“Press I to enter interactive startup”,但此时按 I 还未起作用
echo
fi
if [ -n "$LOGLEVEL" ]; then # 如果 LOGLEVEL 变量的值不为空
/bin/dmesg -n $LOGLEVEL # 则执行 dmesg ,设置打印到 consoel 的日志的级别为 $LOGLEVEL
fi
cmdline=$(cat /proc/cmdline) # 读取 /proc/cmdline ,这是内核启动的时的参数,赋予变量 cmdline
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
scsi=""
network=""
audio=""
other=""
eval `kmodule | while read devtype mod ; do # 从 kmodule 命令的输出一次读入两个变量 devtype (设备类型)和 mod(模块名)
case "$devtype" in # 根据 devtype 做出适当的选择
"IDE") ide="$ide $mod" # 如果 devtype 的值为 IDE ,则把 mod 加入到现有的 ide 变量的值中
echo "ide=\"$ide"\";; # 输出 "ide=" 然后是变量 ide 的值
"SCSI") scsi="$scsi $mod" # 如果 devtype 的值为 SCSI ,则把 mod 变量的值加入到 scsi 变量的值中
echo "scsi=\"$scsi"\";; # 输出 "scsi=" 再输出 $scsi
"NETWORK") network="$network $mod" # 下面的 NETWORK 和 AUDIO 也一样
echo "network=\"$network"\";;
"AUDIO") audio="$audio $mod"
echo "audio=\"$audio"\";;
*) other="$other $mod" # 如果是属于 other 类型的则把模块名 $mod 加入到 other 变量的值列表中
echo "other=\"$other"\";;
esac
done`
LC_ALL=C fgrep -xq "$1" /etc/hotplug/blacklist 2>/dev/null || modprobe $1 >/dev/null 2>&1 # 在 /etc/hotplug/blacklist 文件中查找是否有指定的模块,如果没有,
} # 再用 modprobe 加载指定的模块
for module in $ide ; do # 从 ide 变量中每次取出1个值,赋予变量 module
load_module $module # 再用 load_module 函数加载它。如果该模块存在于 /etc/hotplug/blacklist ,则不会被加载(因为存在于黑名单中)
done
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 模块
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 排序,然后再组合回去
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 从目录名堆栈中弹出,并返回原来的目录
load_module $module # 也一一加载
done
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
for module in $other ; do
load_module $module # 这个直接用 load_moudle 函数加载了
done
success # 调用 success 函数,记录该事件
echo
# 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 ,这是一种伪文件系统
LC_MESSAGES= /usr/bin/rhgb # 把 "/usr/sbin/rhgb" 赋予变量 LC_MESSAGES
RHGB_STARTED=1 # RHGB_STARTED 变量的值为1。
fi
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
update_boot_stage RCclock # 同样是告诉 update_boot_stage 该事件(执行 /usr/bin/rhgb-client --update="$1" 命令,这里 $1 就是 "RCclock")
ARC=0
SRM=0
UTC=0
. /etc/sysconfig/clock
if [ "${CLOCKMODE}" = "GMT" ]; then # 如果变量 CLOCKMODE 为旧式的 GMT 格式
UTC=true # 则 UTC 变量的值为 ture
elif [ "${CLOCKMODE}" = "ARC" ]; then # 如果 CLOCKMODE 的值是 ARC ,则
ARC=true # ARC 的值为 true
fi # 如果 CLOCKMODE 不等于 GMT 或者 UTC ,则 UTC 的值等于 /etc/sysconfig/clock 中的值,一般都是 false
fi # 如果 CLOCKMODE 不等于 GMT 后者 ARC ,则 UTC 的值为 0
CLOCKFLAGS="$CLOCKFLAGS --hctosys" # 而 CLOCKFLAGS 变量的值为原来的值(可能为空)加上 '--hctosys' ,表示把硬件时钟写入内核时钟
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
KEYTABLE= # 设置 KEYTABLE 和 KEYMAP
KEYMAP=
if [ -f /etc/sysconfig/console/default.kmap ]; then # 假如存在 /etc/sysconfig/console/default.kmap ,则
KEYMAP=/etc/sysconfig/console/default.kmap # KEYMAP 变量的值为 /etc/sysconfig/console/default.kmap
else # 否则
if [ -f /etc/sysconfig/keyboard ]; then # 如果存在 /etc/sysconfig/keyboard 文件,则执行它
. /etc/sysconfig/keyboard # 该文件设置 KEYTABLE 变量的值,例如 KEYTABLE="/usr/lib/kbd/keytables/us.map
fi
if [ -n "$KEYTABLE" -a -d "/lib/kbd/keymaps" ]; then # 如果 KEYTABLE 的值不为空,且存在 /lib/kbd/keymaps/ 目录,则
KEYMAP="$KEYTABLE.map" # KEYMAP 的值为 KEYTABLE 的值加上 ".map"
fi
fi
if [ -n "$KEYMAP" ]; then # 假如 KEYMAP 变量的值不为空(间接表示 KEYTABLE 不为空且存在 keymaps 目录)
# Since this takes in/output from stdin/out, we can't use initlog
if [ -n "$KEYTABLE" ]; then # 且 KEYTABLE 的值不为空
echo -n $"Loading default keymap ($KEYTABLE): " # 则输出 "Loading default keymap xxxx"
else
echo -n $"Loading default keymap: " # 否则只输出 Loading default keymap
fi
loadkeys $KEYMAP < /dev/tty0 > /dev/tty0 2>/dev/null && \ # 接下来用 loadkeys 加载 $KEYMAP 指定的键盘映射文件。
success $"Loading default keymap" || failure $"Loading default keymap" # 如果成功则调用 success 函数,否则调用 failure 函数
echo
fi
fi
update_boot_stage RChostname # 告诉 update_boot_stage 函数该事件,以执行 /usr/sbin/rhgb-client --update RChostname 命令
action $"Setting hostname ${HOSTNAME}: " hostname ${HOSTNAME} # 打印 "setting hostname xxxx" 字符串,并执行 hostname ${HOSTNAME}设置主机名
if [ -d /proc/acpi ]; then # 如果存在 /proc/acpi 目录,则
for module in /lib/modules/$unamer/kernel/drivers/acpi/* ; do # 对于在 /lib/modules/<kernel-version>/kernel/drivers/acpi/ 目录下的所有模块文件,
insmod $module >/dev/null 2>&1 # 都用 insmod 命令加载到内核中
done
fi
fastboot=yes # 如果有,则 fastboot 变量的值为 yes。/fastboot 是由 shutdown -f 创建的,表示重启时不作 fsck
fi
fsckoptions=`cat /fsckoptions`
fi
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 [ "$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秒,并
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
fsckoptions="-C $fsckoptions" # -C 表示显示 fsck 进度信息
else # 否则
fsckoptions="-V $fsckoptions" # -V 表示显示 verbose 信息
fi
. /etc/sysconfig/readonly-root # 执行该文件
# Call rc.readonly to set up magic stuff needed for readonly root # 执行 /etc/rc.readonly 脚本
. /etc/rc.readonly
fi
fi
#########################################################################################################################################################
_RUN_QUOTACHECK=0 # 初始化 RUN_QUOTACHECK 的值为0.这里是作一个标记,因为后面的代码可能会导致重启。在这里做好标记
ROOTFSTYPE=`awk '/ \/ / && ($3 !~ /rootfs/) { print $3 }' /proc/mounts` # 从 /proc/mounts 中找出 / 文件系统的类型,并赋予变量 ROOTFSTYPE
echo $STRING
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 -T -a $rootdev $fsckoptions > /etc/rhgb/temp/rhgb-console # 执行 fsck 命令,-T 表示不打印标题栏,-a 表示自动修复错误,设备是 / ,后面是选项
else # 否则
initlog -c "fsck -T -a $rootdev $fsckoptions" # 调用 initlog 执行 fsck ,信息直接输出到屏幕,fsck 命令本身都是一样的。
fi
rc=$? # 把 fsck 的结果送给变量 rc
success "$STRING" &n