第 3 章 系统初始化

目录

3.1. 启动过程概述
3.1.1. 第一阶段:BIOS
3.1.2. 第二阶段:引载加载程序
3.1.3. 第三阶段:迷你 Debian 系统
3.1.4. 第四阶段:常规 Debian 系统
3.2. SysV 风格的 init
3.2.1. 运行级别的含义
3.2.2. 运行级别的配置
3.2.3. 运行级别管理示例
3.2.4. The default parameter for each init script
3.2.5. 主机名
3.2.6. 文件系统
3.2.7. 网络接口初始化
3.2.8. 网络服务初始化
3.2.9. 系统信息
3.2.10. 内核信息
3.3. udev 系统
3.3.1. 内核模块初始化

作为系统管理员,粗略地了解 Debian 系统的启动和配置方式是明智的。尽管准确的细节在安装的软件包及对应的文档中,但这些知识对我们大多数人来说都是必须掌握的。

笔者基于自己和其他人的过往及现在的知识,尽己所能地提供关于 Debian 系统的知识要点及其配置的快速概览作为读者的参考。由于 Debian 系统在不断地更新中,系统的状况可能已经有所变化。在对系统做任何修改之前,请参考各个软件包的最新文档。

[警告] 警告

本章是基于 2013 年发布的 Debian 7.0 (Wheezy) 编写的,所以其内容正在变得过时。

计算机系统从上电事件到能为用户提供完整的操作系统(OS)功能为止,需要经历几个阶段的启动过程

为简便起见,笔者将讨论范围限定在具有默认安装的典型 PC 平台上。

典型的启动过程像是一个四级的火箭。每一级火箭将系统控制权交给下一级。

当然,这些阶段可以有不同的配置。比如,你编译了自己的内核,则可能会跳过迷你 Debian 系统的步骤。因此,在读者亲自确认之前,请勿假定自己系统的情况也是如此。

[注意] 注意

对于 SUN 或 Macintosh 系统等非传统 PC 平台来说,ROM 上的 BIOS 及磁盘上的分区可能大不相同(第 9.5.2 节 “硬盘分区配置”)。对于这种情况,请另寻对应平台相关的文档。

BIOS 是启动过程的第一阶段,在上电事件后开始。CPU 的程序计数器在上电事件后被初始化为一个特定的内存地址,驻留在只读存储器(ROM)中的 BIOS 就是从这个特定的内存地址开始执行。

BIOS 执行硬件的基本初始化(POST: 上电自检)并将系统控制权交给你指定的下一步骤。BIOS 通常和硬件一同提供。

BIOS 启动屏幕通常指示了进入 BIOS 配置界面所需的按键。流行的按键是 F1、F2、F10、Esc、Ins 和 Del 键。假如你的启动屏幕被一个漂亮的图形界面隐藏,你可以按下某些按键(比如 ESC)取消隐藏。这些按键高度依赖于硬件。

硬件位置和 BIOS 启动的代码的优先级可以在 BIOS 配置界面中选择。通常,在已选择的设备(硬盘、软件、CD-ROM……)中,最先找到的设备的最开始的几个扇区将被加载到内存,并执行其中的初始化代码。初始化代码可以是以下任意一种。

  • 引导加载代码

  • 类似 FreeDOS 这样的过滤型操作系统的内核代码

  • 能够加载到如此小的空间中的目标操作系统的内核代码

通常,系统从主硬件的特定分区中引导。传统 PC 硬盘的最开始两个扇区中包含了主引导记录(MBR)。在 MBR 的末尾记录了磁盘分区信息及引导选择。BIOS 中执行的首段引导加载代码占据了 MBR 的其余部分。

引导加载程序是启动过程的第二阶段,由 BIOS 启动。引导加载程序将系统内核映像和 initrd 映像加载到内存并将控制权交给它们。initrd 映像是根文件系统映像,其支持程度依赖于所使用的引导加载程序。

Debian 系统通常使用 Linux 内核作为其默认的系统内核。当前 2.6/3.x 版本 Linux 内核的 initrd 映像从技术上说是 initramfs(初始化 RAM 文件系统)映像。initramfs 映像是根文件系统中所有文件的 cpio 归档再经过 gzip 压缩得到。

[警告] 警告

使用新的 multi-segment initramfs 之后,上述内容已不正确。请参见错误 #790100

Debian 系统默认将 PC 平台的 GRUB 引导加载程序的第一阶段代码安装在 MBR 中。可用的引导加载程序和配置选项如下。


[警告] 警告

假如没有从 grub-rescue-pc 软件包中的映像制作出来的可引导修复盘(CD 或软盘)在手边,请勿把玩引导加载程序。即使硬盘上没有可正常工作的引导加载程序,可引导修复盘也能引导你的系统。

传统 GRUB 的菜单配置文件位于 /boot/grub/menu.lst。例如,文件中有如下的配置条目。

title           Debian GNU/Linux
root            (hd0,2)
kernel          /vmlinuz root=/dev/hda3 ro
initrd          /initrd.img

GRUB 第 2 版的菜单配置文件位于 /boot/grub/grub.cfg。此文件由 /usr/sbin/update-grub 根据 "/etc/grub.d/*" 中的模板及 "/etc/default/grub" 中的设置自动生成。例如,文件中有如下的配置条目。

menuentry "Debian GNU/Linux" {
        set root=(hd0,3)
        linux /vmlinuz root=/dev/hda3
        initrd /initrd.img
}

这些示例中,GRUB 参数的含义如下。


[注意] 注意

传统 GRUB 使用的分区号为 Linux 内核及各种实用工具使用的分区号减 1。GRUB 第 2 版修复了这个问题。

[提示] 提示

在标识一个块设备时,可能需要使用 UUID(参见第 9.5.3 节 “使用 UUID 访问分区”)而不是类似 "/dev/hda3" 这样的文件名,例如 "root=UUID=81b289d5-4341-4003-9602-e254a17ac232 ro"。

[提示] 提示

如果使用了 GRUB,内核的启动参数可以在 /boot/grub/grub.cfg 里面设置。在 Debian 系统里,你不应该直接编辑 /boot/grub/grub.cfg。你可以通过编辑 /etc/default/grub 文件中 GRUB_CMDLINE_LINUX_DEFAULT 的值并运行 update-grub(8) 来更新 /boot/grub/grub.cfg

[提示] 提示

通过使用链式引导技术,你可以在一个引导装载程序中启动另一个引导装载程序。

参见 “info grub” 及 grub-install(8)

常规 Debian 系统是启动流程的第四阶段,由迷你 Debian 系统启动。迷你 Debian 系统的内核在此环境下继续运行。根文件系统将由内存切换到实际的硬盘文件系统上。

init 程序是系统执行的第一个程序(PID=1),它启动其它各种程序以完成主引导流程。init 程序的默认路径是 ”/sbin/init“,但可通过内核启动参数修改,例如 ”init=/path/to/init_program"。

默认的 init 程序一直在变化中:

  • squeeze 之前的 Debian,使用简单的 SysV 风格的 init。

  • wheezy 版本的 Debian 对 SysV 风格的 init 做了改进:使用 LSB 头将启动步骤排序,同时并行执行启动脚本。

  • jessie版本的 Debian 将默认 init 切换成 systemd,以使用事件驱动和并行初始化。

[提示] 提示

"/etc/init.d/rc"、“/etc/init.d/rcS"、“/usr/sbin/update-rc.d” 及 “/usr/sbin/invoke-rc.d” 脚本中的启动机制都是相互兼容的。

[提示] 提示

你的系统中实际使用的 init 命令可以使用 “ps --pid 1 -f” 命令确认。


[提示] 提示

有关启动流程加速的最新信息,请参见 Debian 维基:启动流程加速词条。

[小心] 小心

当前默认的 Debian 系统已不使用 SysV 风格的 init。请阅读其它资源以获取关于现代的基于 systemd 的 init。参见 Debian 管理员手册

本章节描述优秀而老式的 SysV 风格的 init 是如何引导系统的。你的 Debian 系统的运作方式并不与此处描述的内容完全相同,但了解这些基础内容颇具教育意义,因为更新的 init 系统倾向于提供相同的功能。

SysV 风格的启动流程本质上经历了以下几个阶段。

  1. Debian 系统进入运行级别 N(无)并根据 “/etc/inittab” 的描述初始化系统。

  2. Debian 系统进入运行级别 S 并在单用户模式下完成系统的硬件初始化等等。

  3. Debian 系统进入某个指定的多用户运行级别(2 到 5)并启动各个系统服务。

多用户模式的初始运行级别,可通过内核启动参数 “init=” 指定,或在 “/etc/inittab” 中的 "initdefault" 行指定。已安装的 Debian 系统以运行级别 2 启动。

init 系统实际执行的所有脚本文件都在 “/etc/init.d/” 目录中。

参见 init(8)inittab(5) 及 “/usr/share/doc/sysv-rc/README.runlevels.gz” 以获取确切的解释。

For example, let's set up runlevel system somewhat like Red Hat Linux as the following.

  • init starts the system in runlevel=3 as the default.

  • init does not start gdm3(1) in runlevel=(0,1,2,6).

  • init starts gdm3(1) in runlevel=(3,4,5).

This can be done by using editor on the "/etc/inittab" file to change starting runlevel and using user friendly runlevel management tools such as sysv-rc-conf or bum to edit the runlevel. If you are to use command line only instead, here is how you do it (after the default installation of the gdm3 package and selecting it to be the choice of display manager).

# cd /etc/rc2.d ; mv S21gdm3 K21gdm3
# cd /etc ; perl -i -p -e 's/^id:.:/id:3:/' inittab

Please note the "/etc/X11/default-display-manager" file is checked when starting the display manager daemons: xdm, gdm3, sddm, and wdm.

[注意] 注意

You can still start X from any console shell with the startx(1) command.

The default parameter for each init script in "/etc/init.d/" is given by the corresponding file in "/etc/default/" which contains environment variable assignments only. This choice of directory name is specific to the Debian system. It is roughly the equivalent of the "/etc/sysconfig" directory found in Red Hat Linux and other distributions. For example, "/etc/default/cron" can be used to control how "/etc/init.d/cron" works.

The "/etc/default/rcS" file can be used to customize boot-time defaults for motd(5), sulogin(8), etc.

If you cannot get the behavior you want by changing such variables then you may modify the init scripts themselves. These are configuration files editable by system administrators.

Many network services (see 第 6 章 网络应用) are started under multi-user mode directly as daemon processes at boot time by the init script, e.g., "/etc/rc2.d/S20exim4" (for RUNLEVEL=2) which is a symlink to "/etc/init.d/exim4".

Some network services can be started on demand using the super-server inetd (or its equivalents). The inetd is started at boot time by "/etc/rc2.d/S20inetd" (for RUNLEVEL=2) which is a symlink to "/etc/init.d/inetd". Essentially, inetd allows one running daemon to invoke several others, reducing load on the system.

Whenever a request for service arrives at super-server inetd , its protocol and service are identified by looking them up in the databases in "/etc/protocols" and "/etc/services". inetd then looks up a normal Internet service in the "/etc/inetd.conf" database, or a Open Network Computing Remote Procedure Call (ONC RPC)/Sun RPC based service in "/etc/rpc.conf".

Sometimes, inetd does not start the intended server directly but starts the TCP wrapper program, tcpd(8), with the intended server name as its argument in "/etc/inetd.conf". In this case, tcpd runs the appropriate server program after logging the request and doing some additional checks using "/etc/hosts.deny" and "/etc/hosts.allow".

For system security, disable as much network service programs as possible. See 第 4.6.4 节 “Restricting access to some server services”.

See inetd(8), inetd.conf(5), protocols(5), services(5), tcpd(8), hosts_access(5), hosts_options(5), rpcinfo(8), portmap(8), and "/usr/share/doc/portmap/portmapper.txt.gz".

For Linux kernel 2.6 and newer, the udev system provides mechanism for the automatic hardware discovery and initialization (see udev(7)). Upon discovery of each device by the kernel, the udev system starts a user process which uses information from the sysfs filesystem (see 第 1.2.12 节 “procfs 和 sysfs”), loads required kernel modules supporting it using the modprobe(8) program (see 第 3.3.1 节 “内核模块初始化”), and creates corresponding device nodes.

[提示] 提示

If "/lib/modules/<kernel-version>/modules.dep" was not generated properly by depmod(8) for some reason, modules may not be loaded as expected by the udev system. Execute "depmod -a" to fix it.

The name of device nodes can be configured by udev rule files in "/etc/udev/rules.d/". Current default rules tend to create dynamically generated names resulting non-static device names except for cd and network devices. By adding your custom rules similar to what cd and network devices do, you can generate static device names for other devices such as USB memory sticks, too. See "Writing udev rules" or "/usr/share/doc/udev/writing_udev_rules/index.html".

Since the udev system is somewhat a moving target, I leave details to other documentations and describe the minimum information here.

[提示] 提示

For mounting rules in "/etc/fstab", device nodes do not need to be static ones. You can use UUID to mount devices instead of device names such as "/dev/sda". See 第 9.5.3 节 “使用 UUID 访问分区”.

The modprobe(8) program enables us to configure running Linux kernel from user process by adding and removing kernel modules. The udev system (see 第 3.3 节 “udev 系统”) automates its invocation to help the kernel module initialization.

There are non-hardware modules and special hardware driver modules as the following which need to be pre-loaded by listing them in the "/etc/modules" file (see modules(5)).

The configuration files for the modprobe(8) program are located under the "/etc/modprobes.d/" directory as explained in modprobe.conf(5). (If you want to avoid some kernel modules to be auto-loaded, consider to blacklist them in the "/etc/modprobes.d/blacklist" file.)

The "/lib/modules/<version>/modules.dep" file generated by the depmod(8) program describes module dependencies used by the modprobe(8) program.

[注意] 注意

If you experience module loading issues with boot time module loading or with modprobe(8), "depmod -a" may resolve these issues by reconstructing "modules.dep".

The modinfo(8) program shows information about a Linux kernel module.

The lsmod(8) program nicely formats the contents of the "/proc/modules", showing what kernel modules are currently loaded.

[提示] 提示

You can identify exact hardware on your system. See 第 9.4.3 节 “硬件识别”.

[提示] 提示

You may configure hardware at boot time to activate expected hardware features. See 第 9.4.4 节 “硬件配置”.

[提示] 提示

You can probably add support for your special device by recompiling the kernel. See 第 9.9 节 “The kernel”.