各位是否曾经对电脑整个开机的流程感到好奇呢 ? 这一次 , 我们所要讨论的
主题 , 就是 Linux 从开机的一瞬间到 login 为止 , 到底发生了什么事情 ?
想必各位都知道 , 在刚开机时 , 由于 80x86 的特性 , CS ( Code Segment )
这个寄存器中全部都放着 1 , 而 IP ( Instruction Pointer ) 这个寄存器
中全部都放着 0 , 换句话说 , CS=FFFF 而 IP=0000 , 此时 , CPU 就依据
CS 及 IP 的值 , 到 FFFF0H 去执行那个地方所放的指令 . 这时候 , 由于
FFFF0H 已经到了高位址的顶端 , 所以 , FFFF0H 这个地方 , 总是会放一个
JMP 指令 , 跳到比较低的位址 . 接著 , ROM BIOS 就会作一些检查的动作
像内存 , 键盘 等...... 并在我们俗称的 UMB ( Upper Memory Block )
之中扫描 , 看看是否有合法的 ROM 存在 ( 比如 SCSI 卡上的 ROM ) .
假如有 , 就到里面去执行一些东西 , 执行完之后再继续刚才的行程 . 到了
最后 , 读取硬盘上的第一个 sector . 在这里 , 我假设各位由硬盘启动
因此 , 就硬盘的构造而言 , 它的第一个 sector 称为 MBR ( Master Boot
Record ) . 因为一个 sector 是 512 bytes , 而 MBR 这 512 bytes 可分
为两个部份 , 第一个部份为 Pre-Boot 区 , 占了 446 bytes ; 第二部份
是 Partition Table , 占了 66 bytes . Pre-Boot 区的作用之一 , 就是
去看看那个 Partition 被标成 Active , 然後去读那个 Partition 的 Boot
区 .
在 Linux 的启动方面 , 一般人最常把 LILO 放在 MBR 或 Superblock
假如你把 LILO 放在 MBR , 那很明显的 , 当读取到 MBR 的时候 , LILO
就被执行 , 此时 , 你的屏幕上会出现 boot: 接着 , 就进行 Load Kernel
的动作 . 在另一方面来说 , 假如你把 LILO 安装在 Superblock , 通常你
还会有一个管理开机的程序 , 也许是放在 MBR ( 像 OSBS ) 或者是放在一
个单独的 Partition ( 像 OS/2 的 Boot Manager ) . 再由这个管理开机
的程式去读取 LILO , 进而做 Load Kernel 的动作 .
好了 , 到了目前为止 , 我们已经讲到 Load Kernel 的动作 . Kernel 被
load 到 memory 中之后 , 接着进行一连串 probe 周边的动作 , 像串口
并口 , 软盘 , 声卡 , 硬盘 , 光驱 等 ...... 接着 mount root
partition . 在这之后 , kernel 会起动 init 这个 process . init 这
个 process 的 PID 为 1 , 它是所有 process 的祖先 .
接下来呢 ? 系统就开始执行 /rc.d/rc.S , 在这里 , 我们暂时打住 ,
先对大概的 initialization script 执行的顺序作一个浏览 , 请看下面
的流程 :
init[1]
rc.S begin <--- 目前我们已经讲到这里
rc.serial begin
rc.serial end
rc.S end
init[1] enter runlevel 5
rc.M begin
rc.inet1 begin
rc.inet1 end
rc.inet2 begin
rc.inet2 end
rc.font begin
rc.font end
rc.local begin
rc.local end
rc.M end
login
上面的流程清楚的指出 , 在 rc.S 这个 shell script 中 , 会去执行 rc.serial
接着再执行 rc.M , rc.M 中又包含了 rc.inet1 , rc.inet2 , rc.font , rc.local
最后执行 login . 在下面的内容中 , 将为各位介绍这几个 shell script
从下面开始 , 凡是每一列之前有一个 # 的 , 为原来 shell script 中的注释
有两个 # 的 , 为笔者加上的注释 , 当然啦 , 没有任何 # 的为 shell script
的内容 , 而对命令或内容的解释 , 一律都写在命令或内容的前面 .
首先由 rc.S 开始 :
***************************** rc.S **********************************
#!/bin/sh
#
# /etc/rc
#
# These commands are executed at boot time by init(8).
# User customization should go in /etc/rc.local.
echo '======== rc.S is running ! System Initializing Now !!! ========'
PATH=/sbin:/usr/sbin:/bin:/usr/bin
## 打开所有 swap ! 下面 /sbin/swapon -a 的意思是 : 使得 /etc/fstab 中被记录
## 成 swap 的 device 全部启动 .
/sbin/swapon -a
?
## 喔 ! 下面这个指令 update 就很重要了 , 它负责每隔一段固定的时间 , 就将
## buffer 中的资料 , 利用 sync 写回磁盘上 , 并将 superblock 做 update
## 的动作 . 使用 ps 这个指令看看有那些 process , 就可看到 update 还有一个
## bdflush , 这两个 process 都是必然要存在的 , 可不要随便砍掉 , 要不然 ,
## 万一系统 crash 了 , 那磁盘里面的资料就不是最新的了 ......
/sbin/update &
?
## 利用 echo -n >;>; 制造一个文件 , 并用 rm -f 这个档案来测试 root partition
## 是不是 read-only 或者是可读写
READWRITE=no
if echo -n >;>; "Testing filesystem status"; then
rm -f "Testing filesystem status"
READWRITE=yes
fi
?
## 假如 root partition 是 read-only 就作 fsck 的动作 , 假如不是 read-only
## 而是 read-write 的话 , 就做下面 else 之后的动作
if [ ! $READWRITE = yes ]; then
## 利用 fsck 做检查及修复文件系统的工作 , 后面接的两个参数 -A , -a .
## -A 的意思是 : fsck 会依据 /etc/fstab 中的记录 , 去检查所有的文件
## 系统 ; 而 -a 就是 auto 的意思 , 当 fsck 有修复的动作时 , 它不会问
## 你问题 , 而直接修复 .
/sbin/fsck -A -a
?
## 假如 fsck 有 error , [ $? -gt 1 ] 括号里面的意思是 : 若上个命令的
## 传回值大于 1 , 而上个命令就是 fsck . 让我们看看 fsck 的传回值 :
## 0 - No errors
## 1 - File system errors corrected
## 2 - File system errors corrected, system should
## be rebooted if file system was mounted
## 4 - File system errors left uncorrected
## 8 - Operational error
## 16 - Usage or syntax error
## 128 - Shared library error
## 很明显的 , 若有任何错误产生的话 , 那 fsck 的传回值都大于 1 . 其实
## 就我的观点认为 , 应该写成 if [ $? -ge 1 ] 比较好 . 既然有错呢 , 系统
## 应该就要跳至单用户模式 , 在单用户模式中主要就是 /etc/rc.d/rc.K
## 中的两件事 : 关掉 swap 及 卸下所有的文件系统 , 而最后重新 login .
## 一般正常的情况下 , if 下面这一大段是不会执行的 , 而会跳至下面
## 标有 ************************* Normal 1 ************************* 处
if [ $? -gt 1 ] ; then
echo
echo
echo "**************************************"
echo "fsck returned error code - REBOOT NOW!"
echo "**************************************"
echo
echo
/bin/login
fi
## ****************************** Normal 1 **************************
## 当 fsck 检查没有错误之后 , 就把 root partition 重新 mount 上来
## 下面指令的参数有三个 , -w 代表mount 成可读写 , -n 代表把一个 file-
## system mount 上来 , 但不会把记录写到 /etc/mtab 中 , 在上次对 /etc/mtab
## 介绍时有提到 , 当我们使用 mount 这个指令把一个 filesystem mount 上来
## 的时候 , /etc/mtab 就会记录 ! 利用 -n 这个 option 可使得做 mount 的动
## 作 , 却不会记录 . -o 后面可以接许多的选项 , 在这里 , 我们给它的选项是
## remount . remount 的意思是 : 重新 mount 一个已经被 mount 的 filesystem
## 这个选项通常被用来改变该 filesystem 的读写标志 ,尤其是把 filesystem
## 从 read-only 的状态 , 改变成 read-write 的状态 .
echo "Remounting root device with read-write enabled."
/sbin/mount -w -n -o remount /
?
## 在前面的情况中 , 都是 root partition 为 read-only 的状态下 , 所做的一些
## 工作 , 到了最后一个指令 /sbin/mount -w -n -o remount / , 才把 root
## partition mount 成 read-write . 各位有没有看到前面那行 :
## if [ ! $READWRITE = yes ]; then ..... 下面这个 else 就是与这个 if 对应
## 也就是说 , 前面那个 if 的区块中 , 所作的工作都是在 root partition 为
## read-only 的条件成立下所作的事 , 那很明显的 , 下面这个 else 就是 root
## partition 为 read-write 的条件下所作的工作 . 假如你的 root partition
## 为 read-writeable 的话 , 那么系统就会显示下面的信息 . cat << EOF 所作的
## 事 , 就是把 EOF 之前的信息全部显示在屏幕上 :
## 我想 , 下面的信息写得很明显了 , 它说 : 你的 root partition 被 mount 成
## read-write , 没有办法检查 , 要使检查的动作能够顺利的进行 , 你必须把
## root partition mount 成 read-only ! 那要怎么做呢 ? 很容易 , 只要利用
## rdev -R / 1 就可以了 ......
else
cat << EOF
*** Root partition has already been mounted read-write. Cannot check!
For filesystem checking to work properly, your system must initially mount
the root partition as read only. Please modify your kernel with 'rdev' so
that
it does this. If you're booting with LILO, type:
rdev -R /vmlinuz 1
(^^^^^^^^ ... or whatever your kernel name is.)
If you boot from a kernel on a floppy disk, put it in the drive and type:
rdev -R /dev/fd0 1
This will fix the problem *AND* eliminate this annoying message. :^)
EOF
?
## 下面这个指令没什么好说的 , 就是暂停 10 秒钟 , 让 user 能够有充足的
## 时间看完上面的信息
sleep 10
fi
?
## 删除 /etc/mtab /etc/nologin /etc/utmp
/bin/rm -f /etc/mtab* /etc/nologin /etc/utmp
?
## 制造 /etc/utmp , 这是一个很典型制造空文件的写法 . /dev/null 这个 node
## 蛮有趣的 , 在某一方面来说 , 它有点像是一个 " 黑洞 " . 怎么说呢 ?
## 各位可以试试看下面的指令 ls >;>; /dev/null , 当你使用这个指令之后会
## 发生什么事呢 ? 什么也没发生 , 而且 ls 的输出就好像被丢到黑洞里 , 无
## 影无踪了 . 那也许你会想 : 那这有什么用 ? 我的回答是 : 的确没有什么
## 很大的用处 , 但当你想抑制输出的信息时 , 你就会用得到了 .
cat /dev/null >;>; /etc/utmp
?
## 依据 fstab ( filesystem table ) 中的描述 , 自动的挂上文件系统
## 但此时因为 TCP/IP 还没有设定 , 故不用 NFS
echo 'Mount Filesystem !!!'
/sbin/mount -avt nonfs
?
## 设定系统的时钟 . 下面这几行所做的事就是 : 看看 /sbin/clock 这个文件是
## 不是可执行的 , 假如可以执行 , 就把 CMOS 中的时间设定为系统的时间 .
if [ -x /sbin/clock ]; then
echo 'Set System Clock'
/sbin/clock -s
fi
?
## 下面的四行若没有 mark , 则每次开机 issue 及 motd 都会被改变 , 这应该
## 可算是 FAQ 级的问题了 ...... 因为我有自己设计的 issue 及 motd , 所以
## 下面的四行前面都有 # , 被注释掉 .
## 假如你要有自己的设定 , 下面一定都要 mark 起来
#echo >; /etc/issue
#echo Welcome to Linux /bin/uname -a | /bin/cut -d\ -f3. >;>; /etc/issue
#echo >;>; /etc/issue
#echo "/bin/uname -a | /bin/cut -d\ -f1,3. (Posix)." >; /etc/motd
?
## 接下来 , 将执行 rc.serial , 顾名思义 , rc.serial 是作串口设定的工作
## 在 rc.serial 中 , 内容虽然也是很简单 , 但并不像 rc.S 那样直接 . 换句话
## 说 , 读者至少要 " 稍微 " 懂一点 shell programming , 所以说呢 , 假如
## 还不会 shell programming 的读者呢 , 都应该赶快去找一本书来看一下 , 在
## 这篇文章的结尾 , 我会提出一些书单 , 各位可以去找找这几本书 ......
/bin/sh /etc/rc.d/rc.serial
echo '================= rc.S is finish NOW !!! ========================='
?
?
到了这里 , rc.S 的最後一步 , 是去执行 rc.serial . 大家可以看一看
/rc.d/rc.serial . 好像很长的样子 , 但实际上呢 , 各位必然发现到了 , 这个
shell script 大部份指令的前面都有一个 '#' 号 , 这代表着 , 这些指令完全
不会被执行 . 所以呢 , 真正有用的只不过寥寥十几行吧 ! 在另一方面来说 ,
假如你是用网卡连上网络 , 那 rc.serial 对你并没有什么大用处 .
?
**************************** rc.serial ******************************
#!/bin/sh
#
# /etc/rc.serial
# Initializes the serial ports on your system
#
# Version 2.01
echo '======================= rc.serial is begin !!! ====================='
cd /dev
## 下面三行中的前两行是设定一些变量 , 由于在这个 shell script 中 , 需要
## 用到 /bin/setserial -b 这个指令 , 或是需要用到所有以 cua 开头的 node
## 的次数太多了 , 因此 , 把它们设定为一个变量 , 是一个不错的方法 . 尤其
## PORTS=`echo cua? cua??` 这是一个聪明的写法 , 那为什么不写成 PORT=
## `echo cua*` 呢 ? 各位可以在 /dev 下分别使用这两个指令 , 观察输出到底
## 有什么不同 ......
SETSERIAL="/bin/setserial -b"
PORTS=`echo cua? cua??`
echo -n "Configuring serial ports...."
?
## 下面这行 , 没有学过 shell programming 的人很可能会看不懂 , 不过没有
## 关系 , 这行中的 ${SETSERIAL} 会被换成 /bin/setserial -b , 而 ${PORTS}
## 会被换成 cua0 cua1 cua2 ....... cua31 , 所以整句翻译就是 :
## /bin/setserial -b -W cua0 cua1 cua2 cua3 cua4 cua5 cua6 ...... cua31
## 那这行指令到底在做什么呢 ? 其实只是在做中断侦测的工作 .
${SETSERIAL} -W ${PORTS}
?
## 各位看到下面原来的注释了吧 . 当你有一些特殊的卡时 , 你可以把相对应部
## 份前面的 '#' 去掉 , 以便能做自动设定的工作 . 其实呢 , 这种情况实在
## 不多 , 大部份人的设备都差不了多少 , 说到关于串口 , 差异就更少了 .
#
# AUTOMATIC CONFIGURATION
#
# Uncomment the appropriate lines below to enable auto-configuration
# of a particular board. Or comment them out to disable them....
#
?
## 好了 , 这下我们又多了一个变量 : AUTO_IRQ , 这在下面会用到 .
AUTO_IRQ=auto_irq
?
## 下面几行非常整齐 , 它们可以分别被换成 :
## /bin/setserial -b /dev/cua? auto_irq skip_test autoconfig
## setserial 说穿了也没什么 , 这个指令可以让你对 serial port 做设定及回报
## 的动作 , 像 IRQ , I/O port 啦等等的事情 . 一般的情况下 , 大家的电脑中
## 通常只有 COM1-COM4 , 但假如你想增加新的 port , 那 setserial 就派上用
## 场了 .
# These are the standard COM1 through COM4 devices
#
# If you have an internal modeme with a Rockwell Chipset, add a "skip_test"
# to the /dev/cua3 line below. (It's not added by default because it will
# screw up people with 8514 displays).
#
${SETSERIAL} /dev/cua0 ${AUTO_IRQ} skip_test autoconfig
${SETSERIAL} /dev/cua1 ${AUTO_IRQ} skip_test autoconfig
${SETSERIAL} /dev/cua2 ${AUTO_IRQ} skip_test autoconfig
${SETSERIAL} /dev/cua3 ${AUTO_IRQ} autoconfig
# These are for the first AST Fourport board (base address 0x1A0)
#
${SETSERIAL} /dev/cua4 ${AUTO_IRQ} autoconfig
${SETSERIAL} /dev/cua5 ${AUTO_IRQ} autoconfig
${SETSERIAL} /dev/cua6 ${AUTO_IRQ} autoconfig
${SETSERIAL} /dev/cua7 ${AUTO_IRQ} autoconfig
# These are for the second AST Fourport board (base address 0x2A0)
#
${SETSERIAL} /dev/cua8 ${AUTO_IRQ} autoconfig
${SETSERIAL} /dev/cua9 ${AUTO_IRQ} autoconfig
${SETSERIAL} /dev/cua10 ${AUTO_IRQ} autoconfig
${SETSERIAL} /dev/cua11 ${AUTO_IRQ} autoconfig
?
## 从这里以下 , 我省略了一大段 , 因为这一大段都是支持特殊的卡
# These are the 3rd and 4th ports on the Accent Async board.
#
#${SETSERIAL} /dev/cua12 ${AUTO_IRQ} autoconfig
#${SETSERIAL} /dev/cua13 ${AUTO_IRQ} autoconfig
#
?
.
.
.
.
.
.
.
?
## 好了 , 我们跳掉了一大段 , 直到这里 . 各位看到下面的注解了 .
## 假如你想用手动的方法指定 IRQ , I/O port , 及指定 chip 的型号
## 那你可以拿掉下面对应那行前面的 '#' 并作适当的修改 , 比如说
## 你用的是比较新的 16550 或 16550A , 而不是 16450 , 那你就可以
## 在下面用手动的方法指定 . 实际上 , 用 autoconfig 及 autoirq
## 的选项就可以了 , 没有必要用手动的方式 . 除非侦测不到 .
###############################################################
#
# MANUAL CONFIGURATION
#
# If you want to do manual configuration of one or more of your
# serial ports, uncomment and modify the relevant lines.
#
###############################################################
# These are the standard COM1 through COM4 devices
#
#${SETSERIAL} /dev/cua0 uart 16450 port 0x3F8 irq 4
#${SETSERIAL} /dev/cua1 uart 16450 port 0x2F8 irq 3
#${SETSERIAL} /dev/cua2 uart 16450 port 0x3E8 irq 4
#${SETSERIAL} /dev/cua3 uart 16450 port 0x2E8 irq 3
.
.
.
.
.
.
.
.
## Ok , 到此 , rc.S 及 rc.serial 已经结束 , 因为截稿时间的关系 , rc.M
## rc.inet1 , rc.inet2 , rc.font , rc.local 将在以后为各位介绍 .
echo "done."
${SETSERIAL} -bg ${PORTS}
echo ' ====================== rc.serial is complete !!! ==================='
?
?
* 关于 Shell Programming 的书单 :
Title: The Unix C Shell Field Guide
Authors: Gail Anderson and Paul Anderson
Publisher: Prentice Hall
Edition: 1986
ISBN: 0-13-937468-X
这本是 C-Shell 的 Bible , 想学 C-Shell 的人 , 可以去看这本书 .
?
Title: Unix Shell Programming
Authors: Stephen Kochan and Patrick Wood
Publisher: Hayden
Edition: 1990
ISBN: 0-672-48448-X
喔 ! 这本书以 Bourne Shell 为主 , 内容深入浅出 , 读者很容易就可以了解
这本书的内容 , 进而掌握 Bourne Shell 的精髓 . 此外 , 这本书也有提到
Korn Shell , 大体上来说 , 是一本值得看的好书 .
?
*如何联系作者 :
E-Mail Address :
[email protected]
[email protected]
Dormitory : 交通大学十舍 317R
..
?
?
Linux 开机程序之研讨
CCCA 资工86 许景华
在上次的介绍中 , 我们已经看完了 rc.S 及 rc.serial 这两个 shell script .
现在 , 我们将把剩下的 shell script 再作一个介绍 .
首先还是看看全部的流程 :
?
init[1]
rc.S begin
rc.serial begin
rc.serial end
rc.S end <-- 上一次我们说明到这里
init[1] enter runlevel 5
rc.M begin
rc.inet1 begin
rc.inet1 end
rc.inet2 begin
rc.inet2 end
rc.font begin
rc.font end
rc.local begin
rc.local end
rc.M end
login
?
这次主要的部份可分为两项 : 因为 init 将会去读取 inittab , 所以 inittab
将被列为第一部份的重点 , 而第二部份就是 rc.M , rc.font , rc.local
这几个 shell script 的说明 . ( rc.inet1 , rc.inet2 这两个关于网络的
shell script 将在以后单独说明 )
好了 , 我们先从 inittab 看起吧 ! 看看上面的流程 , 在第一行 : init[1]
也就是 init 这个 process 被启动之后 , 它会去读取 /etc/inittab 这个文件
以完成系统的启动 . 从这里 , 我们看到了 LINUX 的确融合了 SVR4 及 SunOS
的一些特性 , inittab 这个文件 , 在 SunOS 系统中是不存在的 , 但是它却是
SVR4 典型的文件 . init 这个 process 会依据 /etc/inittab 中所记载的内容
进入不同的 run-level 并启动不同的 process . 所以 inittab 的重要性
可见一斑 . 那什么叫 run-level 呢 ? 所谓 run-level 就是系统中定义了许多
不同的 level , 而系统会随著 level 的不同而去启动不同的资源 .
现在就让我们来看一下 /etc/inittab 中的内容 :
在 /etc/inittab 这个文件中 , 每一列是一个进入点 , 假如我们仔细观察每一列
的话 , 那我们就会很容易的发现 , /etc/inittab 的每一列可以被 " : " 这个
字符分成好几个栏位 . 这几个栏位的格式如下 :
id:runlevels:action:process
而它们代表的意义分别如下 :
id : 由两个独特的字符所组成的辨示符号 , 在比较新的 UNIX 系统中 , 已不
受只能有两个字符的限制 .
runlevels : 指出下面一个栏位中的 action 以及 下下个栏位中的 process
会在那些 runlevel 中被执行 . 这一栏的合法值有 0,1,2...6
s 以及 S . 而在正常的启动程序之后 , Superuser 可以使用
telinit 这个指令来改变系统的 runlevel . 又因为在 LINUX 中
, runlevel 的预设值是 5 ( 等一下就会看到 ) 所以 , 只有
那些每一列中 runlevel 那栏有 5 这个值的 , 后面的 process
才会被启动 . 所以 , 我们就可以想像的到 : " 由于系统的
runlevel 不同 , 所起动的 process 也不尽相同 , 所以系统
起动的资源在每个不同的 runlevel 就会有差异存在 .
action : 这个栏位有一点复杂 , 在这个栏位中记录着 init 在启动相对应的
process 时 , 对 process 所采取的动作 , 而合法的动作有下面几项:
initdefault : 指出系统在启动时 , 预设进入的 run-level 值 ,
比如说 , 我们可以在 /etc/inittab 中找到下面这
一列 : id:3:initdefault:
很明显的 , 系统将在启动时 , 进入 runlevel 为 3
的模式 . 当然 , 你可以把 3 改成 4 试试看 , 如
果你改成了 4 , 那将会执行 /etc/rc.d/rc.4 , 也
就是 run xdm . xdm 在以后有机会的话 , 将为各
位介绍 ......
sysinit : 在系统起动时 , 这个 process 会被执行 . 而所有 process
前的 action 中有 boot 及 bootwait 的 process , 必
须等到这些 action 为 sysinit 的 process 执行完之后
它们才能够执行 .
wait : 在起动一个 process 之后 , 若要再起动另一个 process , 则
必需等到这个 process 结束之后才能继续 .
respawn : 代表这个 process 即使在结束之后 , 也会重新被启动 ,
最典型的例子就是 getty ( 在 LINUX 中为 agetty ) .
看看下面的循环 :
-- getty -->; login -->; shell -->; logout --
^ |??????????????????????????????????????? |
? |<---------------------------------------|
即使在 getty 结束之后 , 它也会重新被启动 .
ctrlaltdel : 想必有人会以键盘上的 Ctrl , Alt , 及 Del 这三个
键来达到使系统 shutdown 的目的 , 现在我们果然在
/etc/inittab 中看到了这一列 :
ca::ctrlaltdel:/sbin/shutdown -t3 -rf now
所以说 , 当我们按下这三个键的时候 , init 会收到
SIGINT 这个 signal , 接着就执行 shutdown 的动作
不过 , 我们最好不要养成按 Ctrl-Alt-Del 来使系统
shutdown 的习惯 , 尤其在单用户多任务的操作系统 , 像
OS/2 , 甚至 Windows 95 , shutdown 几乎都是标准
的关机程序了 , 更何况是多用户多任务的 LINUX , 所以 ,
shutdown 这个指令是一定要熟悉的 .
除了上面的几个 action 之外 , 另外还有一些合法的 action , 但这
些 action 并不需要太注意 , 要用的时候再利用 man init 去查询就
可以了 .
process : 这一栏中可以是 shell script 或是可执行的程序 .
好了 , 当我们了解 /etc/inittab 中每一栏的意义之后 , 要看懂 /etc/inittab
就是一件轻松愉快的工作了 . 在 /etc/inittab 档中 , 我们可以看到下面这一段
c1:12345:respawn:/sbin/agetty 38400 tty1
c2:12345:respawn:/sbin/agetty 38400 tty2
c3:45:respawn:/sbin/agetty 38400 tty3
c4:45:respawn:/sbin/agetty 38400 tty4
c5:45:respawn:/sbin/agetty 38400 tty5
c6:456:respawn:/sbin/agetty 38400 tty6
简单来说 , 系统在起动之后会制造出六个虚拟的 console . 我想大家应该有试过
用 Ctrl-Alt + F1 - F6 可在这六个 console 之间切换 ; 若你使用 XWindows 时
想暂时回到 console 下时 , 可用 Ctrl-Alt + F1 - F6 这三个键来选择 , 若想
回到 XWindows 下时 , 只要以 Ctrl-Alt-F7 就可以回到 XWindows 下了 . 基本
上 , 对於 memory 比较少的人 , 可以不要开那么多的虚拟 console , 那么就可
以去掉上面的几列 . 还有 , 在前面我们也提过 , 可以把预设的 runlevel 从 5
改成 6 , 对於 beginner 来说 , 系统一启动完就直接进入 XWindows 也许是一个
不错的设定方法 ......
介绍完 /etc/inittab 之后 , 我们接着看 rc.M ! 由前面的流程当中 , 我们看到
rc.M 中又包含了四个 shell script , 其中 rc.inet1 及 rc.inet2 是有关于网络
的设定 ; rc.font 是作字体的设定 , 而 rc.local 中可以放一些想要起动的
daemon .
我们现在就来看看 rc.M , 依照往例 , 前面有两个 "#" 的为加上去的注释 .
只有一个 "#" 的为原来的注释 :
#!/bin/sh
#
# rc.M This file is executed by init(8) when the system is being
# initialized for one of the "multi user" run levels (i.e.
# levels 1 through 6). It usually does mounting of file
# systems et al.
#
# Version: @(#)/etc/rc.d/rc.M 2.02 02/26/93
#
# Author: Fred N. van Kempen,
# Heavily modified by Patrick Volkerding
#
## 显示进入多用户模式
echo "Going multiuser..."
?
## 下面一列的意思是 : 假如你在文字模式的 console 下 , 在15分钟内都没有动作
## 的话 , 屏幕就会自动暗下来 , 简单的说 , 就是 screen saver 的功能 .
/bin/setterm -blank 15
?
## 执行 crond 这个 daemon . 不用说 , crond 在系统中扮演了很重要的角色 ,
## 它负责每过一段时间后 , 就去看看 /var/spool/cron/crontabs 中有那些 file
## 要运行 , 这些 file 往往有一个固定的时间 , 比如说 : 每个月的 1 号 , 每
## 天凌晨等 ...... 我们可以用平常的编辑器编好一个文件 , 里面的格式如下 :
##
## 分 时 日 月 星期 命令
##
## 举例来说 , 59 23 31 12 * /etc/wall happy_new_year
## 在每年的 12 月 31 号晚上 11 点 59 分 会对每个系统上的 user 送出
## happy_new_year 中的内容
##
## 接着我们可以利用 crontab <文件名>; 这个指令来把此文件放到
## /var/spool/cron/crontabs中. 我们可以看看 /var/spool/cron/crontab 下
## 有一个 root 的文件 , 看看里面的内容 :
##
## 0,5,10,15,20,25,30,35,40,45,50,55 * * * * /usr/lib/atrun
##
## 所以各位看到了 , 在前两期提到的 at 命令是五分钟才被 run 一次的
##
## 再举一个简单的例子好了 : 我们先用一般的文本编辑器造出一个名为 crontest
## 的档案 , 内容如下 :
##
## 5 * * * * ls -la ~/ >;>; ~/hehehaha
##
## 接着 , 我们键入下面的命令 : crontab crontest
## 此时 , 从内容得知 , 每五分钟 crond 就会执行 ls -la , 把你 home directory
## 的内容加入 hehehaha 这个文件中 .
##
## 当然啦 ! 这个例子简直是毫无意义可言 :) 但是 , 大家既然知道了基本原理 ,
## 利用 crontab , at 这些指令 , 就可以简化一些系统管理的动作 , 同时在执行
## 一些工作时 , 也会比较有弹性 .
/usr/sbin/crond -l10 >;>;/var/adm/cron 2>;&1
?
## 假如 /etc/HOSTNAME 不能读取的话 , 就把 darkstar.frop.org 当成 HOSTNAME
## 中的内容 . 老实说 , 下面这三列去掉也不打紧 ......
if [ ! -r /etc/HOSTNAME ]; then
echo "darkstar.frop.org" >; /etc/HOSTNAME
fi
?
## 下面从 if 到 fi 夹起来的部份 , 主要就是在执行 rc.inet1 , rc.inet2 . 这
## 些都是网络设定的工作 , 尤其是 rc.inet2 , 启动了一大堆 daemon , 这部份
## 要牵扯到的东西太多了 . 像 subnet 与 netmask 等 ...... 类似这种观念 ,
## 都不是三言两语就可以玩完的 , 所以就留待以后再说 .
if [ -x /etc/rc.d/rc.inet1 ];
then
/bin/hostname `cat /etc/HOSTNAME | cut -f1 -d .`
/bin/sh /etc/rc.d/rc.inet1
/bin/sh /etc/rc.d/rc.inet2
else
/sbin/hostname_notcp `cat /etc/HOSTNAME | cut -f1 -d .`
/usr/sbin/syslogd
/usr/sbin/klogd
/usr/sbin/lpd
fi
?
## 在某些资源独占的情况下 , 一些应用程序往往会制造出 lock 文件 . 假如这些
## lock 文件在重新开机以后还是存在的话 , 那就很不好了 . 所以 , 下面就是在
## 作这些删除 lock 文件的动作 , 并把一些输出的信息丢到 /dev/null 去 .
## 在上一期的内容中 , 我们就有提到 /dev/null 了 , 也有提到抑制信息输出的
## 方法 . 现在我们果然看到了一个实例 ......
/bin/rm -f /var/spool/locks/* /var/spool/uucp/LCK..* /tmp/.X*lock 1>; /dev/null 2>; /dev/null
?
## 假如你有玩 hunt 这个 game 的话 , 那在 /tmp 下会有一个 socket 类型的文件
## 我们要把它删除之后才能开始另一个 game ......
if [ -r /tmp/hunt -o -r /tmp/hunt.stats ]; then
echo "Removing your stale hunt sockets from /tmp..."
/bin/rm -f /tmp/hunt*
fi
?
## 设定 share library 的 link 及 cache . 这个指令只有 Superuser 才能使用
## 的 , 它也相当的重要 . 万一你的 /etc/ld.so.cache 很不幸的 corrupt 了 ,
## 那我们也可以利用这个指令来让它重新 link , 先删除 /etc/ld.so.cache ,
## 再以 ldconfig -v 重新制造就可以了 .
/sbin/ldconfig
?
## 起动 sendmail daemon , 并且让它 15 分钟就去看一看 spool , 处理收发信件
if [ -x /usr/sbin/sendmail ]; then
echo "Starting sendmail daemon (/usr/sbin/sendmail -bd -q 15m)..."
/usr/sbin/sendmail -bd -q 15m
fi
?
## 假如 /etc/rc.d/rc.font 是可读的话 , 就执行 rc.font 这个 shell script ,
## 而这个 shell script 主要是设定 text mode 下屏幕的字体
if [ -r /etc/rc.d/rc.font ]; then
/etc/rc.d/rc.font
fi
?
## 在系统管理中 , 我们常常把一些 local 的东西另外放在一个地方 , 这样才不
## 会与原来的东西混淆 . 同时 , 因为 local 的东西更新版本的速度总是也比较
## 快 , 在这种情况下 , 常常会变动的东西也可以放在 local 的区域中 , 这样
## 管理起来比较方便 . 也许各位也注意到了 : 为什么会有 /usr/bin 及
## /usr/local/bin 之分 . 就个人认为 , 像自己 compile 出来的东西 , 假如
## 觉得还不错 , 就可以把它放在 /usr/local/bin , 因为它是新增的 , 所以我
## 把它放在 /usr/local/bin . 当然啦 , 这只是个人喜好罢了 , 你要放那里
## 都是可以的 , 只要找得到 , 易于使用及管理就好 .
## 同样的 , 若我们要起动一些新增的 daemon 或 shell script , 那放在
## 是不错的选择 .
## 下面一列就是去执行 rc.local 中的设定 , 通常是一些 daemon 或是 shell
## script
/etc/rc.d/rc.local
?
# All done.
?
到这里 , rc.M 已经结束了 , 我们来看看从 rc.M 之中执行的 rc.font 及
rc.local ......
下面是 rc.font 的内容 :
#!/bin/sh
#
# This selects your default screen font from among the ones in
# /usr/lib/kbd/consolefonts.
#
## 我想下面这一列的命令非常明显了 , 就是设定 console 中的字体 , 你可以
## 改成自己喜欢的字体 . 或者你也可以利用 fontconfig 这个指令来改变 .
setfont /usr/lib/kbd/consolefonts/default8x16
?
?
看完了 rc.font 后 , 我们来看看 rc.local 的内容 . 我所要说的是 : rc.local
毕竟是自己设定的区域 , 所以每个人的可能都不一样 , 就我而言 , 因为我多 run
了一些 daemon , 所以与大家的可能不太相同 . 所以 , rc.local 作参考就可以了.
下面是我的 rc.local :
#! /bin/sh
# Put any local setup commands in here
# Running selection
?
## lpd 是控制打印机的 daemon , 要想在 LINUX 下用打印机 , 这个 daemon 必需
## 要被起动 , 此外还要修改 /etc/printcap . 详细的情况要去看 PRINT-HOWTO
echo -n "lpd"
/etc/lpd
?
## httpd 就是 WWW server 的 daemon . 想必大家都用过 Mosaic , Netscape 等
## 的浏览器 . 但假如我们想建立自己的 WWW server , httpd 必须要执行 .
echo -n " httpd"
/usr/local/etc/httpd/src/httpd
?
## 在 WWW 的时代还没来临以前 , gopher 可说是具有最方便的信息索引功能 , 即使
## 到了现在 , gopher 仍然占有一席之地 , 在这里 , 因为我有建立自己的 gopher
## server , 所以 gopherd 必需被起动 .
echo -n " gopherd"
/usr/local/sbin/gopherd -u nobody
?
## 下面这个指令是 mouse 在 console 下做 cut & paste
echo -n "Running selection..."
selection -t ms &
echo ' '