第 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. 每个 init 脚本默认的参数
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” 以获取确切的解释。

例如,让我们来建立如下类似 Red Hat Linux 的运行级别系统。

  • init 默认以 runlevel=3 启动系统。

  • init 不会在 runlevel=(0,1,2,6) 时启动 gdm3(1)

  • init 会在 runlevel=(3,4,5) 时启动 gdm3(1)

可以通过修改 "/etc/inittab" 文件来改变启动等级并且也可以使用用户友好的运行级别管理工具例如 sysv-rc-conf 或者 bum 来更改运行级别。如果你只想用命令行方式的话,如下是你应该怎样去做的步骤(默认安装 gdm 软件包后,在显示器管理工具中选中它)。

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

请注意当启动显示器管理进程时:xdm,gdm3,sddmwdm,"/etc/X11/default-display-manager" 文件会被检查。

[注意] 注意

你也可以在任何控制台界面用 startx(1) 命令启动 X。

"/etc/init.d" 中的每个 init 脚本的默认参数由 "etc/default" 中的对应文件给出,此对应文件包含环境变量分配。这些目录是 Debian 系统特定的,它们大致相当于 Red Hat Linux 和其他发行版中的 "/etc/sysconfig" 目录。例如,“/etc/default/cron" 可以控制 "/etc/init.d/cron" 的运行方式。

"/etc/default/rcS"文件可用于定制启动时默认的 motd(5), sulogin(8), 等.

如果通过改变这些变量不能达到你所想要的效果,那么你应该修改 init 脚本。如下是一些系统管理员可以编辑的配置文件。

许多网络服务(参考第 6 章 网络应用)在启动多用户模式的时候通过 init 脚本直接作为后台守护进程启动,例如 “/etc/rc2.d/S20exim4”(运行级别为2)有一个指向 "/etc/init.d/exim4" 的符号链接。

一些网络服务能够使用 super-server inetd (或者它的等价物) 启动。在启动的时候,inetd 通过链接到 "/etc/init.d/inetd" 的 "/etc/rc2.d/S20inetd" 启动 (运行等级为 2)。根本上,inetd 允许一个正在运行的守护进程去调用其它进程,减轻了系统的负载。

任何时候一个服务请求到达 super-server inetd , 通过在"/etc/protocols"和"/etc/services"里面查找数据库,该请求的协议和服务就会被辨识出来。inetd在"/etc/inetd.conf"数据库里查找一个通用的互联网服务,或者在"/etc/rpc.conf"里面找到一个基于开放网络计算远程过程调用(ONC RPC)/Sun RPC 的服务。

有些情况下inetd 并不直接启动对应的服务,而是以"/etc/inetd.conf"中对应服务作为参数启动TCP wrapper程序tcpd(8) . 在这种情况下, tcpd会根据"/etc/hosts.deny"和"/etc/hosts.allow"进行额外检查后,启动相应的服务程序.

为了系统安全,应该尽可能的关掉网络服务程序。参考第 4.6.4 节 “限制访问某些服务端的服务”

参考 inetd(8), inetd.conf(5), protocols(5), services(5), tcpd(8), hosts_access(5), host_options(5), rpcinfo(8), portmap(8), 和 "/usr/share/doc/portmap/portmapper.txt.gz"。

Linux 内核 2.6 和更新的内核,udev 系统 提供了自动硬件发现和初始化机制。(参见 udev(7)).在内核发现每个设备的基础上,udev 系统使用从 sysfs 文件系统 (参见 第 1.2.12 节 “procfs 和 sysfs”)的信息启动一个用户进程,使用 modprobe(8) 程序 (参见 第 3.3.1 节 “内核模块初始化”)加载支持它所要求的内核模块, 创建相应的设备节点。

[提示] 提示

如果由于某些理由,"/lib/modules/<kernel-version>/modules.dep"没有被 depmod(8) 正常生成,模块可能不会被 udev 系统按期望的方式加载。执行"depmod -a" 来修复它。

设备节点的名字,可以通过"/etc/udev/rules.d/"里的 udev 文件来配置.当前默认的规则倾向创建动态生成的名字,除了光驱和网络设备外,会生成非静态的设备名。通过添加和光驱、网络设备类似的个性化规则,你也可以为 USB 盘之类的其它设备,生成静态设备名。 参见 "Writing udev rules" 或 "/usr/share/doc/udev/writing_udev_rules/index.html".

由于 udev 系统是一个正在变化的事物,我在其它文档进行了详细描述,在这里只提供了最少的信息。

[提示] 提示

"/etc/fstab"里面的挂载规则,设备节点不必需是静态的。你能够使用 UUID 来挂载设备,来代替"/dev/sda"之类的设备名. 参见 第 9.5.3 节 “使用 UUID 访问分区”.

通过 modprobe(8) 程序添加和删除内核模块,使我们能够从用户进程来配置正在运行的 Linux 内核。udev 系统(参见 第 3.3 节 “udev 系统”)自动化它的调用来帮助内核模块初始化。

下面的非硬件模块和特殊的硬件驱动模块,需要被预先加载,把它们在"/etc/modules"文件里列出 (参见 modules(5)).

modprobe(8) 程序的配置文件是按 modprobe.conf(5)的说明放在"/etc/modprobes.d/" 目录下,(如果你想避免自动加载某些内核模块,考虑把它们作为黑名单放在"/etc/modprobes.d/blacklist" 文件里.)

"/lib/modules/<version>/modules.dep" 文件由 depmod(8) 程序生成,它描述了 modprobe(8) 程序使用的模块依赖性.

[注意] 注意

如果你在启动时出现模块加载问题,或者 modprobe(8)时出现模块加载问题, "depmod -a" 可以通过重构"modules.dep"来解决这些问题。

modinfo(8) 程序显示 Linux 内核模块信息。

lsmod(8) 程序以好看的格式展示"/proc/modules"的内容,显示当前内核加载了哪些模块。

[提示] 提示

你能够精确识别你系统上的硬件。 参见第 9.4.3 节 “硬件识别”.

[提示] 提示

你可以在启动时配置硬件来激活期望的硬件特征。参见 第 9.4.4 节 “硬件配置”.

[提示] 提示

你可以重新编译内核来增加你的特殊设备的支持。参见 第 9.9 节 “内核”.