linux系统配置之开机启动过程(centos)

1.开机流程如下:

 

2.BIOS

BIOS是英文"Basic Input Output System"的缩略词,直译过来后中文名称就是"基本输入输出系统"。其实,它是一组固化到计算机主板上一个ROM芯片上的程序,它保存着计算机最重要的基本输入输出的程序、开机后自检程序和系统自启动程序,它可从CMOS中读写系统设置的具体信息。 其主要功能是为计算机提供最底层的、最直接的硬件设置和控制。

3.BootLoader

  Bootloader是嵌入式系统在加电后执行的第一段代码,在它完成CPU和相关硬件的初始化之后,再将操作系统映像或固化的嵌入式应用程序装在到内存中然后跳转到操作系统所在的空间,启动操作系统运行。他有两个工作模式1.自启动模式:在这种模式下,bootloader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。2.交互模式:在这种模式下,目标机上的bootloader将通过串口或网络等通行手段从开发主机(Host)上下载内核映像等到RAM中。可以被bootloader写到目标机上的固态存储媒质中,或者直接进入系统的引导。也可以通过串口接收用户的命令。

  Bootloader启动大多数都分为两个阶段。第一阶段主要包含依赖于CPU的体系结构硬件初始化的代码,通常都用汇编语言来实现。这个阶段的任务有:
基本的硬件设备初始化(屏蔽所有的中断、关闭处理器内部指令/数据Cache等)。
为第二阶段准备RAM空间。
如果是从某个固态存储媒质中,则复制Bootloader的第二阶段代码到RAM。
设置堆栈。
在第一阶段中为什么要关闭Cache?通常使用Cache以及写缓冲是为了提高系统性能,但由于Cache的使用可能改变访问主存的数量、类型和时间,因此Bootloader通常是不需要的。
跳转到第二阶段的C程序入口点。
第二阶段通常用C语言完成,以便实现更复杂的功能,也使程序有更好的可读性和可移植性。这个阶段的任务有:
初始化本阶段要使用到的硬件设备。
检测系统内存映射。
将内核映像和根文件系统映像从Flash读到RAM。
为内核设置启动参数。
调用内核。
  主要加载vmlinux和initrd到内存中,并跳转到内核中运行

4.kernel

  Kernel 操作系统内核 操作系统内核是指大多数操作系统的核心部分。它由操作系统中用于管理存储器、文件、外设和系统资源的那些部分组成。操作系统内核通常运行进程,并提供进程间的通信。

5.init

init的进程号是1(ps -aux | less),从这一点就能看出,init进程是系统所有进程的起点,Linux在完成核内引导以后,就开始运行init程序。

init程序需要读取配置文件/etc/inittab。inittab是一个不可执行的文本文件,它有若干行指令所组成。

理解Runlevel:

  runlevel用来表示在init进程结束之后的系统状态,在系统的硬件中没有固定的信息来表示runlevel,它纯粹是一种软件结构。init和inittab是runlevel影响系统状态的唯一原因。在上述例子中inittab文件起始阶段的注释主

用来描述runlevel:

  Runlevel 0 是让init关闭所有进程并终止系统。

  Runlevel 1 是用来将系统转到单用户模式,单用户模式只能有系统管理员进入,在该模式下处理那些在有登录用户的情况下不能进行更改的文件,改runlevel的编号1也可以用S代替。

  Runlevel 2 是允许系统进入多用户的模式,但并不支持文件共享,这种模式很少应用。

  Runlevel 3 是最常用的运行模式,主要用来提供真正的多用户模式,也是多数服务器的缺省模式。

  Runlevel 4 一般不被系统使用,用户可以设计自己的系统状态并将其应用到runlevel 4阶段,尽管很少使用,但使用该系统可以实现一些特定的登录请求。

  Runlevel 5 是将系统初始化为专用的X Window终端。对功能强大的Linux系统来说,这并不是好的选择,但用户如果需要这样,也可以通过在runlevel启动来实现该方案。

  Runlevel 6 是关闭所有运行的进程并重新启动系统。

inittab文件内容:

  ###表示当前缺省运行级别为5(initdefault);

  id:5:initdefault: /*启动后进入图形界面,设为3则进入字符终端界面*/

  ###启动时自动执行/etc/rc.d/rc.sysinit脚本(sysinit)

  /*

  在inittab文件中以#开头的所有行都是注释行。注释行有助于用户理解inittab文件,inittab文件中的值都是如下格式:

  label:runlevel:action:process

  label是1~4个字符的标签,用来标示输入的值。一些系统只支持2个字符的标签。鉴于此原因,多数人都将标签字符的个数限制在2个以内。该标签可以是任意字符构成的字符串,但实际上,某些特定的标签是常用的,在Red Hat Linux中使用的标签是:

  代码:

  id 用来定义缺省的init运行的级别

  si 是系统初始化的进程

  ln 其中的n从1~6,指明该进程可以使用的runlevel的级别

  ud 是升级进程

  ca 指明当按下Ctrl+Alt+Del时运行的进程

  pf 指当UPS表明断电时运行的进程

  pr 是在系统真正关闭之前,UPS发出电源恢复的信号时需要运行的进程

  x 是将系统转入X终端时需要运行的进程

  runlevel字段指定runlevel的级别。可以指定多个runlevel级别,也可以不为runlevel字段指定特定的值。

  action字段定义了该进程应该运行在何种状态下:

  代码:

  boot 在系统启动时运行,忽略runlevel

  bootwait 在系统启动时运行,init等待进程完成。忽略runlevel

  ctrlaltdel 当Ctrl+Alt+Del三个键同时按下时运行,把SIGINT信号发送给init。忽略runlevel

  initdefault 不要执行这个进程,它用于设置默认runlevel

  kbrequest 当init从键盘中收到信号时运行。这里要求键盘组合符合KeyBoardSigral(参见/usr/share/doc/kbd-*关于键盘组合的文档)

  off 禁止进入,因此该进程不运行

  once 每一个runlevel级别运行一次

  ondemand 当系统指定特定的运行级别A、B、C时运行

  powerfail 当init收到SIGPWR信号时运行

  powerokwait 当收到SIGPWD信号且/etc/文件中的电源状态包含OK时运行

  powerwait 当收到SIGPWD信号,并且init等待进程结束时运行

  respawn 不管何时终止都重新启动进程

  sysinit 在运行boot或bootwait进程之前运行

  wait 运行进程等待输入运行模式

  process字段包含init执行的进程,该进程采用的格式与在命令行下运行该进程的格式一样,因此process字段都以该进程的名字开头,紧跟着是 运行时,紧跟着是运行时要传递给该进程的参数。比如/sbin/shutdown -t3 -r now,该进程在按下Ctrl+Alt+Del时执行,在命令行下也可以直接输入来重新启动系统。

  特殊目的的记录

  仔细学习例子文件,学习应用其中关于inittab的语法格式。该文件的大多数内容都可以忽略,因为超过一半的内容都是注释,剩余的一些文件内容主要是用来实现某些特殊的功能:

  id 的值表明缺省的runlevel是3。

  ud 的值可以唤醒/sbin/update进程,该进程为保持磁盘的完整性,将在对磁盘进行I/O操作之前清空整个I/O缓冲区。

  pf、pr和ca的值只被特定的中断所调用。

  如果系统是专用的X终端,则只需x的输入值。

  getty进程来提供虚拟终端设备的服务,例如:

  3:2345:respawn:/sbin/mingetty tty3

  标签字段的值是3,3是设备tty3的数字后缀,tty3与相应的进程相关联,该getty进程可以启动的runlevel是2、3、4和5, 当该进程终止时,init马上就重新启动它。启动进程的路径名是/sbin/mingetty,该进程是实现虚拟终端支持的最小版本的getty,为tty3提供启动虚拟设备的进程。

  si::sysinit:/etc/rc.d/rc.sysinit

  该值告诉init程序运行/etc/rc.d/rc.sysinit脚本文件来初始化系统,该脚本文件与所有启动的脚本类似,它只是一个包含Linux的shell命令的可执行文件,注意输入的字符串必须包括该脚本的完整路径。不同版本的Linux存放该脚本的位置也不相同,但不用刻意去记忆这些位置,只需查看/etc/inittab文件即可,该文件中包含启动脚本文件的确切位置。*/

  # System initialization.

  si::sysinit:/etc/rc.d/rc.sysinit

  l0:0:wait:/etc/rc.d/rc 0

  l1:1:wait:/etc/rc.d/rc 1

  l2:2:wait:/etc/rc.d/rc 2

  l3:3:wait:/etc/rc.d/rc 3

  l4:4:wait:/etc/rc.d/rc 4

  ###当运行级别为5时,以5为参数运行/etc/rc.d/rc脚本,init将等待其返回(wait)

  l5:5:wait:/etc/rc.d/rc 5

  l6:6:wait:/etc/rc.d/rc 6

  ###在启动过程中允许按CTRL-ALT-DELETE重启系统

  # Trap CTRL-ALT-DELETE

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

  # When our UPS tells us power has failed, assume we have a few minutes

  # of power left. Schedule a shutdown for 2 minutes from now.

  # This does, of course, assume you have powerd installed and your

  # UPS connected and working correctly.

  pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"

  # If power was restored before the shutdown kicked in, cancel it.

  pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"

  ###在2、3、4、5级别上以ttyX为参数执行/sbin/mingetty程序,打开ttyX终端用于用户登录,

  ###如果进程退出则再次运行mingetty程序(respawn)

  # Run gettys in standard runlevels

  1:2345:respawn:/sbin/mingetty tty1

  2:2345:respawn:/sbin/mingetty tty2

  3:2345:respawn:/sbin/mingetty tty3

  4:2345:respawn:/sbin/mingetty tty4

  5:2345:respawn:/sbin/mingetty tty5

  6:2345:respawn:/sbin/mingetty tty6

  ###在5级别上运行xdm程序,提供xdm图形方式登录界面,并在退出时重新执行(respawn)

  # Run xdm in runlevel 5

  x:5:respawn:/etc/X11/prefdm -nodaemon

  以上面的inittab文件为例,来说明一下inittab的格式。其中以#开始的行是注释行,除了注释行之外,每一行都有以下格式:

  id:runlevel:action:process

  对上面各项的详细解释如下:

  1. id

  id是指入口标识符,它是一个字符串,对于getty或mingetty等其他login程序项,要求id与tty的编号相同,否则getty程序将不能正常工作。

  2. runlevel

  runlevel是init所处于的运行级别的标识,一般使用0-6以及S或s。0、1、6运行级别被系统保留:其中0作为shutdown动作,1作为重启至单用户模式,6为重启;S和s意义相同,表示单用户模式,且无需inittab文件,因此也不在inittab中出现,实际上,进入单用户模式时,init直接在控制台(/dev/console)上运行/sbin/sulogin。在一般的系统实现中,都使用了2、3、4、5几个级别,在Redhat系统中,2表示无NFS支持的多用户模式,3表示完全多用户模式(也是最常用的级别),4保留给用户自定义,5表示XDM图形登录方式。7- 9级别也是可以使用的,传统的Unix系统没有定义这几个级别。runlevel可以是并列的多个值,以匹配多个运行级别,对大多数action来说,仅当runlevel与当前运行级别匹配成功才会执行。

  3. action

  action是描述其后的process的运行方式的。action可取的值包括:initdefault、sysinit、boot、bootwait等:

  initdefault是一个特殊的action值,用于标识缺省的启动级别;当init由核心激活以后,它将读取inittab中的initdefault项,取得其中的runlevel,并作为当前的运行级别。如果没有inittab文件,或者其中没有initdefault项,init将在控制台上请求输入runlevel。

  sysinit、boot、bootwait等action将在系统启动时无条件运行,而忽略其中的runlevel。

  其余的action(不含initdefault)都与某个runlevel相关。各个action的定义在inittab的man手册中有详细的描述。

  4. process

  process为具体的执行程序。程序后面可以带参数。

  第三部分:系统初始化

  在init的配置文件中有这么一行:

  si::sysinit:/etc/rc.d/rc.sysinit

  它调用执行了/etc/rc.d/rc.sysinit,而rc.sysinit是一个bash shell的脚本,它主要

  在init的配置文件中有这么一行:

  si::sysinit:/etc/rc.d/rc.sysinit

  它调用执行了/etc/rc.d/rc.sysinit,而rc.sysinit是一个bash shell的脚本,它主要是完成一些系统初始化的工作,rc.sysinit是每一个运行级别都要首先运行的重要脚本。它主要完成的工作有:激活交换分区,检查磁盘,加载硬件模块以及其它一些需要优先执行任务。

  rc.sysinit约有850多行,但是每个单一的功能还是比较简单,而且带有注释,建议有兴趣的用户可以自行阅读自己机器上的该文件,以了解系统初始化所详细情况。由于此文件较长,所以不在本文中列出来,也不做具体的介绍。

  当rc.sysinit程序执行完毕后,将返回init继续下一步。

  第四部分:启动对应运行级别的守护进程

  在rc.sysinit执行后,将返回init继续其它的动作,通常接下来会执行到/etc/rc.d/rc程序。以运行级别5为例,init将执行配置文件inittab中的以下这行:

  l5:5:wait:/etc/rc.d/rc 5

  这一行表示以5为参数运行/etc/rc.d/rc,/etc/rc.d/rc是一个Shell脚本,它接受5作为参数,去执行/etc /rc.d/rc5.d/目录下的所有的rc启动脚本,/etc/rc.d/rc5.d/目录中的这些启动脚本实际上都是一些链接文件,而不是真正的rc 启动脚本,真正的rc启动脚本实际上都是放在/etc/rc.d/init.d/目录下。而这些rc启动脚本有着类似的用法,它们一般能接受start、stop、restart、status等参数。

  /etc/rc.d/rc5.d/中的rc启动脚本通常是K或S开头的链接文件,对于以以S开头的启动脚本,将以start参数来运行。而如果发现存在相应的脚本也存在K打头的链接,而且已经处于运行态了(以/var/lock/subsys/下的文件作为标志),则将首先以stop为参数停止这些已经启动了的守护进程,然后再重新运行。这样做是为了保证是当init改变运行级别时,所有相关的守护进程都将重启。

  至于在每个运行级中将运行哪些守护进程,用户可以通过chkconfig或setup中的"System Services"来自行设定。常见的守护进程有:

  amd:自动安装NFS守护进程

  apmd:高级电源管理守护进程

  arpwatch:记录日志并构建一个在LAN接口上看到的以太网地址和IP地址对数据库

  autofs:自动安装管理进程automount,与NFS相关,依赖于NIS

  crond:Linux下的计划任务的守护进程

  named:DNS服务器

  netfs:安装NFS、Samba和NetWare网络文件系统

  network:激活已配置网络接口的脚本程序

  nfs:打开NFS服务

  portmap:RPC portmap管理器,它管理基于RPC服务的连接

  sendmail:邮件服务器sendmail

  smb:Samba文件共享/打印服务

  syslog:一个让系统引导时起动syslog和klogd系统日志守候进程的脚本

  xfs:X Window字型服务器,为本地和远程X服务器提供字型集

  Xinetd:支持多种网络服务的核心守护进程,可以管理wuftp、sshd、telnet等服务

  这些守护进程也启动完成了,rc程序也就执行完了,然后又将返回init继续下一步。

  第五部分:建立终端

  rc执行完毕后,返回init。这时基本系统环境已经设置好了,各种守护进程也已经启动了。init接下来会打开6个终端,以便用户登录系统。通过按Alt+Fn(n对应1-6)可以在这6个终端中切换。在inittab中的以下6行就是定义了6个终端:

  1:2345:respawn:/sbin/mingetty tty1

  2:2345:respawn:/sbin/mingetty tty2

  3:2345:respawn:/sbin/mingetty tty3

  4:2345:respawn:/sbin/mingetty tty4

  5:2345:respawn:/sbin/mingetty tty5

  6:2345:respawn:/sbin/mingetty tty6

  从上面可以看出在2、3、4、5的运行级别中都将以respawn方式运行mingetty程序,mingetty程序能打开终端、设置模式。同时它会显示一个文本登录界面,这个界面就是我们经常看到的登录界面,在这个登录界面中会提示用户输入用户名,而用户输入的用户将作为参数传给login 程序来验证用户的身份。

  第六部分:登录系统,启动完成

  对于运行级别为5的图形方式用户来说,他们的登录是通过一个图形化的登录界面。登录成功后可以直接进入KDE、Gnome等窗口管理器。而本文主要讲的还是文本方式登录的情况:

  当我们看到mingetty的登录界面时,我们就可以输入用户名和密码来登录系统了。

  Linux的账号验证程序是login,login会接收mingetty传来的用户名作为用户名参数。然后login会对用户名进行分析:如果用户名不是root,且存在/etc/nologin文件,login将输出nologin文件的内容,然后退出。这通常用来系统维护时防止非root 用户登录。只有/etc/securetty中登记了的终端才允许root用户登录,如果不存在这个文件,则root可以在任何终端上登录。/etc /usertty文件用于对用户作出附加访问限制,如果不存在这个文件,则没有其他限制。

  在分析完用户名后,login将搜索/etc/passwd以及/etc/shadow来验证密码以及设置账户的其它信息,比如:主目录是什么、使用何种shell。如果没有指定主目录,将默认为根目录;如果没有指定shell,将默认为/bin/bash。

  login程序成功后,会向对应的终端在输出最近一次登录的信息(在/var/log/lastlog中有记录),并检查用户是否有新邮件(在/usr/spool/mail/的对应用户名目录下)。然后开始设置各种环境变量:对于bash来说,系统首先寻找/etc/profile脚本文件,并执行它;然后如果用户的主目录中存在.bash_profile文件,就执行它,在这些文件中又可能调用了其它配置文件,所有的配置文件执行后后,各种环境变量也设好了,这时会出现大家熟悉的命令行提示符,到此整个启动过程就结束了。

inittab 文档描述在系统引导及通常的操作期间, 都启动哪些进程(比如/etc/init.d/boot, /etc/init.d/rc, getty 等等).

Init

(8) 讨论有关runlevels (运行级) 的概念, 每一个运行级都有他自己启动进程的集合. 有效的运行级为0 -6 加上用于ondemand 条目的A , B 和C . inittab 文档中的每一个条目有如下的格式:

id :runlevels :action :process

以`#’ 开头的行被忽略.

id

inittab 文档中条目的唯一标识, 限于1-4 个字符(假如是用版本号小于5.2.18 或a.out 的库编译生成的sysvinit 程式, 则仅限于2 个字符).

注意: 对于getty 或其他的注册进程, id 必须是响应的终端线路的tty 后缀, 如1 响应tty1 , 否则, 注册过程不能正常的工作.

runlevels

列出发生指定动作的运行级.

action

描述要发生的动作.

process

要执行的进程. 假如process 域以一个`+’ 开头, init 不会在utmp 和wtmp 文档中为此进程记帐. 这是由于getty 自己主持utmp/wtmp 记帐的需要, 同时这也是个历史遗留的漏洞.

runlevels 域能够包含表示不同运行级的多个字符, 例如123 表示本进程在运行级为1, 2 和3 时都要启动. 用于ondemand 条目的runlevels 域能够包含A , B , 或C . 用于sysinit , boot , 和bootwait 条目的runlevels 域被忽略.

当改变运行级时, 在新运行级中没有给出的那些正在运行的进程被杀死, 先使用SIGTERM 信号, 然后是SIGKILL.

action 域能够使用的动作有:

respawn

该进程只要终止就立即重新启动(如getty).

wait

只要进入指定的运行级就启动本进程, 并且init 等待该进程的结束.

once

只要进入指定的运行级就启动一次本进程.

boot

在系统引导期间执行本进程. runlevels 域被忽略.

bootwait

在系统引导期间执行本进程. 并且init 等待该进程的结束(如/etc/rc). runlevels 域被忽略.

off

什么也不做.

ondemand

在进入ondemand 运行级时才会执行标记为ondemand 的那些进程. 无论怎样, 实际上没有改变运行级(ondemand 运行级就是`a’, `b’, 和`c’).

initdefault

initdefault 条目给出系统引导完成后进入的运行级, 假如不存在这样的条目, init 就会在控制台询问要进入的运行级. process 域被忽略.

sysinit

系统引导期间执行此进程. 本进程会在boot 或bootwait 条目之前得到执行. runlevels 域被忽略.

powerwait

本进程在电源不足时执行. 通常在有进程把UPS 和电脑相连时通知init 进程, Init 在继续其他工作之前要等待此进程结束.

powerfail

类似powerwait , 但是init 不等待此进程完成.

powerokwait

在init 收到电源已恢复的通知后立即执行此进程.

powerfailnow

本进程在init 被告知UPS 电源快耗尽同时外部电源失败(无效) 时被执行. (假设UPS 和监控进程能够发现这样的情况).

ctrlaltdel

在init 收到SIGINT 信号时执行此进程. 这意味着有人在控制台按下了CTRL-ALT-DEL 组合键, 典型地, 可能是想执行类似shutdown 然后进入单用户模式或重新引导机器.

kbrequest

本进程在init 收到一个从控制台键盘产生的特别组合按键信号时执行.

对于此功能本文档尚未完成; 能够在kbd-x.xx 包中找到更多信息(在写作本文档时最新的是kbd-0.94). 当然您可能想为某些"KeyboardSignal" 行为映射组合键, 如为了映射(Alt-上箭头) 能够在键盘映射文档中 使用如下的方式:

alt keycode 103 = KeyboardSignal

举例

这是个和老的Linux inittab 文档类似的例子文档:

# inittab for linux

id:1:initdefault:

rc::bootwait:/etc/rc

1:1:respawn:/etc/getty 9600 tty1

2:1:respawn:/etc/getty 9600 tty2

3:1:respawn:/etc/getty 9600 tty3

4:1:respawn:/etc/getty 9600 tty4

本文档在引导时执行/etc/rc 并且在ty1-tty4 上启动getty 进程.

一个更详尽的inittab 会有不同的运行级(参考本身的注释):

# 进入默认的运行级

id:2:initdefault:

# 在进行其他工作之前先完成系统初始化.

si::sysinit:/etc/rc.d/bcheckrc

# 运行级0 挂起系统, 6 重新引导, 1 单用户模式.

l0:0:wait:/etc/rc.d/rc.halt

l1:1:wait:/etc/rc.d/rc.single

l2:2345:wait:/etc/rc.d/rc.multi

l6:6:wait:/etc/rc.d/rc.reboot

# "3 个键" 按下时要做的工作.

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

# 运行级2和3: 在控制台生成getty 进程, 运行级为3时在modem 上生成getty.

1:23:respawn:/sbin/getty tty1 VC linux

2:23:respawn:/sbin/getty tty2 VC linux

3:23:respawn:/sbin/getty tty3 VC linux

4:23:respawn:/sbin/getty tty4 VC linux

S2:3:respawn:/sbin/uugetty ttyS2 M19200

文档

/etc/inittab

zylonite 上是

::sysinit:/etc/init.d/rcS

ttyS0::respawn:-/bin/sh

ttyS1::respawn:-/bin/sh

ttyS2::respawn:-/bin/sh

tty1::respawn:-/bin/sh

tty2::respawn:-/bin/sh

::ctrlaltdel:/bin/umount -a -r

fedra 3 上是

#

# inittab This file describes how the INIT process should set up

# the system in a certain run-level.

#

# Author: Miquel van Smoorenburg,

# Modified for RHS Linux by Marc Ewing and Donnie Barnes

#

# Default runlevel. The runlevels used by RHS are:

# 0 - halt (Do NOT set initdefault to this)

# 1 - Single user mode

# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)

# 3 - Full multiuser mode

# 4 - unused

# 5 - X11

# 6 - reboot (Do NOT set initdefault to this)

#

id:3:initdefault:

# System initialization.

si::sysinit:/etc/rc.d/rc.sysinit

l0:0:wait:/etc/rc.d/rc 0

l1:1:wait:/etc/rc.d/rc 1

l2:2:wait:/etc/rc.d/rc 2

l3:3:wait:/etc/rc.d/rc 3

l4:4:wait:/etc/rc.d/rc 4

l5:5:wait:/etc/rc.d/rc 5

l6:6:wait:/etc/rc.d/rc 6

# Trap CTRL-ALT-DELETE

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

# When our UPS tells us power has failed, assume we have a few minutes

# of power left. Schedule a shutdown for 2 minutes from now.

# This does, of course, assume you have powerd installed and your

# UPS connected and working correctly.

pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"

# If power was restored before the shutdown kicked in, cancel it.

pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"

# Run gettys in standard runlevels

1:2345:respawn:/sbin/mingetty tty1

2:2345:respawn:/sbin/mingetty tty2

3:2345:respawn:/sbin/mingetty tty3

4:2345:respawn:/sbin/mingetty tty4

5:2345:respawn:/sbin/mingetty tty5

6:2345:respawn:/sbin/mingetty tty6

# Run xdm in runlevel 5

x:5:respawn:/etc/X11/prefdm -nodaemon

6./etc/rc.d/rc.sysinit

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 $" Welcome 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 $" Press '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"";;                                                                            # 输出 "ide=" 然后是变量 ide 的值 
  "SCSI") scsi="$scsi $mod"                                                                          # 如果 devtype 的值为 SCSI ,则把 mod 变量的值加入到 scsi 变量的值中 
     echo "scsi="$scsi"";;                                                                           # 输出 "scsi=" 再输出 $scsi   
  "NETWORK") network="$network $mod"                                                    # 下面的 NETWORK 和 AUDIO 也一样 
     echo "network="$network"";; 
  "AUDIO") audio="$audio $mod" 
     echo "audio="$audio"";; 
  *) other="$other $mod"                                                                            # 如果是属于 other 类型的则把模块名 $mod 加入到 other 变量的值列表中 
     echo "other="$other"";; 
 esac 
done` 
  
load_module () {                    # 定义一个函数名为 load_module 
 LC_ALL=C fgrep -xq "$1" /etc/hotplug/blacklist 2>/dev/null || modprobe $1 >/dev/null 2>&1        # 在 /etc/hotplug/blacklist 文件中查找是否有指定的模块,如果没有, 
}                                                                                                                                            # 再用 modprobe 加载指定的模块 
  
# IDE                                        # 下面开始加载所有 IDE 类型的设备的模块 
for module in $ide ; do                 # 从 ide 变量中每次取出1个值,赋予变量 module  
 load_module $module                 # 再用 load_module 函数加载它。如果该模块存在于 /etc/hotplug/blacklist ,则不会被加载(因为存在于黑名单中) 
done 
  
# SCSI                                    # SCSI 方面的比较特殊,除了加载 scsi 变量中的模块外,还会从 modprobe -c (显示所有配置)中找出 scsi 卡(hostadapter)的模块 
for module in `/sbin/modprobe -c | awk '/^alias[[:space:]]+scsi_hostadapter[[:space:]]/ { print $3 }'` $scsi; do   # 从 modprobe -c 中找出所有scsi卡的模块别名, 
 load_module $module             # 并和 scsi 变量一起,通过 for 循环一一加载 
done 
 
load_module floppy                # 然后加载 floppy 模块 
  
echo -n $" storage"                # 输出 "storage" ,表示存储方面的模块加载已经完成 
  
# Network                            # 接下来是加载网络设备模块 
 
pushd /etc/sysconfig/network-scripts >/dev/null 2>&1        # pushd 是一个 bash 的内建命令,把目录名放入目录堆栈的顶部,并进入指定目录 
interfaces=`ls ifcfg* | LC_ALL=C egrep -v '(ifcfg-lo|:|rpmsave|rpmorig|rpmnew)' |         # 找出 network-scripts 目录下所有 lscfg 开头的文件名,排除指定备份和loopback 
            LC_ALL=C egrep -v '(~|.bak)$' |                                                                     # 排除以 ~ 和 .bask 结尾的文件名 
            LC_ALL=C egrep 'ifcfg-[A-Za-z0-9._-]+$' |                                                        # 找出所有以 ifcfg- 开头,并以多个字母/数字结尾的文件名 
     sed 's/^ifcfg-//g' |                                                                                                  # 把前缀 ifcfg- 去掉,只留下后面的接口名 
     sed 's/[0-9]/ &/' | LC_ALL=C sort -k 1,1 -k 2n | sed 's/ //'`                                         # 在数字前面加上空格,是类型和编号分离,便于 sort 排序,然后再组合回去 
                                                                                                                                 # 目的是按顺序启动接口 
for i in $interfaces ; do                                                                                                # 对于在 $interfaces 变量中的每个接口 
 eval $(LC_ALL=C fgrep "DEVICE=" ifcfg-$i)                                                                     # 从对应的文件 ifcfg-$i 找出 DEVICE= 行 
 load_module $DEVICE                                                                                                 # 然后用 load_module 加载它,注意,DEVICE= 行可以是别名,例如 eth1.n7css 
done 
popd >/dev/null 2>&1                                                                                                 # 把 network-scripts 从目录名堆栈中弹出,并返回原来的目录 
                                                                                 
for module in $network ; do                                                                                        #  剩下还有 network 变量中的模块 
 load_module $module                                                                                                # 也一一加载 
done 
  
echo -n $" network"                                                                                                    # 输出 network 字符串,表示加载网络设备模块工作完毕 
  
# Sound 
for module in `/sbin/modprobe -c | awk '/^alias[[:space:]]+snd-card-[[:digit:]]+[[:space:]]/ { print $3 }'` $audio; do    # 和 scsi 一样,加载在 modprobe -c 和 audio 变量 
 load_module $module                                                                                                                                          # 中的模块 
done 
  
echo -n $" audio"                                                                                                        # 输出 audio ,表示声卡设备驱动加载完毕,一般会有多个驱动被加载         
  
# Everything else (duck and cover)                                                                             # 剩下的就是 other 类型的设备了 
for module in $other ; do 
 load_module $module                                                                                                # 这个直接用 load_moudle 函数加载了 
done 
  
echo -n $" done"                                                                                                        # 输出 done 表示完成 
success                                                                                                                    # 调用 success 函数,记录该事件 
echo 
  
########################################################################################################################################################  
# 注释 :下面是启用 Software-RAID 的检测 
  
echo "raidautorun /dev/md0" | nash --quiet       # 用 nash (小型脚本解释器)执行 "raidautorun /dev/md0" 命令,对所有paritition ID 为 0xFD 分区进行检查 
########################################################################################################################################################   
  
# Start the graphical boot, if necessary; /usr may not be mounted yet, so we             # 显示图形模式的启动画面,但由于 rhgb 命令在 /usr 分区上, 
# may have to do this again after mounting                                                             #  这时 /usr 分区还没有被挂载,所以在挂载后重新做一遍这部分脚本 
RHGB_STARTED=0           # 初始化 RHGB_STARTED 变量的值为 0,表示尚未启动。RHGB 是 redhat graphical boot 的含义, 
mount -n /dev/pts         # 挂载 /dev/pts ,类型是 devpts ,目的是获得 pty ,这是一种伪文件系统 
  
if strstr "$cmdline" rhgb && [ "$BOOTUP" = "color" -a "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb ]; then        # 如果内核启动参数 $cmdline 含有 rhgb ,且 
                                                                                                                                                        # BOOTUP 变量的值为 color ,且 GRPAHICAL 变量的值为 yes 
                                                                                                                                                        # 且 /usr/bin/rhgb 文件存在并可执行 
   LC_MESSAGES= /usr/bin/rhgb                                                                                                           # 把 "/usr/sbin/rhgb" 赋予变量 LC_MESSAGES 
   RHGB_STARTED=1                                                                                                                            # RHGB_STARTED 变量的值为1。 
fi 
  
########################################################################################################################################################   
# 注释 :下面部分是使用 sysctl 设置内核的参数 
  
# Configure kernel parameters                     
update_boot_stage RCkernelparam             # 调用 update_boot_stage 函数,该函数由 /etc/rc.d/init.d/functions 脚本定义,主要执行 "/usr/sbin/rhgb-client --update $1" 
action $"Configuring kernel parameters: " sysctl -e -p /etc/sysctl.conf    # 调用 action 函数,执行 sysctl 命令,它读取 /etc/sysctl.conf ,同时输出 “Configuring kernel  
                                                                                                       # parameters” 字符串,action 函数也是由 /etc/rc.d/init.d/functions 脚本定义 
  
# 补充 :action 函数的作用是把第一个参数 $1 赋予变量 string 并输出到屏幕,同时把该字符串也写入 /etc/rhgb/temp/rhgb-console 
  
# 然后把 $1 后面的部分作为命令交给 initlog 执行,并根据结果调用 success 或者 failure 函数。而 success 或者 failure 函数又会根据结果输出 [ OK ] 或者 [ FAILED ] 。 
  
# 除了 success 和 failure 函数外,functions 脚本还定义了 passed 和 warnning 函数,它们又分别调用 echo_passwd() 和 echo_warnning()函数, 
  
# 以输出 [ PASSED ] 和 [ WARNNING ]  
  
########################################################################################################################################################   
# 注释 :下面设置系统的硬件时钟 
# Set the system clock.                    # 接下来设置系统时钟 
update_boot_stage RCclock                # 同样是告诉 update_boot_stage 该事件(执行 /usr/bin/rhgb-client --update="$1" 命令,这里 $1 就是 "RCclock") 
ARC=0                                                 
SRM=0 
UTC=0 
  
if [ -f /etc/sysconfig/clock ]; then        # 如果存在 /etc/sysconfig/clock 则执行该文件 
   . /etc/sysconfig/clock 
  
   # convert old style clock config to new values 
   if [ "${CLOCKMODE}" = "GMT" ]; then                        # 如果变量 CLOCKMODE 为旧式的 GMT 格式 
      UTC=true                                                                # 则 UTC 变量的值为 ture  
   elif [ "${CLOCKMODE}" = "ARC" ]; then                     # 如果 CLOCKMODE 的值是 ARC ,则 
      ARC=true                                                                # ARC 的值为 true 
   fi                                                                         # 如果 CLOCKMODE 不等于 GMT 或者 UTC ,则 UTC 的值等于 /etc/sysconfig/clock 中的值,一般都是 false 
fi                                                                            # 如果 CLOCKMODE 不等于 GMT 后者 ARC ,则 UTC 的值为 0 
  
CLOCKDEF=""                                                            # CLOCKDEF 变量的值默认为空 
CLOCKFLAGS="$CLOCKFLAGS --hctosys"                       # 而 CLOCKFLAGS 变量的值为原来的值(可能为空)加上 '--hctosys' ,表示把硬件时钟写入内核时钟 
  
case "$UTC" in                                                          # 根据 UTC 变量的值进行选择                                              
    yes|true) CLOCKFLAGS="$CLOCKFLAGS --utc"             # 如果 UTC 的值为 yes 或者 true,则变量 CLOCKFLAGS 的值加上 --utc ,表示采用 UTC 时区 
  CLOCKDEF="$CLOCKDEF (utc)" ;;                                     # 同时变量 CLOCKDEF 的值加上 "(utc)" 
    no|false) CLOCKFLAGS="$CLOCKFLAGS --localtime"     # 如果变量 UTC 的值为 false 或者 no,则 
  CLOCKDEF="$CLOCKDEF (localtime)" ;;                        # CLOCKDEF 的值加上 "(localtime)" 。由于安装 OS时没有选择 UTC,CLOCKDEF 的值一般最后就是 (localtime) 而已 
esac 
case "$ARC" in                                                          # 根据 ARC 变量的值来选择               
    yes|true) CLOCKFLAGS="$CLOCKFLAGS --arc"            # 如果是 yes 或者 true ,则 CLOCKFLAGS 的值加上 --arc ,CLOCKDEF 的值加上 '(arc)' 
  CLOCKDEF="$CLOCKDEF (arc)" ;;                                # 如果 ARC 的值为 0 ,则 CLOCKFLAGS 和 CLOCKDEF 的值都不变 
esac 
case "$SRM" in                                                         # 如果 SRM 的值是 yes 或者 true ,则 CLOCKFLAGS 的值加上 "--srm" ,CLOCKDEF 加上 "(srm)" 
    yes|true) CLOCKFLAGS="$CLOCKFLAGS --srm" 
  CLOCKDEF="$CLOCKDEF (srm)" ;; 
esac 
  
/sbin/hwclock $CLOCKFLAGS                    # 执行 hwclock $CLOCKFLAGS 命令,一般就是 /sbin/hwclock --hctosys  
  
action $"Setting clock $CLOCKDEF: `date`" date        # 并输出 "setting clock " 和 $CLOCKDEF ,并执行 date 输出当前时间,然后输出 [ OK ] 
########################################################################################################################################################  
# 注释 :下面部分是设置 key map   
if [ "$CONSOLETYPE" = "vt" -a -x /bin/loadkeys ]; then            # 接下来是设置 keymap ,一般都是 us  
 KEYTABLE=                                                                        #  设置 KEYTABLE 和 KEYMAP  
 KEYMAP= 
 if [ -f /etc/sysconfig/console/default.kmap ]; then                # 假如存在 /etc/sysconfig/console/default.kmap ,则 
  KEYMAP=/etc/sysconfig/console/default.kmap                            # KEYMAP 变量的值为 /etc/sysconfig/console/default.kmap 
 else                                                                                    # 否则 
  if [ -f /etc/sysconfig/keyboard ]; then                                        # 如果存在 /etc/sysconfig/keyboard 文件,则执行它 
    . /etc/sysconfig/keyboard                                                       # 该文件设置 KEYTABLE 变量的值,例如 KEYTABLE="/usr/lib/kbd/keytables/us.map 
  fi 
  if [ -n "$KEYTABLE" -a -d "/lib/kbd/keymaps" ]; then              # 如果 KEYTABLE 的值不为空,且存在 /lib/kbd/keymaps/ 目录,则 
     KEYMAP="$KEYTABLE.map"                                                # KEYMAP 的值为 KEYTABLE 的值加上 ".map"  
  fi 
 fi 
 if [ -n "$KEYMAP" ]; then                                                                 # 假如 KEYMAP 变量的值不为空(间接表示 KEYTABLE 不为空且存在 keymaps 目录) 
  # Since this takes in/output from stdin/out, we can't use initlog 
  if [ -n "$KEYTABLE" ]; then                                                                    # 且 KEYTABLE 的值不为空 
    echo -n $"Loading default keymap ($KEYTABLE): "                                     # 则输出 "Loading default keymap xxxx" 
  else 
    echo -n $"Loading default keymap: "                                                    # 否则只输出 Loading default keymap 
  fi     
  loadkeys $KEYMAP < /dev/tty0 > /dev/tty0 2>/dev/null &&             # 接下来用 loadkeys 加载 $KEYMAP 指定的键盘映射文件。 
     success $"Loading default keymap" || failure $"Loading default keymap"        # 如果成功则调用 success 函数,否则调用 failure 函数 
  echo 
 fi 
fi 
  
######################################################################################################################################################## 
# 注释 :下面部分是设置主机名  
  
# Set the hostname.                                # 接下来是设置主机名 
update_boot_stage RChostname                # 告诉 update_boot_stage 函数该事件,以执行 /usr/sbin/rhgb-client --update RChostname 命令 
action $"Setting hostname ${HOSTNAME}: " hostname ${HOSTNAME}        # 打印 "setting hostname xxxx" 字符串,并执行 hostname ${HOSTNAME}设置主机名 
                                                                 #  注意,HOSTNAME 变量前面已经有过赋值的了,就是 `/bin/hostname` 命令返回的值 
  
######################################################################################################################################################## 
# 注释 :下面设置 ACPI 部分 
# Initialiaze ACPI bits 
if [ -d /proc/acpi ]; then                                                                    # 如果存在 /proc/acpi 目录,则 
   for module in /lib/modules/$unamer/kernel/drivers/acpi/* ; do            # 对于在 /lib/modules//kernel/drivers/acpi/ 目录下的所有模块文件, 
      insmod $module >/dev/null 2>&1                                                        # 都用 insmod 命令加载到内核中 
   done 
fi 
  
  
######################################################################################################################################################## 
# 注释 :下面是主要的部分,就是确认是否需要对 / 文件系统进行 fsck   
if [ -f /fastboot ] || strstr "$cmdline" fastboot ; then      # 接下来是看对 / 系统进行 fsck 的时候了。如果不存在 /fastboot 文件则看 cmdline 变量是否含有 fastboot 字符串 
 fastboot=yes                                                              # 如果有,则 fastboot 变量的值为 yes。/fastboot 是由 shutdown -f 创建的,表示重启时不作 fsck  
fi 
  
if [ -f /fsckoptions ]; then                                            # 如果存在 /fsckoptions 文件,则把文件的内容赋予变量 fsckoptions 变量 
 fsckoptions=`cat /fsckoptions` 
fi 
  
if [ -f /forcefsck ] || strstr "$cmdline" forcefsck ; then        # 如果不存在 /forcefsck 文件,则检查 cmdline 变量是否含有 forcefsck 字符串,如果有 
 fsckoptions="-f $fsckoptions"                                            # 则在 fsckoptions 的值前面加上 "-f" 。/forcefsck 是由 shutdown -F 创建的,强制 fsck  
elif [ -f /.autofsck ]; then                                                 # 如果存在 /.autofsck 
      if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then        #  且文件 /usr/bin/rhgb-clinet 可执行,且 /usr/bin/rhgb 服务在运行(--pnig),则 
           chvt 1                                                                                                #  切换到虚拟控制台 #1 (主要是显示 fsck 的信息用) 
       fi 
       echo $"Your system appears to have shut down uncleanly"                            # 并显示需要 fsck 的信息 “You system appears to have shut down uncleanly” 
       AUTOFSCK_TIMEOUT=5                                                                              # 设置超时时间为5秒 
      [ -f /etc/sysconfig/autofsck ] && . /etc/sysconfig/autofsck                           # 如果存在 /etc/sysconfig/autofsck 则执行它 
      if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                 # 如果执行该文件后 AUTOFSCK_DEF_CHECK 变量的值为 yes ,则 
          AUTOFSCK_OPT=-f                                                                                        # 变量 AUTOFSCK_OPT 的值为 "-f" 
      fi 
  
      if [ "$PROMPT" != "no" ]; then                                                                // 如果 PROMPT 的值不为 no ,则 
        if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                // 且 AUTOFSCK_DEF_CHECK 的值为 yes  
           if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press N within %d seconds to not force file system integrity check..." n ; then    //  执行 getkey 命令,超时5秒,并 
                                                                                                                                                                                       // 提示5秒内按下 n 键可跳过 fsck 
              AUTOFSCK_OPT=        // 如果用户按下 n ,则 AUTOFSCK_OPT 的值为空,表示取消自动 fsck 
           fi 
        else                    // 如果 AUTOFSCK_DEF_CHECK 的值不为 yes ,则提示5秒内按y 可以强制 fsck 
          if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press Y within %d seconds to force file system integrity check..." y ; then        // 同样是用 getkey 捕捉 y 键 
              AUTOFSCK_OPT=-f        // 如果用户按下了 y 键,则 AUTOFSCK_OPT 的值为 "-f"  
          fi 
       fi 
       echo 
      else         // 如果 PROMPT 的值为 "no", 这是用户无法选择是否 fsck  
        # PROMPT not allowed 
        if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                # 取决于 AUTOFSCK_DEF_CHECK 变量的值,如果是 yes ,则 
          echo $"Forcing file system integrity check due to default setting"     # 提示由于默认的设置强制 fsck 。可能是 mount 次数达到限制,或者多长时间没有 fsck 了 
        else                                             
          echo $"Not forcing file system integrity check due to default setting"            # 如果 AUTOFSCK_DEF_CHECK 的值为 no ,则表示不做 fsck  
        fi 
      fi 
      fsckoptions="$AUTOFSCK_OPT $fsckoptions" 
fi 
  
# 注释 :注意!到这里为止并没有执行 fsck 操作,只是判断是否需要执行 fsck 以及 fsck 命令的选项 
  
if [ "$BOOTUP" = "color" ]; then       # 如果 BOOTUP 的值为 color ,则 
 fsckoptions="-C $fsckoptions"            # -C 表示显示 fsck 进度信息 
else                                              # 否则 
 fsckoptions="-V $fsckoptions"            # -V 表示显示 verbose 信息 
fi 
  
if [ -f /etc/sysconfig/readonly-root ]; then            # 如果存在 /etc/sysconfig/readonly-root ,则 
    . /etc/sysconfig/readonly-root                            # 执行该文件 
      
    if [ "$READONLY" = "yes" ]; then                            # 如果 READONLY 变量的值为 yes ,则 
        # Call rc.readonly to set up magic stuff needed for readonly root        # 执行 /etc/rc.readonly 脚本 
        . /etc/rc.readonly 
    fi 
fi 
 
######################################################################################################################################################### 
# 注释 :下面开始判断 / 文件系统的类型(是本地还是 nfs 格式)。如果是nfs则不执行 fsck ,否则继续 
 
_RUN_QUOTACHECK=0                                            # 初始化 RUN_QUOTACHECK 的值为0.这里是作一个标记,因为后面的代码可能会导致重启。在这里做好标记 
                                                                            # 如果后面没有重启,就可以把 _RUN_QUOTACHECK 设置为1 
ROOTFSTYPE=`awk '/ / / && ($3 !~ /rootfs/) { print $3 }' /proc/mounts`        # 从 /proc/mounts 中找出 / 文件系统的类型,并赋予变量 ROOTFSTYPE  
 
# 注释 :下面用到 -z "$fastboot" ,如果有 /fastboot ,则 fastboot 的值为 yes, 所以就不合 if 的条件,则跳过 fsck 。这就是 shutdown -f 快速启动的效果 
  
if [ -z "$fastboot" -a "$READONLY" != "yes" -a "X$ROOTFSTYPE" != "Xnfs" -a "X$ROOTFSTYPE" != "Xnfs4" ]; then     # 如果fastboot 变量的值为空且  READONLY 变量不为 yes  
                                                                                                                                                                # 且 / 文件系统的类型不是 nfs 或者 nfs4 ,则  
        STRING=$"Checking root filesystem"    # 初始化 string 变量并输出 "checking root filesystem"  
        echo $STRING 
        rootdev=`awk '/ / / && ($3 !~ /rootfs/) {print $1}' /proc/mounts`        # 从 /proc/mounts 中找出 / 文件系统所在的设备,并赋予 rootdev 变量(例如 /dev/hdb2) 
         if [ -b /initrd/"$rootdev" ] ; then        # 如果 /initrd/$rootdev 是一个 block 设备 
              rootdev=/initrd/"$rootdev"                    # 则 rootdev 就是 /initrd/rootdev  
         else                                                 # 否则 rootdev 就是 / ,正常情况应该是执行该步的,也就是 rootdev 最终为 / 
         rootdev=/                                       # rootdev 的值为 / ,因为 fsck 支持用 LABEL、mount point、device name 来指定要检查的文件系统  
         fi 
     
        # 注释 : 下面开始真正的 fsck 操作 
 
         if [ "${RHGB_STARTED}" != "0" -a -w /etc/rhgb/temp/rhgb-console ]; then        # 如果 RHGB_STARTED 不为0且 /etc/rhgb/tmp/rhgb-console 文件可写,则 
              fsck -T -a $rootdev $fsckoptions > /etc/rhgb/temp/rhgb-console           # 执行 fsck 命令,-T 表示不打印标题栏,-a 表示自动修复错误,设备是 / ,后面是选项 
                                                                                                                      # 且信息写入 /etc/rhgb/temp/rhgb-console 
         else                                                                                                           # 否则 
              initlog -c "fsck -T -a $rootdev $fsckoptions"                                                        # 调用 initlog 执行 fsck ,信息直接输出到屏幕,fsck 命令本身都是一样的。 
        fi                                                                                                                      
        rc=$?                                    # 把 fsck 的结果送给变量 rc 
        if [ "$rc" -eq "0" ]; then         # 如果 rc的值为0,表示 fsck 成功通过 
              success "$STRING"                    # 则调用 success 打印 "checking root fileysyes      [OK]" 
          echo 
        elif [ "$rc" -eq "1" ]; then        # 如果 rc 为1 ,则打印 passed ,表示有错误但修复 
             passed "$STRING"                # 这时调用的是 passed 函数(由 /etc/rc.d/init.d/functions 脚本定义,输出 "checking root filesystems     [ PASSED ]") 
             echo 
         elif [ "$rc" -eq "2" -o "$rc" -eq "3" ]; then         # 如果 rc 为 2 (表示系统应该重启)或者为 3(为 1+2 ,表示系统错误已修复,但需要重启) 
              echo $"Unmounting file systems"                        # 提示卸载 / 文件系统 
              umount -a                                                        # 把所有卸载,当然 / 除外 
              mount -n -o remount,ro /                                  # 把 / 设备已 ro 模式重新 mount 
              echo $"Automatic reboot in progress."                # 然后提示将要自动重启了 
              reboot -f                                                         # 在这里执行 reboot -f ,强制重启 
          fi     # 这个 fi 是结束 "[ "$rc" -eq "0" ]; " 的 
  
          # A return of 4 or higher means there were serious problems.    # 如果 fsck 返回的值大于4(不含4,4表示错误未修复),则表示 / 文件系统出现重大错误,无法继续 
         if [ $rc -gt 1 ]; then    # 这里之所以用 -gt 1 ,是因为如果前面返回2或者3就会自动重启了,如果执行到这里说明 rc 的值必须至少大于等于4 
             if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then    # 如果 rhgb-client 可执行且 rhgb 服务在运行 
                  chvt 1                 # 强制切换到 1# 虚拟控制台 
             fi 
  
            failure "$STRING"        # 调用 failure 函数,打印 "checking root filesystem     [ FAILURE ]" 
            echo 
            echo 
            echo $"*** An error occurred during the file system check."        # 提示 fsck 过程出错 
            echo $"*** Dropping you to a shell; the system will reboot"         # 提示将进入紧急模式的 shell  
            echo $"*** when you leave the shell."                                       # 当你退出该 shell 时会自动重启 
  
            str=$"(Repair filesystem)"        # 设置紧急模式下的 shell 提示符 
            PS1="$str # # "; export PS1     #  设置提示符变量 PS1 为 "(Repair filesystem) # " , 表示当前是第几个命令 
            [ "$SELINUX" = "1" ] && disable_selinux 
            sulogin                                    # 自动执行 sulogin ,这不是 su ,而是 single-user login 的意思,自动进入单用户模式的 shell 
         
                              # 注意这里将进入一个交互式的 shell 。只有你执行 exit 命令,才会执行下面的代码,否则下面的代码根本不会被执行 
  
            echo $"Unmounting file systems"    # 提示卸载文件系统 
            umount -a                                    # 卸载所有文件系统,/ 除外 
            mount -n -o remount,ro /              # / 以 ro 模式挂载 
            echo $"Automatic reboot in progress."        # 提示将自动重启 
            reboot -f                                     # 立即重启 
 
         elif [ "$rc" -eq "1" ]; then                     # 如果 rc 的值等于1 ,则表示错误修复完毕 
            _RUN_QUOTACHECK=1                    # 如果上面的 fsck 没有出错,自然就不会重启,所以这里把 _RUN_QUOTACHECK 的值设置为1,表示可以进行 quotacheck 了 
 
         fi   # 这个 fi 是结束 " [ $rc -gt 1 ]; " 的                             
 
         if [ -f /.autofsck -a -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then    #  如果 rhgb-clinet 可运行且 rhgb 服务在运行 
          chvt 8                                                                                                                    # 则切换换 8# 控制台,也就是图形 
         fi 
fi        # 这个 fi 是结束前面的 ”[ -z "$fastboot" -a "$READONLY" != "yes" -a "X$ROOTFSTYPE" != "Xnfs" -a "X$ROOTFSTYPE" != "Xnfs4" ]; “ 的 
  
       # 所以如果存在 /fastboot 文件,就直接跳转到这里 
  
######################################################################################################################################################## 
# 注释 :在 initrd 的 /linuxrc 或者 /init 脚本中会把初始/文件系统卸载掉,如果没有卸载完全,则在这里会重新做一次 
  
# Unmount the initrd, if necessary                    # 接下来是卸载 initrd 初始/ 文件系统,一般 /init 都会在结尾 umount /initrd/dev 的  
if LC_ALL=C fgrep -q /initrd /proc/mounts && ! LC_ALL=C fgrep -q /initrd/loopfs /proc/mounts ; then    # 如果 /proc/mounts 文件还有 /initrd/proc 条目,  
                                                                                                                                                      # 且不存在 /initrd/loops 条目,则 
   if [ -e /initrd/dev/.devfsd ]; then                                                                                                     # 如果存在 /initrd/dev/.devfsd 文件,则        
      umount /initrd/dev                                                                                                                            # 卸载 /initrd/dev 目录 
   fi 
   umount /initrd                                            # 最后把整个 /initrd 卸载 
   /sbin/blockdev --flushbufs /dev/ram0 >/dev/null 2>&1    # 最后用 blockdev --flushbs 命令清除 /dev/ram0 的内容,该社备就是被挂载为初始/的 
fi 
########################################################################################################################################################                                                                                # 注释 :下面是对 / 进行 quota 检查,其他文件系统暂时不执行 quotacheck  
 
# Possibly update quotas if fsck was run on /.                    # 如果 fsck 已经对 / 进行了检查,则执行 quotacheck 更新 quota 情况 
LC_ALL=C grep -E '[[:space:]]+/[[:space:]]+' /etc/fstab |         # 在 /etc/fstab 中找出 / 文件系统所在的行,并打印第4个字段(属性)
    awk '{ print $4 }' |                                                            # 这是属性字段 
    LC_ALL=C fgrep -q quota                                                    # 找出是否有 quota 字符串,不管是 usrquota 还是 grpquota  
_ROOT_HAS_QUOTA=$?                                                    # 把结果赋予 _ROOT_HAS_QUOTA 变量 
if [ "X$_RUN_QUOTACHECK" = "X1" -a                               #  如果 x$_RUN_QUOTACHECK 返回 X1 ,且 
    "X$_ROOT_HAS_QUOTA" = "X0" -a                                 #   X$_ROOT_HAS_QUOTA 返回 X0 ,则表示当前可以执行 quotacheck ,且 / 也启用了 quota  
    -x /sbin/quotacheck ]; then                                        #  如果存在 quotacheck 命令    
 if [ -x /sbin/convertquota ]; then                                    # 如果存在 convertquota 命令 
     if [ -f /quota.user ]; then                                                # 且存在 /quota.user 则 
          action $"Converting old user quota files: "                           # 并调用 action 函数,打印提示信息,用 convertquota 转换成 v2 格式的 aquota.user 文件 
          /sbin/convertquota -u / && rm -f /quota.user                # 然后删除 v1格式的 /quota.user 
     fi 
     if [ -f /quota.group ]; then                                            # 同样对 /quota.group 转换成 v2 格式的 aquota.group,并删除 /quota.group 文件 
          action $"Converting old group quota files: "  
          /sbin/convertquota -g / && rm -f /quota.group            # 同样在转换后删除旧的 v1 的 group quota 文件 
     fi 
 fi 
 action $"Checking root filesystem quotas: " /sbin/quotacheck -nug /        # 然后执行 quotacheck -nug / 对 / 文件系统进行检查,并打印提示信息 
fi    # 这个 fi 是结束 "[ "X$_RUN_QUOTACHECK" = "X1" -a "X$_ROOT_HAS_QUOTA" = "X0" -a -x /sbin/quotacheck ]; " 的 
  
######################################################################################################################################################## 
# 注释 :下面这个部分是设置 ISA 设备的,不过由于现在基本没有 ISA 设备了,所以可以跳过该部分,甚至可以在内核中指定不支持 ISA  
if [ -x /sbin/isapnp -a -f /etc/isapnp.conf -a ! -f /proc/isapnp ]; then         
    # check for arguments passed from kernel 
    if ! strstr "$cmdline" nopnp ; then 
 PNP=yes 
    fi 
    if [ -n "$PNP" ]; then 
 action $"Setting up ISA PNP devices: " /sbin/isapnp /etc/isapnp.conf 
    else 
 action $"Skipping ISA PNP configuration at users request: " /bin/true 
    fi 
fi 
########################################################################################################################################################  
# Remount the root filesystem read-write.                                    # 现在 / 文件系统 fsck 过了,可以按照 read-write 的模式挂载了 
update_boot_stage RCmountfs                                                      # 告诉 rhgb 服务器更新 RCmountfs 服务的状态 
state=`awk '/ / / && ($3 !~ /rootfs/) { print $4 }' /proc/mounts`    # 从 /proc/mounts 中找出 ' / ' 字符串并且第3个字段不等于 'rootfs',则打印其第4个字段,赋予 state 
[ "$state" != "rw" -a "$READONLY" != "yes" ] &&                               # 如果 state 变量的值不为 'rw' 且 READONLY 变量的值为 yes ,则   
  action $"Remounting root filesystem in read-write mode: " mount -n -o remount,rw /        # 用 -o remount -o rw 重新挂载 / 为 rw 模式,并打印提示信息 
########################################################################################################################################################  
# 注释 :下面开始是 LVM2 的部分了 
# LVM2 initialization                                                                                        # 下面部分是 LVM2  的初始化部分 
if [ -x /sbin/lvm.static ]; then                                                                          # 如果存在 /sbin/lvm.static 且可执行 
    if ! LC_ALL=C fgrep -q "device-mapper" /proc/devices 2>/dev/null ; then        # 如果 /proc/devices 文件不含有 device-mapper ,则
         modprobe dm-mod >/dev/null 2>&1                                                                        # 调用 modprobe 加载 dm-mod  
    fi 
    echo "mkdmnod" | /sbin/nash --quiet >/dev/null 2>&1                                    # 并调用 nash 执行 mkdmmod ,不过是以 quiet 模式执行的 
    [ -n "$SELINUX" ] && restorecon /dev/mapper/control >/dev/null 2>&1 
    if [ -c /dev/mapper/control -a -x /sbin/lvm.static ]; then                                # 假如存在 /dev/mapper/control 这个字符设备且 /sbin/lvm.static 可执行,则 
         if /sbin/lvm.static vgscan --mknodes --ignorelockingfailure > /dev/null 2>&1 ; then        # 调用 lvm.static 执行 vgscan 扫描所有卷组(忽略锁错误) 
             action $"Setting up Logical Volume Management:" /sbin/lvm.static vgchange -a y --ignorelockingfailure        # 如果扫描到卷组,则调用 action 函数, 
         fi                                                                                                                                                      # 打印提示信息,并执行 vgchange -ay 激活全部卷组 
    fi 
fi 
 
# LVM initialization                 # 这是 LVM1 的部分,因为系统可能同时存在 lVM1 和 lvm2 的 pv 
if [ -f /etc/lvmtab ]; then        # 如果存在 /etc/lvmtab 文件 
    [ -e /proc/lvm ] || modprobe lvm-mod > /dev/null 2>&1    # 且 /proc/lvm 文件存在,如果没有则调用 modprobe lvm-mod 加载 lvm-mod ,注意模块名的不同 
        if [ -e /proc/lvm -a -x /sbin/vgchange ]; then                # 如果现在存在 /proc/lvm 文件且 /sbin/vgchange 可执行 
             action $"Setting up Logical Volume Management:" /sbin/vgscan && /sbin/vgchange -a y    # 则调用 action 打印提示信息,并执行 vgchange -ay 激活它们 
        fi 
fi 
  
########################################################################################################################################################  
# Clean up SELinux labels 
if [ -n "$SELINUX" ]; then 
   for file in /etc/mtab /etc/ld.so.cache ; do 
    [ -r $file ] && restorecon $file  >/dev/null 2>&1 
   done 
fi 
########################################################################################################################################################  
# Clear mtab                                                            # 由于之前的 /etc/mtab 并不准确,所以现在把它的内容清掉 
(> /etc/mtab) &> /dev/null                                              # 用 >/etc/mtab 清空其内容   
  
# Remove stale backups 
rm -f /etc/mtab~ /etc/mtab~~                                    # 删除 /etc/mtab 的备份文件 
  
# Enter root, /proc and (potentially) /proc/bus/usb and devfs into mtab.        # 把 root ,/proc ,/proc/bus/usb,devpts 加入到 /etc/mtab 中 
mount -f /                                                                    #  -f 表示只更新 /etc/mtab ,但不实际mount,因为这些文件系统在之前都挂载了,没有必要再挂载一次 
mount -f /proc 
mount -f /sys >/dev/null 2>&1 
mount -f /dev/pts 
[ -f /proc/bus/usb/devices ] && mount -f -t usbfs usbfs /proc/bus/usb  # 如果存在 /proc/bus/usb/devices 文件,则用 mount -f -t usbfs 挂载到 /proc/bus/usb 下       
[ -e /dev/.devfsd ] && mount -f -t devfs devfs /dev         # 如果存在 /dev/.devfsd ,则同样挂载 devfs 到 /dev 下 
########################################################################################################################################################  
# configure all zfcp (scsi over fibrechannel) devices before trying to mount them 
# zfcpconf.sh exists only on mainframe 
[ -x /sbin/zfcpconf.sh ] && /sbin/zfcpconf.sh 
########################################################################################################################################################  
# The root filesystem is now read-write, so we can now log        # 由于之前的 / 是只读的,所以通过 initlog 来记录信息,现在可以通过 syslog 了 
# via syslog() directly.. 
if [ -n "$IN_INITLOG" ]; then 
    IN_INITLOG=                                                                            # 如果 IN_INITLOG 的值不为空则清空它 
fi 
  
if ! strstr "$cmdline" nomodules && [ -f /proc/modules ] ; then        # 如果内核启动参数含有 "nomodules" ,且存在 /proc/modules 文件,则 
    USEMODULES=y                                                                            # 把 USEMODULES 设置为 y 
fi 
  
# Load modules (for backward compatibility with VARs)                 
if [ -f /etc/rc.modules ]; then 
 /etc/rc.modules 
fi 

7./etc/rc.d/rc

/etc/rc.d/rc是系统运行级别切换时所执行的文件,其文件分析如下:

#! /bin/bash 
#此文件用于对系统的运行级别改变后,对于启动及停止服务进行响应。 
# rc            This file is responsible for starting/stopping 
#               services when the runlevel changes. 

# Original Author: 
#               Miquel van Smoorenburg, ; 

# 下列的角本用于检测正确的运行级别 
# check a file to be a correct runlevel script 
check_runlevel () 

# Check if the file exists at all.(语句含义为如果前边的命令执行未成功,则返回1)检测文件是否存在 
[ -x "$1" ] || return 1   #如果第一个参数不是可执行文件,那么返回1 
# 拒绝备份和由rpm所产生的文件Reject backup files and files generated by rpm. 
case "$1" in 
*.rpmsave|*.rpmorig|*.rpmnew|*~|*.orig)  #如果第1个参数中包含下列的扩展名,则返回1,否则返回0 
return 1 
;; 
esac 
return 0 

# Now find out what the current and what the previous runlevel are. 
#现在查找当前和以前的运行级别。 
argv1="$1"   #为argv1赋值,等于第一个参数 
set `/sbin/runlevel`   
runlevel=$2  #目前运行级为第二个参数 
previous=$1  #以前的运行级别为第1个参数 
export runlevel previous   #导出这两个变量为全局的变量 
. /etc/init.d/functions   #执行文件functions 
# See if we want to be in user confirmation mode  
#是否我们是在用户确认的模式下。即是否进入用户干预的模式 
if [ "$previous" = "N" ]; then 
if [ -f /var/run/confirm ]      #confirm文件存在 
|| grep -i confirm /proc/cmdline >;/dev/null ; then  
#在/proc/cmdline中以不区分大小写的模式下查找符合confirm格式的内容 
#if条件语句含义为:如果/var/run/confirm文件存在,在/proc/cmdline中有与confirm匹配的内容 
rm -f /var/run/confirm   #删除文件 
CONFIRM=yes #为变量赋值 
export CONFIRM  #输出变量 
echo $"Entering interactive startup"   #在输出终端显示“进入用户交互模式” 
else 
echo $"Entering non-interactive startup"  #在输出终端显示“非用户交互模式” 
fi 
fi 
# Get first argument. Set new runlevel to this argument. 
#取第1个参数,为新的运行级别赋值 
[ -n "$argv1" ] && runlevel="$argv1"   #(-n参数含义为参数长度是否为0)如果第一个参数的长度不为0,建造设置runlevel等于参数1。 
# Is there an rc directory for this new runlevel? 
#是否为新的运行级别设置了运行用的rc目录 
[ -d /etc/rc$runlevel.d ] || exit 0  #(-d含义为目录查找)如果在etc目录下没有此运行级别的目录,由退出0。 
# First, run the KILL scripts. 
#首先,运行kill角本,杀掉原运行级的服务 
for i in /etc/rc$runlevel.d/K* ; do  #此for循环的意义为:i是否是运行级目录下的K文件 
check_runlevel "$i" || continue   
#调用运行级检测函数,如果返回0,则继续查找(||含义为如果前一个命令不成功,则执行后者。 
# Check if the subsystem is already up. 
# 检测子系统是否已经加载 
subsys=${i#/etc/rc$runlevel.d/K??} 
#这句话的含义去掉变量i中所有的/etc/rc$runlevel.d/K??的部分,并将结果赋给变量subsys 
#例:i=/etc/rc$runlevel.d/K05saslauthd,那么去掉/etc/rc$runlevel.d/K??部分后,subsys为saslauthd 
[ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ]  
|| continue 
#-o选项好象是一个or选项,在另一个相同的文件中,此选项为||,上句的意义为$subsys或$subsys.init文件是否存在 
#如果这两个文件都不存在,执行continue 
# Bring the subsystem down. 
#将子系统(也可以说是子服务)关闭 
if egrep -q "(killproc |action )" $i ; then    
#-q参数含义为不在输出设备上显示,不管找到未找到匹配东西, 
#即使有错也是如此,这句话的意思是利用正则表达式egrep,在文件$i中查找包含killproc或action 的句子 
#如果找到,则停止此服务 
$i stop 
#执行文件名为$i的服务停止 
else 
action $"Stopping $subsys: " $i stop 
fi 
done 
# Now run the START scripts. 
#启动开发角本 
for i in /etc/rc$runlevel.d/S* ; do 
#查找在/etc/rc$runlevel.d/S*中文件名为i的文件,并循环 
check_runlevel "$i" || continue 
#检测其运行级 
# Check if the subsystem is already up. 
#检测其子系统是否已经运行 
subsys=${i#/etc/rc$runlevel.d/S??} 
#这名句的解释同其KILL中的语句。 
[ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ]  
&& continue 
#同上      
# If we're in confirmation mode, get user confirmation 
#如果我们是在用户确认的启动格式(即interactive交互模式) 
if [ -n "$CONFIRM" ]; then 
#前边已经将confirm文件删除,这里是什么意思? 
confirm $subsys 
case $? in 
0) :;; 
2) CONFIRM=;; 
*) continue;; 
esac  
fi 
#启动子服务(系统) 
# Bring the subsystem up. 
if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then 
#如果变量subsys等于halt或reboot,实际上这也是两个运行级,一个是0,一个是6 
export LC_ALL=C 
exec $i start 
#文件$i启动 
fi 
if egrep -q "(daemon |action |success |failure )" $i 2>;/dev/null  
|| [ "$subsys" = "single" -o "$subsys" = "local" ]; then 
#条件语句含义: 
#如果在文件$i中找到daemon、action、success、failure或在没有找到前述几个关键字的情况下 
#但$subsye=single(单用户方式)或等于local则为真 
$i start 
else 
action $"Starting $subsys: " $i start 
fi 
done 
上述角本重点处说明: 
1、${变量名1#$变量名2} 
此外的含义为从变量名1的内容中将变量名2所代表的内容除去,但有一个限制,即必须从第1位开始,且当连续使用时要用下述方法进行转义赋值: 
变量3=` echo ${变量1#$变量名2} `,然后再引用变量3才能正确的进行操作 
2、 grep –q “($变量1)” $变量2 
此外在()处,不能有空格,否则查询将不会有结果 
3、[ -选项 条件(或语句)] 的语法中,在[]中一定要有空格 
仅针对RH9,不当处请指出。 :em19:

8./etc/rc.d/rcn.d

此目录为各相应运行级的服务的链接文件

9./etc/rc.d/rc.local

此文件为用户自定义的开机运行程序,用户可以将需要开机运行的命令添加到此文件中

10.mingetty

在rc.sysinit执行后,将返回init继续其它的动作,通常接下来会执行到/etc/rc.d/rc程序。 
 根据启动级别执行相应的脚本rcx.d: 
l0:0:wait:/etc/rc.d/rc 0 
  l1:1:wait:/etc/rc.d/rc 1 
  l2:2:wait:/etc/rc.d/rc 2 
  l3:3:wait:/etc/rc.d/rc 3 
  l4:4:wait:/etc/rc.d/rc 4

  rc执行完毕后,返回init。这时基本系统环境已经设置好了,各种守护进程也已经启动了。init接下来会打开6个终端,以便用户登录系统。通过按Alt+Fn(n对应1-6)可以在这6个终端中切换。在inittab中的以下6行就是定义了6个终端: 
  1:2345:respawn:/sbin/mingetty tty1 
  2:2345:respawn:/sbin/mingetty tty2 
  3:2345:respawn:/sbin/mingetty tty3 
  4:2345:respawn:/sbin/mingetty tty4 
  5:2345:respawn:/sbin/mingetty tty5 
  6:2345:respawn:/sbin/mingetty tty6 
   从上面可以看出在2、3、4、5的运行级别中都将以respawn方式运行mingetty程序(其实mingetty也可以调login程序来实现登陆界面,具体见下文),mingetty程序能打开终端、设置模式。同时它会 显示一个文本登录界面,这个界面就是我们经常看到的登录界面,在这个登录界面中会提示用户输入用户名,而用户输入的用户将作为参数传给login程序来验 证用户的身份。

下面来分析mingetty如何实现登陆界面附部分源码
    //autologin  自动登陆
    if (autologin) {
        do_prompt (0);//
        printf ("login: %s (automatic login)
", autologin);
        logname = autologin;
    } else//否则调用get_logname()获取用户名,get_logname会调do_prompt()提示输入Login:get_logname没有别处源码
        while ((logname = get_logname ()) == 0)
            /* do nothing */ ;
·
·//省略部分源码
·
execl (loginprog, loginprog, autologin? "-f" : "--", logname, NULL);/*此处替换login程序来实现验证用户名密码
注意:全局变量static char *loginprog = "/bin/login";
*/
static void do_prompt (int showlogin)
{
    FILE *fd;
    int c;

    if (nonewline == 0)
        putchar ('
');
    if (noissue == 0 && (fd = fopen ("/etc/issue", "r"))) {
        while ((c = getc (fd)) != EOF) {
            if (c == '\')
                output_special_char (getc (fd));
            else
                putchar (c);
        }
        fclose (fd);
    }
    if (loginpause) {
        puts ("[press ENTER to login]");
        getc (stdin);
    }
    if (nohostname == 0)
        printf ("%s ", hn);
    if (showlogin)//autologin==0,跳过
        printf ("login: ");
    fflush (stdout);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
**验证成功后会执行/etc/profile(如果存在)文件完成相关环境初始化**
例:终端输入 :mingetty将提示入户输入程序(以login为例),调用login程序,login验证通过后将执行配置文件/etc/profile(如果存在)

到此整个启动过程就结束了。 

#!/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 $" Welcome 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 $" Press '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"";;                                                                            # 输出 "ide=" 然后是变量 ide 的值 
  "SCSI") scsi="$scsi $mod"                                                                          # 如果 devtype 的值为 SCSI ,则把 mod 变量的值加入到 scsi 变量的值中 
     echo "scsi="$scsi"";;                                                                           # 输出 "scsi=" 再输出 $scsi   
  "NETWORK") network="$network $mod"                                                    # 下面的 NETWORK 和 AUDIO 也一样 
     echo "network="$network"";; 
  "AUDIO") audio="$audio $mod" 
     echo "audio="$audio"";; 
  *) other="$other $mod"                                                                            # 如果是属于 other 类型的则把模块名 $mod 加入到 other 变量的值列表中 
     echo "other="$other"";; 
 esac 
done` 
  
load_module () {                    # 定义一个函数名为 load_module 
 LC_ALL=C fgrep -xq "$1" /etc/hotplug/blacklist 2>/dev/null || modprobe $1 >/dev/null 2>&1        # 在 /etc/hotplug/blacklist 文件中查找是否有指定的模块,如果没有, 
}                                                                                                                                            # 再用 modprobe 加载指定的模块 
  
# IDE                                        # 下面开始加载所有 IDE 类型的设备的模块 
for module in $ide ; do                 # 从 ide 变量中每次取出1个值,赋予变量 module  
 load_module $module                 # 再用 load_module 函数加载它。如果该模块存在于 /etc/hotplug/blacklist ,则不会被加载(因为存在于黑名单中) 
done 
  
# SCSI                                    # SCSI 方面的比较特殊,除了加载 scsi 变量中的模块外,还会从 modprobe -c (显示所有配置)中找出 scsi 卡(hostadapter)的模块 
for module in `/sbin/modprobe -c | awk '/^alias[[:space:]]+scsi_hostadapter[[:space:]]/ { print $3 }'` $scsi; do   # 从 modprobe -c 中找出所有scsi卡的模块别名, 
 load_module $module             # 并和 scsi 变量一起,通过 for 循环一一加载 
done 
 
load_module floppy                # 然后加载 floppy 模块 
  
echo -n $" storage"                # 输出 "storage" ,表示存储方面的模块加载已经完成 
  
# Network                            # 接下来是加载网络设备模块 
 
pushd /etc/sysconfig/network-scripts >/dev/null 2>&1        # pushd 是一个 bash 的内建命令,把目录名放入目录堆栈的顶部,并进入指定目录 
interfaces=`ls ifcfg* | LC_ALL=C egrep -v '(ifcfg-lo|:|rpmsave|rpmorig|rpmnew)' |         # 找出 network-scripts 目录下所有 lscfg 开头的文件名,排除指定备份和loopback 
            LC_ALL=C egrep -v '(~|.bak)$' |                                                                     # 排除以 ~ 和 .bask 结尾的文件名 
            LC_ALL=C egrep 'ifcfg-[A-Za-z0-9._-]+$' |                                                        # 找出所有以 ifcfg- 开头,并以多个字母/数字结尾的文件名 
     sed 's/^ifcfg-//g' |                                                                                                  # 把前缀 ifcfg- 去掉,只留下后面的接口名 
     sed 's/[0-9]/ &/' | LC_ALL=C sort -k 1,1 -k 2n | sed 's/ //'`                                         # 在数字前面加上空格,是类型和编号分离,便于 sort 排序,然后再组合回去 
                                                                                                                                 # 目的是按顺序启动接口 
for i in $interfaces ; do                                                                                                # 对于在 $interfaces 变量中的每个接口 
 eval $(LC_ALL=C fgrep "DEVICE=" ifcfg-$i)                                                                     # 从对应的文件 ifcfg-$i 找出 DEVICE= 行 
 load_module $DEVICE                                                                                                 # 然后用 load_module 加载它,注意,DEVICE= 行可以是别名,例如 eth1.n7css 
done 
popd >/dev/null 2>&1                                                                                                 # 把 network-scripts 从目录名堆栈中弹出,并返回原来的目录 
                                                                                 
for module in $network ; do                                                                                        #  剩下还有 network 变量中的模块 
 load_module $module                                                                                                # 也一一加载 
done 
  
echo -n $" network"                                                                                                    # 输出 network 字符串,表示加载网络设备模块工作完毕 
  
# Sound 
for module in `/sbin/modprobe -c | awk '/^alias[[:space:]]+snd-card-[[:digit:]]+[[:space:]]/ { print $3 }'` $audio; do    # 和 scsi 一样,加载在 modprobe -c 和 audio 变量 
 load_module $module                                                                                                                                          # 中的模块 
done 
  
echo -n $" audio"                                                                                                        # 输出 audio ,表示声卡设备驱动加载完毕,一般会有多个驱动被加载         
  
# Everything else (duck and cover)                                                                             # 剩下的就是 other 类型的设备了 
for module in $other ; do 
 load_module $module                                                                                                # 这个直接用 load_moudle 函数加载了 
done 
  
echo -n $" done"                                                                                                        # 输出 done 表示完成 
success                                                                                                                    # 调用 success 函数,记录该事件 
echo 
  
########################################################################################################################################################  
# 注释 :下面是启用 Software-RAID 的检测 
  
echo "raidautorun /dev/md0" | nash --quiet       # 用 nash (小型脚本解释器)执行 "raidautorun /dev/md0" 命令,对所有paritition ID 为 0xFD 分区进行检查 
########################################################################################################################################################   
  
# Start the graphical boot, if necessary; /usr may not be mounted yet, so we             # 显示图形模式的启动画面,但由于 rhgb 命令在 /usr 分区上, 
# may have to do this again after mounting                                                             #  这时 /usr 分区还没有被挂载,所以在挂载后重新做一遍这部分脚本 
RHGB_STARTED=0           # 初始化 RHGB_STARTED 变量的值为 0,表示尚未启动。RHGB 是 redhat graphical boot 的含义, 
mount -n /dev/pts         # 挂载 /dev/pts ,类型是 devpts ,目的是获得 pty ,这是一种伪文件系统 
  
if strstr "$cmdline" rhgb && [ "$BOOTUP" = "color" -a "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb ]; then        # 如果内核启动参数 $cmdline 含有 rhgb ,且 
                                                                                                                                                        # BOOTUP 变量的值为 color ,且 GRPAHICAL 变量的值为 yes 
                                                                                                                                                        # 且 /usr/bin/rhgb 文件存在并可执行 
   LC_MESSAGES= /usr/bin/rhgb                                                                                                           # 把 "/usr/sbin/rhgb" 赋予变量 LC_MESSAGES 
   RHGB_STARTED=1                                                                                                                            # RHGB_STARTED 变量的值为1。 
fi 
  
########################################################################################################################################################   
# 注释 :下面部分是使用 sysctl 设置内核的参数 
  
# Configure kernel parameters                     
update_boot_stage RCkernelparam             # 调用 update_boot_stage 函数,该函数由 /etc/rc.d/init.d/functions 脚本定义,主要执行 "/usr/sbin/rhgb-client --update $1" 
action $"Configuring kernel parameters: " sysctl -e -p /etc/sysctl.conf    # 调用 action 函数,执行 sysctl 命令,它读取 /etc/sysctl.conf ,同时输出 “Configuring kernel  
                                                                                                       # parameters” 字符串,action 函数也是由 /etc/rc.d/init.d/functions 脚本定义 
  
# 补充 :action 函数的作用是把第一个参数 $1 赋予变量 string 并输出到屏幕,同时把该字符串也写入 /etc/rhgb/temp/rhgb-console 
  
# 然后把 $1 后面的部分作为命令交给 initlog 执行,并根据结果调用 success 或者 failure 函数。而 success 或者 failure 函数又会根据结果输出 [ OK ] 或者 [ FAILED ] 。 
  
# 除了 success 和 failure 函数外,functions 脚本还定义了 passed 和 warnning 函数,它们又分别调用 echo_passwd() 和 echo_warnning()函数, 
  
# 以输出 [ PASSED ] 和 [ WARNNING ]  
  
########################################################################################################################################################   
# 注释 :下面设置系统的硬件时钟 
# Set the system clock.                    # 接下来设置系统时钟 
update_boot_stage RCclock                # 同样是告诉 update_boot_stage 该事件(执行 /usr/bin/rhgb-client --update="$1" 命令,这里 $1 就是 "RCclock") 
ARC=0                                                 
SRM=0 
UTC=0 
  
if [ -f /etc/sysconfig/clock ]; then        # 如果存在 /etc/sysconfig/clock 则执行该文件 
   . /etc/sysconfig/clock 
  
   # convert old style clock config to new values 
   if [ "${CLOCKMODE}" = "GMT" ]; then                        # 如果变量 CLOCKMODE 为旧式的 GMT 格式 
      UTC=true                                                                # 则 UTC 变量的值为 ture  
   elif [ "${CLOCKMODE}" = "ARC" ]; then                     # 如果 CLOCKMODE 的值是 ARC ,则 
      ARC=true                                                                # ARC 的值为 true 
   fi                                                                         # 如果 CLOCKMODE 不等于 GMT 或者 UTC ,则 UTC 的值等于 /etc/sysconfig/clock 中的值,一般都是 false 
fi                                                                            # 如果 CLOCKMODE 不等于 GMT 后者 ARC ,则 UTC 的值为 0 
  
CLOCKDEF=""                                                            # CLOCKDEF 变量的值默认为空 
CLOCKFLAGS="$CLOCKFLAGS --hctosys"                       # 而 CLOCKFLAGS 变量的值为原来的值(可能为空)加上 '--hctosys' ,表示把硬件时钟写入内核时钟 
  
case "$UTC" in                                                          # 根据 UTC 变量的值进行选择                                              
    yes|true) CLOCKFLAGS="$CLOCKFLAGS --utc"             # 如果 UTC 的值为 yes 或者 true,则变量 CLOCKFLAGS 的值加上 --utc ,表示采用 UTC 时区 
  CLOCKDEF="$CLOCKDEF (utc)" ;;                                     # 同时变量 CLOCKDEF 的值加上 "(utc)" 
    no|false) CLOCKFLAGS="$CLOCKFLAGS --localtime"     # 如果变量 UTC 的值为 false 或者 no,则 
  CLOCKDEF="$CLOCKDEF (localtime)" ;;                        # CLOCKDEF 的值加上 "(localtime)" 。由于安装 OS时没有选择 UTC,CLOCKDEF 的值一般最后就是 (localtime) 而已 
esac 
case "$ARC" in                                                          # 根据 ARC 变量的值来选择               
    yes|true) CLOCKFLAGS="$CLOCKFLAGS --arc"            # 如果是 yes 或者 true ,则 CLOCKFLAGS 的值加上 --arc ,CLOCKDEF 的值加上 '(arc)' 
  CLOCKDEF="$CLOCKDEF (arc)" ;;                                # 如果 ARC 的值为 0 ,则 CLOCKFLAGS 和 CLOCKDEF 的值都不变 
esac 
case "$SRM" in                                                         # 如果 SRM 的值是 yes 或者 true ,则 CLOCKFLAGS 的值加上 "--srm" ,CLOCKDEF 加上 "(srm)" 
    yes|true) CLOCKFLAGS="$CLOCKFLAGS --srm" 
  CLOCKDEF="$CLOCKDEF (srm)" ;; 
esac 
  
/sbin/hwclock $CLOCKFLAGS                    # 执行 hwclock $CLOCKFLAGS 命令,一般就是 /sbin/hwclock --hctosys  
  
action $"Setting clock $CLOCKDEF: `date`" date        # 并输出 "setting clock " 和 $CLOCKDEF ,并执行 date 输出当前时间,然后输出 [ OK ] 
########################################################################################################################################################  
# 注释 :下面部分是设置 key map   
if [ "$CONSOLETYPE" = "vt" -a -x /bin/loadkeys ]; then            # 接下来是设置 keymap ,一般都是 us  
 KEYTABLE=                                                                        #  设置 KEYTABLE 和 KEYMAP  
 KEYMAP= 
 if [ -f /etc/sysconfig/console/default.kmap ]; then                # 假如存在 /etc/sysconfig/console/default.kmap ,则 
  KEYMAP=/etc/sysconfig/console/default.kmap                            # KEYMAP 变量的值为 /etc/sysconfig/console/default.kmap 
 else                                                                                    # 否则 
  if [ -f /etc/sysconfig/keyboard ]; then                                        # 如果存在 /etc/sysconfig/keyboard 文件,则执行它 
    . /etc/sysconfig/keyboard                                                       # 该文件设置 KEYTABLE 变量的值,例如 KEYTABLE="/usr/lib/kbd/keytables/us.map 
  fi 
  if [ -n "$KEYTABLE" -a -d "/lib/kbd/keymaps" ]; then              # 如果 KEYTABLE 的值不为空,且存在 /lib/kbd/keymaps/ 目录,则 
     KEYMAP="$KEYTABLE.map"                                                # KEYMAP 的值为 KEYTABLE 的值加上 ".map"  
  fi 
 fi 
 if [ -n "$KEYMAP" ]; then                                                                 # 假如 KEYMAP 变量的值不为空(间接表示 KEYTABLE 不为空且存在 keymaps 目录) 
  # Since this takes in/output from stdin/out, we can't use initlog 
  if [ -n "$KEYTABLE" ]; then                                                                    # 且 KEYTABLE 的值不为空 
    echo -n $"Loading default keymap ($KEYTABLE): "                                     # 则输出 "Loading default keymap xxxx" 
  else 
    echo -n $"Loading default keymap: "                                                    # 否则只输出 Loading default keymap 
  fi     
  loadkeys $KEYMAP < /dev/tty0 > /dev/tty0 2>/dev/null &&             # 接下来用 loadkeys 加载 $KEYMAP 指定的键盘映射文件。 
     success $"Loading default keymap" || failure $"Loading default keymap"        # 如果成功则调用 success 函数,否则调用 failure 函数 
  echo 
 fi 
fi 
  
######################################################################################################################################################## 
# 注释 :下面部分是设置主机名  
  
# Set the hostname.                                # 接下来是设置主机名 
update_boot_stage RChostname                # 告诉 update_boot_stage 函数该事件,以执行 /usr/sbin/rhgb-client --update RChostname 命令 
action $"Setting hostname ${HOSTNAME}: " hostname ${HOSTNAME}        # 打印 "setting hostname xxxx" 字符串,并执行 hostname ${HOSTNAME}设置主机名 
                                                                 #  注意,HOSTNAME 变量前面已经有过赋值的了,就是 `/bin/hostname` 命令返回的值 
  
######################################################################################################################################################## 
# 注释 :下面设置 ACPI 部分 
# Initialiaze ACPI bits 
if [ -d /proc/acpi ]; then                                                                    # 如果存在 /proc/acpi 目录,则 
   for module in /lib/modules/$unamer/kernel/drivers/acpi/* ; do            # 对于在 /lib/modules//kernel/drivers/acpi/ 目录下的所有模块文件, 
      insmod $module >/dev/null 2>&1                                                        # 都用 insmod 命令加载到内核中 
   done 
fi 
  
  
######################################################################################################################################################## 
# 注释 :下面是主要的部分,就是确认是否需要对 / 文件系统进行 fsck   
if [ -f /fastboot ] || strstr "$cmdline" fastboot ; then      # 接下来是看对 / 系统进行 fsck 的时候了。如果不存在 /fastboot 文件则看 cmdline 变量是否含有 fastboot 字符串 
 fastboot=yes                                                              # 如果有,则 fastboot 变量的值为 yes。/fastboot 是由 shutdown -f 创建的,表示重启时不作 fsck  
fi 
  
if [ -f /fsckoptions ]; then                                            # 如果存在 /fsckoptions 文件,则把文件的内容赋予变量 fsckoptions 变量 
 fsckoptions=`cat /fsckoptions` 
fi 
  
if [ -f /forcefsck ] || strstr "$cmdline" forcefsck ; then        # 如果不存在 /forcefsck 文件,则检查 cmdline 变量是否含有 forcefsck 字符串,如果有 
 fsckoptions="-f $fsckoptions"                                            # 则在 fsckoptions 的值前面加上 "-f" 。/forcefsck 是由 shutdown -F 创建的,强制 fsck  
elif [ -f /.autofsck ]; then                                                 # 如果存在 /.autofsck 
      if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then        #  且文件 /usr/bin/rhgb-clinet 可执行,且 /usr/bin/rhgb 服务在运行(--pnig),则 
           chvt 1                                                                                                #  切换到虚拟控制台 #1 (主要是显示 fsck 的信息用) 
       fi 
       echo $"Your system appears to have shut down uncleanly"                            # 并显示需要 fsck 的信息 “You system appears to have shut down uncleanly” 
       AUTOFSCK_TIMEOUT=5                                                                              # 设置超时时间为5秒 
      [ -f /etc/sysconfig/autofsck ] && . /etc/sysconfig/autofsck                           # 如果存在 /etc/sysconfig/autofsck 则执行它 
      if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                 # 如果执行该文件后 AUTOFSCK_DEF_CHECK 变量的值为 yes ,则 
          AUTOFSCK_OPT=-f                                                                                        # 变量 AUTOFSCK_OPT 的值为 "-f" 
      fi 
  
      if [ "$PROMPT" != "no" ]; then                                                                // 如果 PROMPT 的值不为 no ,则 
        if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                // 且 AUTOFSCK_DEF_CHECK 的值为 yes  
           if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press N within %d seconds to not force file system integrity check..." n ; then    //  执行 getkey 命令,超时5秒,并 
                                                                                                                                                                                       // 提示5秒内按下 n 键可跳过 fsck 
              AUTOFSCK_OPT=        // 如果用户按下 n ,则 AUTOFSCK_OPT 的值为空,表示取消自动 fsck 
           fi 
        else                    // 如果 AUTOFSCK_DEF_CHECK 的值不为 yes ,则提示5秒内按y 可以强制 fsck 
          if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press Y within %d seconds to force file system integrity check..." y ; then        // 同样是用 getkey 捕捉 y 键 
              AUTOFSCK_OPT=-f        // 如果用户按下了 y 键,则 AUTOFSCK_OPT 的值为 "-f"  
          fi 
       fi 
       echo 
      else         // 如果 PROMPT 的值为 "no", 这是用户无法选择是否 fsck  
        # PROMPT not allowed 
        if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then                                                # 取决于 AUTOFSCK_DEF_CHECK 变量的值,如果是 yes ,则 
          echo $"Forcing file system integrity check due to default setting"     # 提示由于默认的设置强制 fsck 。可能是 mount 次数达到限制,或者多长时间没有 fsck 了 
        else                                             
          echo $"Not forcing file system integrity check due to default setting"            # 如果 AUTOFSCK_DEF_CHECK 的值为 no ,则表示不做 fsck  
        fi 
      fi 
      fsckoptions="$AUTOFSCK_OPT $fsckoptions" 
fi 
  
# 注释 :注意!到这里为止并没有执行 fsck 操作,只是判断是否需要执行 fsck 以及 fsck 命令的选项 
  
if [ "$BOOTUP" = "color" ]; then       # 如果 BOOTUP 的值为 color ,则 
 fsckoptions="-C $fsckoptions"            # -C 表示显示 fsck 进度信息 
else                                              # 否则 
 fsckoptions="-V $fsckoptions"            # -V 表示显示 verbose 信息 
fi 
  
if [ -f /etc/sysconfig/readonly-root ]; then            # 如果存在 /etc/sysconfig/readonly-root ,则 
    . /etc/sysconfig/readonly-root                            # 执行该文件 
      
    if [ "$READONLY" = "yes" ]; then                            # 如果 READONLY 变量的值为 yes ,则 
        # Call rc.readonly to set up magic stuff needed for readonly root        # 执行 /etc/rc.readonly 脚本 
        . /etc/rc.readonly 
    fi 
fi 
 
######################################################################################################################################################### 
# 注释 :下面开始判断 / 文件系统的类型(是本地还是 nfs 格式)。如果是nfs则不执行 fsck ,否则继续 
 
_RUN_QUOTACHECK=0                                            # 初始化 RUN_QUOTACHECK 的值为0.这里是作一个标记,因为后面的代码可能会导致重启。在这里做好标记 
                                                                            # 如果后面没有重启,就可以把 _RUN_QUOTACHECK 设置为1 
ROOTFSTYPE=`awk '/ / / && ($3 !~ /rootfs/) { print $3 }' /proc/mounts`        # 从 /proc/mounts 中找出 / 文件系统的类型,并赋予变量 ROOTFSTYPE  
 
# 注释 :下面用到 -z "$fastboot" ,如果有 /fastboot ,则 fastboot 的值为 yes, 所以就不合 if 的条件,则跳过 fsck 。这就是 shutdown -f 快速启动的效果 
  
if [ -z "$fastboot" -a "$READONLY" != "yes" -a "X$ROOTFSTYPE" != "Xnfs" -a "X$ROOTFSTYPE" != "Xnfs4" ]; then     # 如果fastboot 变量的值为空且  READONLY 变量不为 yes  
                                                                                                                                                                # 且 / 文件系统的类型不是 nfs 或者 nfs4 ,则  
        STRING=$"Checking root filesystem"    # 初始化 string 变量并输出 "checking root filesystem"  
        echo $STRING 
        rootdev=`awk '/ / / && ($3 !~ /rootfs/) {print $1}' /proc/mounts`        # 从 /proc/mounts 中找出 / 文件系统所在的设备,并赋予 rootdev 变量(例如 /dev/hdb2) 
         if [ -b /initrd/"$rootdev" ] ; then        # 如果 /initrd/$rootdev 是一个 block 设备 
              rootdev=/initrd/"$rootdev"                    # 则 rootdev 就是 /initrd/rootdev  
         else                                                 # 否则 rootdev 就是 / ,正常情况应该是执行该步的,也就是 rootdev 最终为 / 
         rootdev=/                                       # rootdev 的值为 / ,因为 fsck 支持用 LABEL、mount point、device name 来指定要检查的文件系统  
         fi 
     
        # 注释 : 下面开始真正的 fsck 操作 
 
         if [ "${RHGB_STARTED}" != "0" -a -w /etc/rhgb/temp/rhgb-console ]; then        # 如果 RHGB_STARTED 不为0且 /etc/rhgb/tmp/rhgb-console 文件可写,则 
              fsck -T -a $rootdev $fsckoptions > /etc/rhgb/temp/rhgb-console           # 执行 fsck 命令,-T 表示不打印标题栏,-a 表示自动修复错误,设备是 / ,后面是选项 
                                                                                                                      # 且信息写入 /etc/rhgb/temp/rhgb-console 
         else                                                                                                           # 否则 
              initlog -c "fsck -T -a $rootdev $fsckoptions"                                                        # 调用 initlog 执行 fsck ,信息直接输出到屏幕,fsck 命令本身都是一样的。 
        fi                                                                                                                      
        rc=$?                                    # 把 fsck 的结果送给变量 rc 
        if [ "$rc" -eq "0" ]; then         # 如果 rc的值为0,表示 fsck 成功通过 
              success "$STRING"                    # 则调用 success 打印 "checking root fileysyes      [OK]" 
          echo 
        elif [ "$rc" -eq "1" ]; then        # 如果 rc 为1 ,则打印 passed ,表示有错误但修复 
             passed "$STRING"                # 这时调用的是 passed 函数(由 /etc/rc.d/init.d/functions 脚本定义,输出 "checking root filesystems     [ PASSED ]") 
             echo 
         elif [ "$rc" -eq "2" -o "$rc" -eq "3" ]; then         # 如果 rc 为 2 (表示系统应该重启)或者为 3(为 1+2 ,表示系统错误已修复,但需要重启) 
              echo $"Unmounting file systems"                        # 提示卸载 / 文件系统 
              umount -a                                                        # 把所有卸载,当然 / 除外 
              mount -n -o remount,ro /                                  # 把 / 设备已 ro 模式重新 mount 
              echo $"Automatic reboot in progress."                # 然后提示将要自动重启了 
              reboot -f                                                         # 在这里执行 reboot -f ,强制重启 
          fi     # 这个 fi 是结束 "[ "$rc" -eq "0" ]; " 的 
  
          # A return of 4 or higher means there were serious problems.    # 如果 fsck 返回的值大于4(不含4,4表示错误未修复),则表示 / 文件系统出现重大错误,无法继续 
         if [ $rc -gt 1 ]; then    # 这里之所以用 -gt 1 ,是因为如果前面返回2或者3就会自动重启了,如果执行到这里说明 rc 的值必须至少大于等于4 
             if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then    # 如果 rhgb-client 可执行且 rhgb 服务在运行 
                  chvt 1                 # 强制切换到 1# 虚拟控制台 
             fi 
  
            failure "$STRING"        # 调用 failure 函数,打印 "checking root filesystem     [ FAILURE ]" 
            echo 
            echo 
            echo $"*** An error occurred during the file system check."        # 提示 fsck 过程出错 
            echo $"*** Dropping you to a shell; the system will reboot"         # 提示将进入紧急模式的 shell  
            echo $"*** when you leave the shell."                                       # 当你退出该 shell 时会自动重启 
  
            str=$"(Repair filesystem)"        # 设置紧急模式下的 shell 提示符 
            PS1="$str # # "; export PS1     #  设置提示符变量 PS1 为 "(Repair filesystem) # " , 表示当前是第几个命令 
            [ "$SELINUX" = "1" ] && disable_selinux 
            sulogin                                    # 自动执行 sulogin ,这不是 su ,而是 single-user login 的意思,自动进入单用户模式的 shell 
         
                              # 注意这里将进入一个交互式的 shell 。只有你执行 exit 命令,才会执行下面的代码,否则下面的代码根本不会被执行 
  
            echo $"Unmounting file systems"    # 提示卸载文件系统 
            umount -a                                    # 卸载所有文件系统,/ 除外 
            mount -n -o remount,ro /              # / 以 ro 模式挂载 
            echo $"Automatic reboot in progress."        # 提示将自动重启 
            reboot -f                                     # 立即重启 
 
         elif [ "$rc" -eq "1" ]; then                     # 如果 rc 的值等于1 ,则表示错误修复完毕 
            _RUN_QUOTACHECK=1                    # 如果上面的 fsck 没有出错,自然就不会重启,所以这里把 _RUN_QUOTACHECK 的值设置为1,表示可以进行 quotacheck 了 
 
         fi   # 这个 fi 是结束 " [ $rc -gt 1 ]; " 的                             
 
         if [ -f /.autofsck -a -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then    #  如果 rhgb-clinet 可运行且 rhgb 服务在运行 
          chvt 8                                                                                                                    # 则切换换 8# 控制台,也就是图形 
         fi 
fi        # 这个 fi 是结束前面的 ”[ -z "$fastboot" -a "$READONLY" != "yes" -a "X$ROOTFSTYPE" != "Xnfs" -a "X$ROOTFSTYPE" != "Xnfs4" ]; “ 的 
  
       # 所以如果存在 /fastboot 文件,就直接跳转到这里 
  
######################################################################################################################################################## 
# 注释 :在 initrd 的 /linuxrc 或者 /init 脚本中会把初始/文件系统卸载掉,如果没有卸载完全,则在这里会重新做一次 
  
# Unmount the initrd, if necessary                    # 接下来是卸载 initrd 初始/ 文件系统,一般 /init 都会在结尾 umount /initrd/dev 的  
if LC_ALL=C fgrep -q /initrd /proc/mounts && ! LC_ALL=C fgrep -q /initrd/loopfs /proc/mounts ; then    # 如果 /proc/mounts 文件还有 /initrd/proc 条目,  
                                                                                                                                                      # 且不存在 /initrd/loops 条目,则 
   if [ -e /initrd/dev/.devfsd ]; then                                                                                                     # 如果存在 /initrd/dev/.devfsd 文件,则        
      umount /initrd/dev                                                                                                                            # 卸载 /initrd/dev 目录 
   fi 
   umount /initrd                                            # 最后把整个 /initrd 卸载 
   /sbin/blockdev --flushbufs /dev/ram0 >/dev/null 2>&1    # 最后用 blockdev --flushbs 命令清除 /dev/ram0 的内容,该社备就是被挂载为初始/的 
fi 
########################################################################################################################################################                                                                                # 注释 :下面是对 / 进行 quota 检查,其他文件系统暂时不执行 quotacheck  
 
# Possibly update quotas if fsck was run on /.                    # 如果 fsck 已经对 / 进行了检查,则执行 quotacheck 更新 quota 情况 
LC_ALL=C grep -E '[[:space:]]+/[[:space:]]+' /etc/fstab |         # 在 /etc/fstab 中找出 / 文件系统所在的行,并打印第4个字段(属性)
    awk '{ print $4 }' |                                                            # 这是属性字段 
    LC_ALL=C fgrep -q quota                                                    # 找出是否有 quota 字符串,不管是 usrquota 还是 grpquota  
_ROOT_HAS_QUOTA=$?                                                    # 把结果赋予 _ROOT_HAS_QUOTA 变量 
if [ "X$_RUN_QUOTACHECK" = "X1" -a                               #  如果 x$_RUN_QUOTACHECK 返回 X1 ,且 
    "X$_ROOT_HAS_QUOTA" = "X0" -a                                 #   X$_ROOT_HAS_QUOTA 返回 X0 ,则表示当前可以执行 quotacheck ,且 / 也启用了 quota  
    -x /sbin/quotacheck ]; then                                        #  如果存在 quotacheck 命令    
 if [ -x /sbin/convertquota ]; then                                    # 如果存在 convertquota 命令 
     if [ -f /quota.user ]; then                                                # 且存在 /quota.user 则 
          action $"Converting old user quota files: "                           # 并调用 action 函数,打印提示信息,用 convertquota 转换成 v2 格式的 aquota.user 文件 
          /sbin/convertquota -u / && rm -f /quota.user                # 然后删除 v1格式的 /quota.user 
     fi 
     if [ -f /quota.group ]; then                                            # 同样对 /quota.group 转换成 v2 格式的 aquota.group,并删除 /quota.group 文件 
          action $"Converting old group quota files: "  
          /sbin/convertquota -g / && rm -f /quota.group            # 同样在转换后删除旧的 v1 的 group quota 文件 
     fi 
 fi 
 action $"Checking root filesystem quotas: " /sbin/quotacheck -nug /        # 然后执行 quotacheck -nug / 对 / 文件系统进行检查,并打印提示信息 
fi    # 这个 fi 是结束 "[ "X$_RUN_QUOTACHECK" = "X1" -a "X$_ROOT_HAS_QUOTA" = "X0" -a -x /sbin/quotacheck ]; " 的 
  
######################################################################################################################################################## 
# 注释 :下面这个部分是设置 ISA 设备的,不过由于现在基本没有 ISA 设备了,所以可以跳过该部分,甚至可以在内核中指定不支持 ISA  
if [ -x /sbin/isapnp -a -f /etc/isapnp.conf -a ! -f /proc/isapnp ]; then         
    # check for arguments passed from kernel 
    if ! strstr "$cmdline" nopnp ; then 
 PNP=yes 
    fi 
    if [ -n "$PNP" ]; then 
 action $"Setting up ISA PNP devices: " /sbin/isapnp /etc/isapnp.conf 
    else 
 action $"Skipping ISA PNP configuration at users request: " /bin/true 
    fi 
fi 
########################################################################################################################################################  
# Remount the root filesystem read-write.                                    # 现在 / 文件系统 fsck 过了,可以按照 read-write 的模式挂载了 
update_boot_stage RCmountfs                                                      # 告诉 rhgb 服务器更新 RCmountfs 服务的状态 
state=`awk '/ / / && ($3 !~ /rootfs/) { print $4 }' /proc/mounts`    # 从 /proc/mounts 中找出 ' / ' 字符串并且第3个字段不等于 'rootfs',则打印其第4个字段,赋予 state 
[ "$state" != "rw" -a "$READONLY" != "yes" ] &&                               # 如果 state 变量的值不为 'rw' 且 READONLY 变量的值为 yes ,则   
  action $"Remounting root filesystem in read-write mode: " mount -n -o remount,rw /        # 用 -o remount -o rw 重新挂载 / 为 rw 模式,并打印提示信息 
########################################################################################################################################################  
# 注释 :下面开始是 LVM2 的部分了 
# LVM2 initialization                                                                                        # 下面部分是 LVM2  的初始化部分 
if [ -x /sbin/lvm.static ]; then                                                                          # 如果存在 /sbin/lvm.static 且可执行 
    if ! LC_ALL=C fgrep -q "device-mapper" /proc/devices 2>/dev/null ; then        # 如果 /proc/devices 文件不含有 device-mapper ,则
         modprobe dm-mod >/dev/null 2>&1                                                                        # 调用 modprobe 加载 dm-mod  
    fi 
    echo "mkdmnod" | /sbin/nash --quiet >/dev/null 2>&1                                    # 并调用 nash 执行 mkdmmod ,不过是以 quiet 模式执行的 
    [ -n "$SELINUX" ] && restorecon /dev/mapper/control >/dev/null 2>&1 
    if [ -c /dev/mapper/control -a -x /sbin/lvm.static ]; then                                # 假如存在 /dev/mapper/control 这个字符设备且 /sbin/lvm.static 可执行,则 
         if /sbin/lvm.static vgscan --mknodes --ignorelockingfailure > /dev/null 2>&1 ; then        # 调用 lvm.static 执行 vgscan 扫描所有卷组(忽略锁错误) 
             action $"Setting up Logical Volume Management:" /sbin/lvm.static vgchange -a y --ignorelockingfailure        # 如果扫描到卷组,则调用 action 函数, 
         fi                                                                                                                                                      # 打印提示信息,并执行 vgchange -ay 激活全部卷组 
    fi 
fi 
 
# LVM initialization                 # 这是 LVM1 的部分,因为系统可能同时存在 lVM1 和 lvm2 的 pv 
if [ -f /etc/lvmtab ]; then        # 如果存在 /etc/lvmtab 文件 
    [ -e /proc/lvm ] || modprobe lvm-mod > /dev/null 2>&1    # 且 /proc/lvm 文件存在,如果没有则调用 modprobe lvm-mod 加载 lvm-mod ,注意模块名的不同 
        if [ -e /proc/lvm -a -x /sbin/vgchange ]; then                # 如果现在存在 /proc/lvm 文件且 /sbin/vgchange 可执行 
             action $"Setting up Logical Volume Management:" /sbin/vgscan && /sbin/vgchange -a y    # 则调用 action 打印提示信息,并执行 vgchange -ay 激活它们 
        fi 
fi 
  
########################################################################################################################################################  
# Clean up SELinux labels 
if [ -n "$SELINUX" ]; then 
   for file in /etc/mtab /etc/ld.so.cache ; do 
    [ -r $file ] && restorecon $file  >/dev/null 2>&1 
   done 
fi 
########################################################################################################################################################  
# Clear mtab                                                            # 由于之前的 /etc/mtab 并不准确,所以现在把它的内容清掉 
(> /etc/mtab) &> /dev/null                                              # 用 >/etc/mtab 清空其内容   
  
# Remove stale backups 
rm -f /etc/mtab~ /etc/mtab~~                                    # 删除 /etc/mtab 的备份文件 
  
# Enter root, /proc and (potentially) /proc/bus/usb and devfs into mtab.        # 把 root ,/proc ,/proc/bus/usb,devpts 加入到 /etc/mtab 中 
mount -f /                                                                    #  -f 表示只更新 /etc/mtab ,但不实际mount,因为这些文件系统在之前都挂载了,没有必要再挂载一次 
mount -f /proc 
mount -f /sys >/dev/null 2>&1 
mount -f /dev/pts 
[ -f /proc/bus/usb/devices ] && mount -f -t usbfs usbfs /proc/bus/usb  # 如果存在 /proc/bus/usb/devices 文件,则用 mount -f -t usbfs 挂载到 /proc/bus/usb 下       
[ -e /dev/.devfsd ] && mount -f -t devfs devfs /dev         # 如果存在 /dev/.devfsd ,则同样挂载 devfs 到 /dev 下 
########################################################################################################################################################  
# configure all zfcp (scsi over fibrechannel) devices before trying to mount them 
# zfcpconf.sh exists only on mainframe 
[ -x /sbin/zfcpconf.sh ] && /sbin/zfcpconf.sh 
########################################################################################################################################################  
# The root filesystem is now read-write, so we can now log        # 由于之前的 / 是只读的,所以通过 initlog 来记录信息,现在可以通过 syslog 了 
# via syslog() directly.. 
if [ -n "$IN_INITLOG" ]; then 
    IN_INITLOG=                                                                            # 如果 IN_INITLOG 的值不为空则清空它 
fi 
  
if ! strstr "$cmdline" nomodules && [ -f /proc/modules ] ; then        # 如果内核启动参数含有 "nomodules" ,且存在 /proc/modules 文件,则 
    USEMODULES=y                                                                            # 把 USEMODULES 设置为 y 
fi 
  
# Load modules (for backward compatibility with VARs)                 
if [ -f /etc/rc.modules ]; then 
 /etc/rc.modules 
fi 

原文地址:https://www.cnblogs.com/tankblog/p/6805193.html