1 启动过程概述
1.1 第一阶段:BIOS
BIOS是启动过程的第一阶段,在上电事件后开始。CPU的程序计数器在上电事件后被初始化为一个特定的内存地址,驻留在只读存储器(ROM)中的BIOS就是从这个特定的内存地址开始执行。
BIOS执行硬件的基本初始化(POST:上电自检)并将系统控制权交给你指定的下一步骤。BIOS通常和硬件一同提供。
硬件位置和BIOS启动的代码的优先级可以在BIOS配置界面中选择。通常,在已选择的设备(硬盘、软件、CD-ROM......)中,最先找到的设备的最开始的几个扇区将被加载到内存,并执行其中的初始化代码。初始化代码可以是以下任意一种。
- 引导加载代码
- 类似FreeDOS这样的过滤型操作系统的内核代码
- 能够加载到如此小的空间中的目标操作系统的内核代码
通常,系统从主硬件的特定分区中引导。传统PC硬盘的最开始两个扇区中包含了主引导记录(MBR)。在MBR的末尾记录了磁盘分区信息及引导选择。BIOS中执行的首段引导加载代码占据了MBR的其余部分。
1.2 第二阶段:引载加载程序
由BIOS启动,将系统内核映像和initrd映像加载到内存并将控制权交给它们。initrd映像是根文件系统映像,其支持程度依赖于所使用的引导加载程序。
当前2.6/3.x版本Linux内核的initrd镜像从技术上说是initramfs(初始化RAM 文件系统)镜像。基本的initrd镜像是root文件系统中各个文件使用cpio压缩得到的。内核可以在启动流程中非常早的阶段,在加载基本的initrd镜像之前即更新微码。以未压缩cpio格式存储微码二进制文件的initrd镜像和基本initrd镜像两部分可以联合组成一个initrd镜像,从而帮助实现上述功能。
Debian系统默认将PC 平台的GRUB引导加载程序的第一阶段代码安装在MBR 中。
传统GRUB的菜单配置文件位于/boot/grub/menu.lst
。例如,文件中有如下的配置条目:
title Debian GNU/Linux
root (hd0,2)
kernel /vmlinuz root=/dev/hda3 ro
initrd /initrd.img
在debian10和11中均为找到上述文件。
GRUB第2版的菜单配置文件位于/boot/grub/grub.cfg。此文件由/usr/sbin/update-grub根据"/etc/grub.d/*"中的模板及"/etc/default/grub"中的设置自动生成。例如,文件中有如下的配置条目。
软件包 | initrd | 引导加载程序 | 说明 |
---|---|---|---|
grub-legacy | 支持 | 传统GRUB | 可智能识别磁盘分区和文件系统(如vfat、ext3......) |
grub-pc | 支持 | GRUB第2版 | 可智能识别磁盘分区和文件系统(如vfat、ext3......)。默认安装 |
grub-rescue-pc | 支持 | GRUB第2版 | 此为GRUB第2版的可引导修复映像(CD和软盘)(PC/BIOS版本) |
lilo | 支持 | Lilo | 依赖于数据在硬盘上的扇区位置。(较老) |
syslinux | 支持 | Isolinux | 可识别ISO9660文件系统。引导CD使用此项。 |
syslinux | 支持 | Syslinux | 可识别MSDOS文件系统(FAT)。引导软盘使用此项。 |
loadlin | 支持 | Loadlin | 新系统从FreeDOS或MSDOS中启动。 |
mbr | 不支持 | NeilTurton的MBR | 此为取代MSDOS MBR的自由软件。只可识别硬盘分区。 |
menuentry "Debian GNU/Linux" {
set root=(hd0,3)
linux /vmlinuz root=/dev/hda3
initrd /initrd.img
}
条目的含义如下:
GRUB参数 | 说明 |
---|---|
root | 使用主磁盘的第3个分区,在传统GRUB中将此参数设置为"(hd0,2)",在GRUB第2版中将此参数设置为"(hd0,3)" |
kernel | 使用位于"/vmlinuz"的内核,同时将"root=/dev/hda3ro"作为参数传递给内核 |
initrd | 使用位于"/initrd.img"的initrd/initramfs映像 |
传统GRUB使用的分区号为Linux内核及各种实用工具使用的分区号减1。GRUB第2版修复了这个问题。
在标识一个块设备时,可能需要使用UUID,例如"root=UUID=81b289d5-4341-4003-9602-e254a17ac232 ro"。
1.3 第三阶段:mini-Debian系统
由引导加载程序启动。它会在内存中运行系统内核和根文件系统。这是启动流程的一个可选准备阶段。这个系统通常被称为initrd或initramfs系统。内存中类似的系统在Debian安装程序中使用。
/init程序是内存中的根文件系统上执行的第一个程序。这个程序在用户空间把内核初始化,并把控制权交给下一阶段。迷你Debian系统能够在主引导流程之前添加内核模块或以加密形式挂载根文件系统,使引导流程更加灵活。
- 如果initramfs 是由initramfs-tools 创建,则"/init" 程序是一个shell 脚本程序。
- 通过给内核添加"break=init" 等启动参数,实现中断这部分启动流程以获取root shell。这个shell环境已足够成熟,通过它可以很好地检查机器的硬件。
- 迷你Debian系统中可用的命令是精简过的,且主要由一个称为busybox的GNU工具提供。
- 如果initramfs 是由dracut 创建,则"/init" 程序是一个二进制systemd 程序。
- 迷你Debian系统中可用的命令是一个精简过的systemd环境。
1.4 第四阶段:常规Debian系统
由迷你Debian系统启动。迷你Debian系统的内核在此环境下继续运行。根文件系统将由内存切换到实际的硬盘文件系统上。
init程序是系统执行的第一个程序,它启动其它各种程序以完成主引导流程。init程序的默认路径是"/sbin/init",但可通过内核启动参数修改,例如"init=/path/to/init_program"。
默认的init程序一直在变化中:
- squeeze之前的Debian(Debian 6.0),使用简单的SysV风格的init。
- wheezy版本的Debian(Debian 7)对SysV风格的init做了改进:使用LSB头将启动步骤排序,同时并行执行启动脚本。
- jessie版本的Debian(Debian 8)将默认init切换成systemd,以使用事件驱动和并行初始化。
2 systemd初始化
systemd初始化进程基于单元配置文件来并行派生进程,这些单元配置文件使用声明样式来书写,代替之前的类SysV的过程样式。这些单元配置文件从下面的一系列路径来加载:
/lib/systemd/system
: OS默认配置文件/etc/systemd/system
: 系统管理员的配置文件,它将忽略操作系统默认的配置文件/run/systemd/system
: 运行时产生的配置文件,它将忽略安装的配置文件
他们的相互依赖关系通过"Wants=", "Requires=", "Before=", "After=", ...等指示来配置,里的"MAPPING OF UNIT PROPERTIES TO THEIR INVERSES"。资源控制也是被定义。根据单元配置文件的后缀来区分它们的类型:
*.service
描述由systemd控制和监管的进程.*.device
描述在sysfs里面作为udev设备树展示的设备。*.mount
描述由systemd控制和监管的文件系统挂载点。*.automount
描述由systemd控制和监管的文件系统自动挂载点。*.swap
描述由systemd控制和监管的swap文件或设备。*.path
描述被systemd监控的路径,用于基于路径的活动。*.socket
描述被systemd控制和监管的套接字,用于基于套接字的活动。*.timer
描述被systemd控制和监管的计时器,用于基于时间的活动。*.slice
管理cgroups的资源。*.scope
使用systemd的总线接口来程序化的创建,用以管理一系列系统进程。*.target
把其它单元配置文件分组,在启动的时候,来创建同步点。
系统启动时,systemd进程会尝试启动/lib/systemd/system/default.target
。首先,一些特殊的target单元,比如"local-fs.target"、"swap.target"和"cryptsetup.target" 会被引入以挂载文件系统。之后,其它target单元也会根据单元依赖关系而被引入。systemd提供向后兼容的功能。在/etc/init.d/rc.d/
里面的SysV风格的启动脚本仍然会被分析;telinit会被转换为systemd的单元活动请求。
2.1 主机名
内核维护系统主机名。在启动的时候,通过systemd-hostnamed.service启动的系统单位设置系统的主机名,此主机名保存在/etc/hostname
。这个文件应该只包含系统主机名,而不是全称域名。
2.2 文件系统
硬盘和网络文件系统的挂载选项可以在/etc/fstab
中设置
加密文件系统的配置设置在/etc/crypttab
中
软RAID的配置mdadm设置在/etc/mdadm/mdadm.conf
2.3 网络接口初始化
Debian系统中,网络接口通常由两个服务进行初始化:lo接口通常在"networking.service"处理,而其它接口则由"NetworkManager.service"处理。
2.4 内核消息
在控制台上显示的内核错误信息,能够通过设置他们的阈值水平来配置。
root@debian11:~# dmesg -n3
错误级别值 | 错误级别名称 | 说明 |
---|---|---|
0 | KERN_EMERG | 系统不可用 |
1 | KERN_ALERT | 行为必须被立即采取 |
2 | KERN_CRIT | 危险条件 |
3 | KERN_ERR | 错误条件 |
4 | KERN_WARNING | 警告条件 |
5 | KERN_NOTICE | 普通但重要的条件 |
6 | KERN_INFO | 信息提示 |
7 | KERN_DEBUG | debug |
2.5 系统消息
在systemd下, 内核和系统的信息都通过日志服务systemd-journald.service
来记录,放在/var/log/journal
下的不变的二进制数据,或放在/run/log/journal/
下的变化的二进制数据. 这些二进制日志数据,可以通过journalctl命令来访问。
在systemd下, 系统日志工具rsyslogd改变它的行为来读取变化的二进制数据,并能够创建传统的不变的ASCII系统日志数据。
/etc/default/rsyslog
和/etc/rsyslog.conf
能够自定义系统消息的日志文件和屏幕显示
2.6 systemd下的系统管理
systemd不仅仅提供系统初始化,还提供通用的系统管理功能。比如说日志记录,登录管理,时间管理,网络管理等等。
systemd通过几个命令来管理:
- systemctl命令控制systemd的系统和服务管理器(命令行),
- systemsdm命令控制systemd的系统和服务管理器(图形界面),
- journalctl命令查询systemd日志,
- loginctl命令控制systemd登录管理器,
- systemd-analyze分析系统启动性能。
$unit
可以是一个单元名,也可以匹配的多个单元
系统状态改变命令,通常是通过sudo
来处理,用以获得需要的系统管理权限。
systemctl status $unit|$PID|$device
的输出使用有颜色的点(●)来概述单元状态:
- 白色的
●
表示一个"不活动"或"变为不活动中"的状态。 - 红色的
●
表示"失败"或者"错误"状态。 - 绿色
●
表示"活动"、"重新加载中"或"激活中"状态。
2.7 定制systemd
使用默认安装,通过systemd启动的过程中,在network.target启动后,很多网络服务作为后台守护进程(daemon)启动。shd也不列外。让我们修改为按需启动sshd作为一个定制化的例子。
首先,禁用系统安装的服务单元。
$ sudo systemctl stop sshd.service
$ sudo systemctl mask sshd.service
传统Unix服务的按需套接字激活(on-demand socket activation)系统由indetd超级服务来提供。在systemd下, 相同功能能够通过增加*.socket
和*.service
单元配置文件来启用。
sshd.socket用来定义一个监听的套接字
[Unit]
Description=SSH Socket for Per-Connection Servers
[Socket]
ListenStream=22
Accept=yes
[Install]
WantedBy=sockets.target
sshd@.service作为sshd.socket匹配的服务文件
[Unit]
Description=SSH Per-Connection Server
[Service]
ExecStart=-/usr/sbin/sshd -i
StandardInput=socket
然后重新加载。
$ sudo systemctl daemon-reload
3 udev系统
对于Linux内核2.6版和更新版本,udev系统提供了自动硬件发现和初始化机制。在内核发现每个设备的基础上,udev系统使用从sysfs文件系统的信息启动一个用户进程,使用modprobe程序加载支持它所要求的内核模块, 创建相应的设备节点。
如果
/lib/modules/5.10.0-8-amd64/modules.dep
没有正常生成,模块可能不会被udev系统按期望的方式加载。执行depmod -a
来修复它(重构modules.dep)。该文件描述了modprobe程序使用的模块依赖性。
设备节点的名字,可以通过/etc/udev/rules.d/
里的udev文件来配置。当前默认的规则倾向创建动态生成的名字,除了光驱和网络设备外,会生成非静态的设备名。通过添加和光驱、网络设备类似的个性化规则,你也可以为USB盘之类的其它设备,生成静态设备名。
3.1 内核模块初始化
通过modprobe程序添加和删除内核模块,使我们能够从用户进程来配置正在运行的Linux内核。udev系统自动化它的调用来帮助内核模块初始化。
下面的非硬件模块和特殊的硬件驱动模块,需要被预先加载,把它们在"/etc/modules" 文件里列出
- TUN/TAP 模块提供虚拟的Point-to-Point 网络设备(TUN) 和虚拟的Ethernet以太网网络设备(TAP),
- netfilter 模块提供netfilter防火墙能力
- watchdog timer驱动模块。
modprobe程序的配置文件是按modprobe.conf的说明放在/etc/modprobes.d/
目录下,(如果你想避免自动加载某些内核模块,考虑把它们作为黑名单放在/etc/modprobes.d/blacklist
文件里.)
modinfo程序显示Linux内核模块信息。
lsmod程序以好看的格式展示/proc/modules
的内容, 显示当前内核加载了哪些模块。