Linux内核安装与启动
2013-12-21 14:09:06 阿炯

本站赞助商链接,请多关照。 当按下电源键的那一刻,到系统桌面或者命令行界面出现,这中间究竟发生了什么?是什么在背后默默运作,让 Linux 系统能够顺利启动并为众所用?这个问题的答案,就藏在 Linux 内核的启动过程之中。为什么 Linux 系统能够在不同的硬件设备上稳定运行?为什么它能够高效地管理系统资源?这些问题的答案,都与 Linux 内核的启动过程紧密相连。深入了解 Linux 内核的启动过程,不仅能够明白系统是如何从无到有地构建起来,还能掌握系统底层的运行机制,从而在系统出现问题时,能够更加准确地进行调试和优化。今天就一起踏上探索 Linux 内核启动过程的奇妙之旅,揭开它神秘的面纱,看看那些在幕后默默工作的代码和机制,究竟是如何协同合作,让 Linux 系统得以顺利启动并提供一个强大而稳定的运行环境。

从本质上来说,Linux 内核是一段运行在最高特权级别的特殊程序,它牢牢掌控着计算机的硬件资源,为上层的应用程序提供稳定、高效的运行环境。


顶部是用户(或应用程序)空间。这是用户应用程序执行的地方。用户空间下面是内核空间,Linux内核就位于这里。GNU C库(glibc)也在这里。它为内核提供了一个系统调用接口,也为用户空间应用程序和内核之间的转换提供了一个机制。这非常重要,因为内核和用户空间应用程序使用不同的受保护地址空间。每个用户空间进程使用自己的虚拟地址空间,而内核占用一个单独的地址空间。

Linux内核可以进一步分为3层。最上面是系统调用接口,实现一些基本功能,比如读写。系统调用接口下面是内核代码,可以更准确的定义为独立于架构的内核代码。这些代码对于Linux支持的所有处理器架构都是通用的。在这些代码下面是依赖于架构的代码,它构成了通常称为BSP(板支持包)的部分。这些代码用作给定架构的处理器和平台特定代码。

Linux内核源代码的目录结构

核心目录结构三个主要部分:

1、内核核心代码,包括第3章所描述的各个子系统和子模块,以及其它的支撑子系统,例如电源管理、Linux初始化等。

2、其它非核心代码,例如库文件(因为Linux内核是一个自包含的内核,即内核不依赖其它的任何软件,自己就可以编译通过)、固件集合、KVM(虚拟机技术)等。

3、编译脚本、配置文件、帮助文档、版权说明等辅助性文件使用ls命令看到的内核源代码的顶层目录结构,具体描述如下。include/ ---- 内核头文件,需要提供给外部模块(例如用户空间代码)使用。

kernel/ ---- Linux内核的核心代码,包含了3.2小节所描述的进程调度子系统,以及和进程调度相关的模块
mm/ ---- 内存管理子系统(3.3小节)。
fs/ ---- VFS子系统(3.4小节)。
net/ ---- 不包括网络设备驱动的网络子系统(3.5小节)。
ipc/ ---- IPC(进程间通信)子系统。
arch/ ---- 体系结构相关的代码,例如arm, x86等等。
arch/mach- ---- 具体的machine/board相关的代码。
arch/include/asm ---- 体系结构相关的头文件。
arch/boot/dts ---- 设备树(Device Tree)文件。
init/ ---- Linux系统启动初始化相关的代码。
block/ ---- 提供块设备的层次。
sound/ ---- 音频相关的驱动及子系统,可以看作“音频子系统”。
drivers/ ---- 设备驱动(在Linux kernel 3.10中,设备驱动占了49.4的代码量)。
lib/ ---- 实现需要在内核中使用的库函数,例如CRC、FIFO、list、MD5等。
crypto/ ----- 加密、解密相关的库函数。
security/ ---- 提供安全特性(SELinux)。
virt/ ---- 提供虚拟机技术(KVM等)的支持。
usr/ ---- 用于生成initramfs的代码。
firmware/ ---- 保存用于驱动第三方设备的固件。
samples/ ---- 一些示例代码。
tools/ ---- 一些常用工具,如性能剖析、自测试等。
Kconfig, Kbuild, Makefile, scripts/ ---- 用于内核编译的配置文件、脚本等。
COPYING ---- 版权声明。
MAINTAINERS ----维护者名单。
CREDITS ---- Linux主要的贡献者名单。
REPORTING-BUGS ---- Bug上报的指南。
Documentation, README ---- 帮助、说明文档。

内核之所以能在复杂的系统环境中高效稳定地运行,离不开其精心设计的核心模块。这些核心模块就像一个精密机器中的各个关键部件,各自承担着独特而重要的职责,它们相互协作,共同支撑起整个 Linux 系统的运行。


Linux内核的整体架构

如上图所示,Linux内核的整体架构基于其核心功能被划分为五大子系统。首先,进程调度器负责管理CPU资源,确保各个进程能够以尽可能公平的方式访问CPU;其次,内存管理器负责管理内存资源,使多个进程能够安全共享内存,并通过虚拟内存机制支持进程使用超过物理内存容量的空间,未使用的数据通过文件系统存储于外部非易失存储器中,按需调回内存;第三,虚拟文件系统将各类外部设备(如硬盘、输入输出设备和显示设备等)抽象为统一的文件操作接口,体现了Linux“一切皆是文件”的设计哲学;第四,网络子系统负责管理系统中的网络设备并实现多种网络协议标准;最后,进程间通信机制不直接管理硬件,而是专注于协调系统中进程之间的通信。这些模块相互协作,构成了Linux内核高效、稳定且可扩展的核心架构。

一、硬件初始化:系统启动的前奏

当按下计算机的电源按钮,一场精密而复杂的启动之旅便悄然开启。在这个过程中,硬件初始化是至关重要的第一步,它为后续 Linux 内核的顺利启动奠定了坚实的基础。而 BIOS/UEFI 和引导加载程序在其中扮演着不可或缺的角色。启动顺序:
1.BIOS或启动固件加载并运行引导装载程序
2.引导装载程序在磁盘上找到内核映像并装载到内存中启动
3.内核初始化设备及其初始程序
4.内核挂载root文件系统
5.内核用PID 1 来运行init,用户空间此时开始启动
6.init启动其他系统进程
7.init通常在最后启动一个用于用户登录的进程

1.1BIOS/UEFI:硬件的 “把关者”
BIOS(Basic Input/Output System,基本输入输出系统),是计算机启动时最先运行的一段固化在主板 ROM 芯片中的程序。它就像是一位严谨的 “硬件管家”,在计算机启动初期,承担着硬件自检和初始化的重要职责。BIOS 会逐一检查 CPU、内存、硬盘、显卡等硬件设备,确保其都能正常工作,并为这些设备提供必要的初始化参数,就像为一场演出的各个演员安排好出场顺序和基本动作一样。完成硬件检查后,BIOS 会按照预设的启动顺序,寻找可引导的设备,比如硬盘、光盘或者 U 盘 ,并将系统的控制权传递给该设备上的引导程序,从而拉开操作系统启动的序幕。

随着计算机技术的不断发展,UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口)逐渐崭露头角,成为 BIOS 的继任者。UEFI 采用了更加现代化的设计理念,支持 32 位和 64 位模式,拥有更大的内存地址空间,还提供了图形用户界面和网络启动功能,为用户带来了更加便捷和高效的使用体验。在启动方式上,UEFI 支持 GPT(GUID Partition Table)分区表,突破了 BIOS 使用 MBR(Master Boot Record)分区方式对磁盘大小和分区数量的限制,使得人们能够使用更大容量的硬盘和创建更多的分区。UEFI 还引入了安全引导功能,通过数字签名验证启动程序的真实性,有效抵御了病毒和恶意软件的攻击,为系统的安全性提供了更可靠的保障。

简单来说,BIOS 像是一位坚守传统的老管家,虽然经验丰富,但在面对一些新的需求时,可能会稍显力不从心;而 UEFI 则像是一位年轻有为的新管家,不仅继承了老管家的基本职责,还带来了许多创新的理念和方法,能够更好地适应现代计算机技术的发展需求。在如今的计算机市场中,UEFI 正逐渐取代 BIOS,成为主流的固件接口,但 BIOS 在一些旧设备中仍然发挥着作用,见证着计算机技术的发展历程。

1.2引导加载程序:连接硬件与内核的桥梁
在 BIOS/UEFI 完成硬件初始化并找到可引导设备后,引导加载程序就接过了“接力棒”,成为连接硬件与内核的关键桥梁。引导加载程序的主要任务是从存储设备中读取内核镜像,并将其加载到内存中,同时为内核传递必要的启动参数,确保内核能够顺利启动并运行。

以 GRUB2(GRand Unified Bootloader version 2)为例,它是目前大多数 Linux 发行版默认使用的引导加载程序,功能强大且灵活。GRUB2 采用了模块化设计,使得其核心更加精炼,能够支持多种文件系统和设备,包括常见的 ext4、NTFS 等文件系统以及 USB、SATA 等设备,这使得它在不同的硬件环境中都能游刃有余地工作。当计算机启动时,GRUB2 会首先读取其配置文件,通常位于/boot/grub2/grub.cfg 。这个配置文件中包含了丰富的信息,如系统中安装的各个操作系统的位置、内核镜像的路径以及传递给内核的启动参数等。通过解析这些配置信息,GRUB2 能够准确地找到内核镜像,并将其加载到内存中指定的位置。

在加载内核镜像的过程中,GRUB2 还会根据配置文件中的设置,为内核传递各种启动参数。这些参数就像是给内核下达的“指令”,告诉内核如何初始化硬件设备、挂载文件系统以及设置系统运行的各种环境参数等。root=/dev/sda1这个参数会告诉内核根文件系统所在的设备是/dev/sda1;ro参数表示以只读方式挂载根文件系统,确保在系统启动初期文件系统的完整性和安全性。通过这些启动参数的传递,内核能够在启动时正确地识别和配置硬件环境,为后续的系统运行做好充分准备。

可以说,引导加载程序就像是一位经验丰富的“引航员”,在硬件与内核之间搭建起了一座沟通的桥梁,确保内核能够在正确的时间、以正确的方式加载到内存中,并顺利启动运行,为整个 Linux 系统的稳定运行奠定了坚实的基础。

二、内核加载与初始化:系统核心的启动

在完成硬件初始化并由引导加载程序将内核镜像加载到内存后,Linux 内核正式开始启动,进入内核加载与初始化阶段。这一阶段是整个系统启动过程的核心,它涉及到内核的解压、核心初始化、加载 initramfs 以及挂载根文件系统等一系列关键步骤,每一步都紧密相连,共同构建起系统运行的基础。

2.1内核解压与核心初始化
当引导加载程序将内核镜像成功加载到内存后,内核的启动之旅才刚刚开始。此时,内核镜像可能是以压缩形式存在的,这就需要进行解压操作,就像打开一个精心包装的礼物,才能看到里面真正的宝贝。以常见的 zImage 镜像文件为例,它是经过 gzip 压缩的内核镜像。在启动时,会首先调用解压程序,将压缩的内核镜像解压到内存中的指定位置。这个过程就像是把一本被压缩打包的书籍重新展开,以便能够正常阅读其中的内容。

内核解压完成后,紧接着就进入核心初始化阶段。这一阶段,内核就像是一个刚刚苏醒的巨人,开始有条不紊地对自身进行初始化设置。它会初始化一系列关键的子系统,如进程管理子系统、内存管理子系统、设备驱动子系统等,这些子系统就像是巨人身体的各个重要器官,各自承担着独特而关键的职责。

进程管理子系统负责创建、调度和终止进程,就像一个繁忙的交通警察,指挥着系统中众多进程的运行,确保它们能够有序地共享 CPU 资源,避免出现混乱和冲突。内存管理子系统则如同一个精打细算的管家,负责管理系统的物理内存和虚拟内存,为进程分配合理的内存空间,确保内存的高效利用,避免内存泄漏和浪费,保障系统运行的稳定性和高效性。设备驱动子系统就像是各种设备的翻译官,负责与硬件设备进行交互,为设备提供统一的接口,使得应用程序可以方便地访问硬件设备,无论是硬盘、显卡还是网卡等设备,都能通过设备驱动子系统与内核进行顺畅的沟通。

内核还会初始化系统调用接口,这是用户空间与内核空间进行交互的重要通道,应用程序通过系统调用接口向内核发出各种请求,如文件读写、进程创建等,内核则根据这些请求执行相应的操作,并将结果返回给应用程序。这个过程就像是用户通过一扇特殊的门,向内核这个神秘的城堡内传递指令,然后得到相应的回应和服务。

2.2加载 initramfs
在核心初始化的过程中,加载 initramfs(Initial RAM Filesystem,初始随机存取存储器文件系统)是一个不可或缺的关键环节。initramfs 就像是一个临时搭建的 “工具箱”,里面装满了启动 Linux 系统所需的最低限度工具和驱动程序,这些工具和驱动程序是系统启动过程中解决各种问题的必备 “武器”。

为什么需要这样一个临时的 “工具箱” 呢?这就涉及到一个类似于 “先有鸡还是先有蛋” 的问题。在 Linux 内核被加载到内存并运行后,内核进程最终需要切换到用户空间的进程来使用计算机,而用户进程又存在于外存储设备上,比如 systemd 进程,通常 systemd 进程所在的存储设备也是 Linux 真正的根文件系统所在的位置。然而,内核源码中并没有包含驱动程序,驱动程序存放在外存储设备上。要切换到 systemd 进程,就需要外存储的驱动,但没有驱动又没办法访问外存储,这就形成了一个看似无解的矛盾。

initramfs 的出现巧妙地解决了这个矛盾。它作为一个临时的文件系统,包含了必要的设备驱动,如硬盘、网卡、文件系统等驱动,以及加载驱动的工具及其运行环境,比如基本的 C 库和动态库的链接加载器等。这样一来,内核就不必再从硬盘等外存储设备中获取驱动,而是可以直接从已经加载到内存的 initramfs 中获取所需的驱动,进而驱动硬盘,访问硬盘上的根文件系统,成功打破了这个 “先鸡还是先蛋” 的僵局。

当计算机启动时,initramfs 会被加载到内存中,然后 Linux 内核会使用这个临时文件系统来完成系统启动的一些必要步骤。内核执行 initramfs 中的 init 脚本,这个脚本就像是一个精心策划的行动计划,它会按照预定的步骤探测硬件设备、加载驱动,为挂载真正的根文件系统做好充分准备。在这个过程中,initramfs 就像是一座坚固的桥梁,帮助 Linux 内核在启动过程中顺利跨越各种障碍,完成各项关键任务,确保系统能够平稳地过渡到正常运行状态。

2.3挂载根文件系统
挂载根文件系统是 Linux 内核启动过程中的最后一个关键步骤,也是系统能够正常运行的重要保障。根文件系统就像是系统的“家园”,它包含了操作系统运行所需的所有基本文件和目录结构,是整个系统文件和目录的起点,所有其他文件系统和目录都建立在它的基础之上。

在挂载根文件系统之前,内核已经完成了一系列的初始化工作,包括内核解压、核心初始化以及加载 initramfs 等。此时内核已经具备了足够的能力来识别和访问存储设备上的根文件系统。挂载根文件系统的过程涉及到多个复杂的步骤,首先需要初始化文件系统相关的数据结构,这些数据结构就像是一份详细的地图,记录着文件系统的各种信息,如文件的存储位置、目录结构等,为内核访问文件系统提供了重要的指引。

接着,内核会注册并挂载根文件系统。在这个过程中,内核会根据启动参数中指定的根文件系统类型,如常见的 ext、XFS 等,调用相应的文件系统驱动程序。这些驱动程序就像是一把把特殊的钥匙,能够打开不同类型文件系统的大门,使得内核能够与根文件系统进行有效的交互。

假设根文件系统位于硬盘的某个分区上,内核会通过设备驱动程序与硬盘进行通信,读取分区表信息,找到根文件系统所在的分区。然后,内核会根据文件系统的格式,如 ext4 文件系统的超级块信息,来初始化文件系统的元数据,建立起文件系统的目录结构和文件索引,从而实现对根文件系统的挂载。一旦根文件系统成功挂载,内核就可以访问其中的文件和目录,执行系统启动所需的初始化脚本和程序,如启动 systemd 进程,进入用户空间,完成整个系统的启动过程。

可以说,根文件系统是 Linux 系统架构的基石,没有它,系统将无法启动或正常运行。它不仅为系统提供了基本的文件和目录结构,还包含了启动和运行系统所需的关键文件和工具,如系统初始化脚本、设备文件、配置文件等,这些文件和工具是系统正常运行的重要保障。

三、初始化系统:系统服务的 “调度者”

当 Linux 内核完成初始化并成功挂载根文件系统后,系统便进入了初始化系统阶段。在这个阶段,初始化系统将负责启动各种系统服务,配置系统环境,为用户提供一个完整且可用的系统环境。其中,systemd 和 init 作为初始化系统的关键组件,在系统服务的启动和管理中发挥着核心作用。

3.1systemd 与 init:初始化系统的 “指挥官”
在 Linux 系统的发展历程中,init 一直是传统的系统初始化工具,基于 System V 的实现,它采用串行启动方式,就像一列按顺序停靠站点的火车,每次只能启动一个服务。init 依赖于运行级别(runlevel)来控制系统的状态,通过/etc/inittab文件定义系统启动时的行为,并借助/etc/rc.d/下的脚本控制服务的启动与停止。在运行级别切换时,init 会按照预设的顺序执行相应的脚本,启动或停止对应的服务。然而,这种串行启动方式在服务数量较多时,会导致系统启动时间较长,而且由于缺乏对服务之间依赖关系的明确表达,很容易出现服务启动顺序错误或失败的情况 。

随着 Linux 系统的广泛应用和功能的不断扩展,传统 init 的局限性逐渐凸显。为了满足现代系统对高效启动和灵活服务管理的需求,systemd 应运而生,成为新一代的系统和服务管理器。systemd 摒弃了传统的运行级别概念,转而使用 target 单元来表示不同的系统状态,为系统管理带来了全新的思路和方法。

systemd 最大的优势之一在于它支持并行启动多个服务,这就好比多辆火车同时出发,大大加快了系统的启动速度。它通过/etc/systemd/system/和/usr/lib/systemd/system/中的单元文件定义服务和目标状态,每个单元文件都包含了丰富的配置信息,如服务的启动命令、依赖关系、重启策略等。以sshd.service为例,通过编写相应的单元文件,我们可以清晰地定义它在网络服务启动之后启动,并且在服务出现故障时自动重启,确保系统的安全性和稳定性。

在服务管理方式上,systemd 也展现出了强大的功能和灵活性。它使用.service文件描述服务单元,支持丰富的配置选项,包括依赖关系、自动重启、资源限制、安全上下文等。通过systemctl命令,我们可以方便地管理服务的生命周期,如启动服务(systemctl start sshd.service)、设置服务开机自启(systemctl enable sshd.service)等。systemd 还能自动检测服务状态并进行恢复,提供了更强的可控性和可维护性,让系统管理员能够更加高效地管理系统服务。

systemd 在日志与会话管理方面也有出色的表现。它提供了journald服务,用于收集和存储日志信息,支持结构化日志记录,并可通过journalctl命令方便地查询日志,为系统故障排查提供了有力的支持。logind模块负责管理用户登录会话,支持会话切换和电源管理,进一步提升了系统的管理效率和用户体验。

3.2运行级别与服务启动:系统状态的 “掌控”
在传统的 init 系统中,运行级别是控制系统状态的重要概念,它定义了系统启动时所加载的服务和进程,以及在系统运行过程中可以切换到的不同操作模式。运行级别通常用数字来表示,在常见的 Linux 系统中,有 7 个标准的运行级别,每个级别都有其特定的用途和功能。

运行级别 0 表示系统关机状态,当系统切换到这个级别时,所有的系统服务和进程都会被关闭,计算机硬件也会停止运行,这是一种安全的关机方式,确保数据的完整性和硬件的安全性。运行级别 1 是单用户模式,主要用于系统维护和修复。在这种模式下,系统只允许一个用户(通常是 root 用户)登录,并且大多数网络服务不会启动,只启动最少数量的服务和进程,方便管理员对系统进行故障排除和修复,比如修复文件系统错误、重置密码或者调整系统配置等。

运行级别 2 到 5 代表着不同类型的多用户模式。运行级别 2 通常是多用户模式,但没有网络支持,适合某些特定的环境,比如在进行网络故障排除时,管理员可以将系统切换到这个级别,以排除网络相关的问题是否是由于系统启动时加载的网络服务引起的。运行级别 3 是最常见的多用户模式,具有完整的网络支持,大多数服务器系统都会默认运行在这个级别。在运行级别 3 下,用户可以登录系统,运行各种应用程序,并且可以通过网络进行通信和数据传输。

运行级别 4 在一些系统中可能被预留用于特定的用途,或者可能与运行级别 3 类似,但具有一些额外的配置或服务,不过在实际应用中很少被使用。运行级别 5 是带有图形界面支持的多用户模式,系统不仅会启动所有必要的网络服务和进程,还会启动图形界面相关的服务和进程,以便用户可以通过图形界面进行操作,这种模式通常用于桌面系统或者需要图形界面支持的应用环境。运行级别 6 表示系统重启,当系统切换到这个级别时,会按照预定的顺序关闭所有的服务和进程,然后重新启动计算机硬件。

在 systemd 中,虽然摒弃了传统的运行级别概念,但仍然提供了与运行级别类似的功能,通过 target 单元来实现。例如,multi-user.target相当于传统的运行级别 3,是多用户文本模式;graphical.target相当于运行级别 5,是带有图形界面的多用户模式。而且,systemd 通过单元文件中的依赖关系定义,能够更加智能地管理服务的启动顺序,确保系统的稳定运行。

在服务启动方面,无论是 init 还是 systemd,都有相应的管理方式。在 init 系统中,服务启动脚本通常位于/etc/init.d/或/etc/rc.d/init.d/,通过执行这些脚本来启动、停止或重启服务。而在 systemd 中,使用systemctl命令来管理服务的启动、停止、重启、状态查看等操作,并且可以通过设置服务的开机自启属性,确保服务在系统启动时自动运行。比如,要启动 Nginx 服务,可以使用命令sudo systemctl start nginx.service;要设置 Nginx 服务开机自启,可以使用命令sudo systemctl enable nginx.service 。通过这些管理方式,系统能够按照用户的需求和系统的配置,准确地启动和管理各种服务,为用户提供稳定、高效的系统环境。

四、终端与用户登录:人机交互的开始

当系统完成初始化并启动各项服务后,就进入了终端与用户登录阶段,这是用户与系统进行交互的起点。在这个阶段,系统会完成终端初始化,为用户提供登录环境,并支持多种登录方式,让用户能够安全、便捷地进入系统。

4.1终端初始化:准备登录环境
终端初始化是系统为用户提供登录环境的重要环节。在 Linux 系统中,终端是用户与系统进行交互的主要界面,它可以是物理终端,如显示器和键盘,也可以是虚拟终端,如通过 SSH 远程连接的终端。终端初始化的过程涉及到多个方面,包括设置终端的属性、加载终端驱动程序以及启动登录管理器等。

在传统的 Linux 系统中,getty程序在终端初始化中扮演着关键角色。getty是一个负责管理终端连接的程序,它会监听终端设备的输入,等待用户输入用户名和密码。当用户在终端上按下键盘时,getty会捕获输入信号,并将其传递给系统进行处理。getty还会设置终端的属性,如波特率、字符编码等,确保终端能够正确地显示和接收信息。

在现代的 Linux 系统中,systemd 引入了systemd - getty - service和systemd - login来管理终端。systemd - getty - service负责启动和管理getty进程,它会根据系统的配置,在指定的终端设备上启动getty,为用户提供登录界面。systemd - login则负责处理用户的登录会话,它会验证用户的身份,创建用户会话,并为用户提供相应的权限和环境变量。通过这种方式,systemd 实现了对终端的更高效管理,提高了系统的安全性和稳定性。

以 Ubuntu 系统为例,在启动过程中,systemd - getty - service会在虚拟终端/dev/tty1到/dev/tty6上启动getty进程,用户可以通过按下Ctrl + Alt + F1到Ctrl + Alt + F6组合键来切换到不同的虚拟终端进行登录。当用户在终端上输入用户名和密码后,systemd - login会对用户的身份进行验证,如果验证通过,就会创建用户会话,用户就可以开始使用系统了。

4.2登录方式:多样的进入途径
Linux 系统支持多种登录方式,以满足不同用户的需求和场景。常见的登录方式包括文本界面登录、图形界面登录和远程登录,每种登录方式都有其特点和适用范围。

文本界面登录是最基本的登录方式,它在系统启动后,用户会看到一个文本界面提示符,需要输入用户名和密码进行登录。这种登录方式简单直接,不占用过多系统资源,适合于服务器环境或者对图形界面需求不高的用户。在文本界面登录时,用户通过键盘输入命令来操作计算机,对于熟悉命令行操作的用户来说,这种方式能够高效地完成各种任务。比如,在服务器上进行系统维护、配置服务等操作时,文本界面登录是非常方便的。

图形界面登录则提供了更加直观和友好的用户体验。在 Linux 系统中,常见的图形界面登录管理器有 GDM(GNOME Display Manager)、KDM(KDE Display Manager)、LightDM 等。用户只需要点击屏幕上的用户名图标,然后输入密码进行登录。登录后,用户可以通过鼠标和键盘操作图形界面,运行各种图形化应用程序。这种登录方式适合于普通桌面用户,他们可以通过图形界面轻松地进行文件管理、网页浏览、办公软件使用等操作。比如,在日常办公和娱乐中,图形界面登录能够让用户更加便捷地使用计算机。

远程登录允许用户从其他主机或者终端连接到 Linux 系统进行登录,这在系统管理和维护中非常有用。常用的远程登录协议有 SSH(Secure Shell)和 Telnet。SSH 提供了更安全的连接方式,它使用加密技术对传输的数据进行加密,防止数据被窃取和篡改。通过 SSH,管理员可以在远程主机上执行各种命令,管理服务器的配置、安装软件、查看日志等。而 Telnet 是一种不加密的连接方式,数据在传输过程中可能会被截获,因此安全性较低,现在已经较少使用。

为了确保登录的安全性,Linux 系统采取了一系列措施。设置强密码是非常重要的,密码应该包含字母、数字和特殊字符,长度足够,避免使用简单易猜的密码。启用 SELinux(Security - Enhanced Linux)也是增强系统安全性的有效手段。SELinux 是一种强制访问控制(MAC)安全模块,它通过定义和实施安全策略,限制进程对资源的访问权限,即使系统中的某个服务被攻陷,攻击者也只能在有限的权限范围内活动,无法获取系统的最高权限,从而有效地保护了系统的安全。

可以说,终端与用户登录是 Linux 系统启动过程中的最后一个环节,也是用户与系统交互的开始。通过终端初始化和多种登录方式,Linux 系统为用户提供了一个安全、便捷的登录环境,让用户能够顺利地进入系统,享受 Linux 系统带来的强大功能和高效体验。

五、关机流程:系统的 “落幕”

当我们完成对 Linux 系统的使用后,正确的关机流程同样至关重要。关机过程并非简单地切断电源,而是涉及到数据同步、服务停止以及系统资源的释放等一系列有序操作,以确保系统的安全性和稳定性。在这个过程中,sync 命令和 shutdown/reboot 命令发挥着关键作用。

5.1 sync:数据的 “守护者”
在 Linux 系统中,为了提高 I/O 性能,当我们进行文件写入操作时,数据通常不会立即被写入物理磁盘,而是先被存储在内存缓冲区(即 “脏页”)中。这样做的好处是减少了对磁盘的频繁读写操作,提高了系统的整体运行效率。但这种机制也带来了一定的风险,如果在数据还未写入磁盘时,系统突然断电、崩溃或进行其他异常操作,那么内存中尚未写入磁盘的数据就会丢失,这可能会导致文件损坏、数据不一致等严重问题。

sync 命令的出现,就是为了解决这个问题。它的核心作用是强制将内存缓冲区中所有尚未写入磁盘的缓存数据,包括文件数据、目录结构、元数据等,立即刷新到物理磁盘中,确保数据的持久性和完整性。当我们执行 sync 命令时,系统会调用内核的 sync () 系统函数,将内存中的 “脏页” 数据写入磁盘,使得磁盘上的数据与内存中的数据保持一致。在进行重要文件操作,如修改系统配置文件、完成数据库事务等之后,执行 sync 命令可以保证这些操作的数据已经被安全地保存到磁盘上,避免因意外情况导致数据丢失。

sync 命令的使用场景非常广泛。在安全卸载可移动设备,如 U 盘、移动硬盘时,在执行 umount 命令之前,先运行 sync 命令是一个非常重要的步骤。这可以确保所有针对该设备的数据都已经真正写入了物理设备,防止在卸载过程中数据丢失或损坏。在自动化脚本中,特别是在执行关键操作,如数据库备份、生成重要日志、更新核心文件后,加入 sync 命令可以提高操作的可靠性。在数据库备份完成后,运行 sync 命令可以确保备份文件已经完整地写入磁盘,避免因系统意外导致备份文件损坏或不完整。

虽然 Linux 内核默认每 5 - 30 秒会自动同步缓存(通过 bdflush 或 kupdated 守护进程),但在一些特殊情况下,手动执行 sync 命令仍然是必要的。在进行系统关机或重启操作前,虽然标准的系统关机和重启流程中会自动调用 sync 多次,但在某些复杂的操作环境中,手动执行 sync 命令可以进一步确保数据的安全性。

5.2 shutdown/reboot:系统的有序关闭
shutdown 和 reboot 命令是 Linux 系统中用于实现系统关机和重启的重要工具,它们为系统的关闭和重启提供了一种有序、安全的方式。

shutdown 命令功能强大且灵活,它允许用户有计划地关闭或重启系统,并可以发送警告信息给所有登录用户,让用户有时间保存工作并正常退出系统。shutdown 命令的基本语法为 “sudo shutdown [选项] [时间] [警告信息]”。其中,“选项” 部分有多个参数可供选择,“-h” 表示停止系统并关机,“-r” 表示重启系统。“时间” 参数可以指定关机或重启的时间,“now” 表示立即执行,“+5” 表示 5 分钟后执行,也可以指定具体的时间,如 “22:00” 表示在晚上 10 点执行。“警告信息” 则是在关机或重启前发送给所有登录用户的提示信息,告知他们系统即将进行的操作。

假设我们需要立即关机,可以使用命令 “shutdown -h now”,系统会立即停止所有服务,并将内存中的数据同步到磁盘,然后关闭系统。如果我们希望在 30 分钟后重启系统,并向用户发送提示信息,可以使用命令 “shutdown -r +30 "系统将在 30 分钟后重启,请保存您的工作!"” 。在执行 shutdown 命令后,系统会按照指定的时间进行操作,并在操作前向用户发送警告信息,让用户有足够的时间保存数据和关闭应用程序。

reboot 命令则更为简洁,它的作用是直接重启系统,相当于执行 “shutdown -r now”。当我们需要快速重启系统,如在安装系统更新、修改系统配置后,希望立即应用更改时,可以使用 reboot 命令。reboot 命令会立即停止所有服务,同步数据到磁盘,然后重新启动系统。

正确使用 shutdown 和 reboot 命令对于系统的安全和稳定至关重要。如果直接切断电源或使用不当的关机方式,可能会导致文件系统损坏、数据丢失、系统无法启动等严重问题。在 Linux 系统中,文件系统采用日志式结构,在写入数据时会先记录操作日志,然后再实际写入数据块。如果突然断电,尚未写入磁盘的数据和未完成的操作日志会使文件系统处于不一致状态,下次开机时,系统需要花费大量时间运行 fsck 工具来修复这些不一致,甚至可能无法完全修复,从而导致数据丢失。

在关机或重启系统时,一定要使用正确的命令和流程,确保系统能够安全、有序地关闭或重启。这不仅可以保护系统中的数据和文件系统,还能延长硬件设备的使用寿命,为我们提供一个稳定、可靠的系统运行环境。

六、Linux内核启动实战

6.1 环境准备
操作系统:Ubuntu 22.04(x86_64)
内核版本:5.15.0(目标编译 5.15.100 版本)
工具依赖:build-essential、libncurses5-dev、bison、flex、libssl-dev、grub2-common
内核文件通常存放在/boot目录下,通常该目录也是grub程序的根目录,内核是以bzimage压缩存放在该目录中,并且名称一般都是以vmlinuz-后跟版本号。这样的方式来命名:
vmlinuz-2.6.32-585.el.i686

通过配置文件,该配置文件通常存放在/boot/grub目录中,名字通常为:grub.conf

配置文件定义了各种菜单,每个一般都对应一个内核,以及传递一些运行参数给内核

开头的几个选项是一些通用配置,只要定义默认菜单、超时时间和背景图的一些选项。

每个菜单,基本上都定义了三个类内容:root、kernel、initrd。

6.2 实战步骤
①下载并编译内核:首先从内核官网下载源码,编译出可启动的内核镜像。

# 下载内核源码
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.100.tar.xz
tar -xf linux-5.15.100.tar.xz
cd linux-5.15.100

# 配置编译选项(基于当前系统配置修改)
cp /boot/config-$(uname -r) .config
make menuconfig  # 图形化配置,可按需精简模块(比如关闭不支持的硬件驱动)

# 编译内核(-jN表示N个线程,根据CPU实际核心数调整)
make -j6
make modules_install  # 安装内核模块
make install  # 安装内核镜像到/boot目录
编译完成后,/boot目录会新增 3 个关键文件:

vmlinuz-5.15.100:压缩的内核镜像
initrd.img-5.15.100:临时根文件系统(initramfs)
System.map-5.15.100:内核符号表

②修改 GRUB 配置,指定启动参数:GRUB 是引导内核的关键,需要手动配置启动项,明确告诉 GRUB 内核位置和启动参数。

# 编辑GRUB配置(不同系统路径可能为/etc/grub.d/40_custom或/boot/grub/grub.cfg)
vim /etc/grub.d/40_custom

③添加如下配置(核心是root、kernel、initrd三个选项)

menuentry 'Custom Linux 5.15.100' --class ubuntu --class gnu-linux {
    # 1. 指定GRUB的根目录(即/boot所在分区,这里假设是第一个硬盘的第一个分区)
    root=(hd0,1)  # 注意:Ubuntu的/boot通常在sda1,索引从0开始,故为(hd0,1)

    # 2. 指定内核路径+启动参数
    #   /vmlinuz-5.15.100 对应实际路径/boot/vmlinuz-5.15.100
    #   root=/dev/sda2 告诉内核:系统真正的根目录在sda2
    #   console=tty0 输出启动日志到显示器
    #   debug 开启内核调试模式(可选,用于跟踪细节)
    kernel /vmlinuz-5.15.100 root=/dev/sda2 console=tty0 debug

    # 3. 指定临时根文件系统(协助内核挂载真正的根目录)
    initrd /initrd.img-5.15.100
}

保存后更新 GRUB:
update-grub  # 生成新的grub.cfg

④重启系统,跟踪启动流程:重启后在 GRUB 菜单选择「Custom Linux 5.15.100」,观察启动过程。若想记录日志,可在启动后查看。

# 查看内核启动日志(包含从解压到初始化的关键步骤)
dmesg | less

# 重点关注以下关键字段:
# [    0.000000] Linux version 5.15.100  # 内核版本
# [    0.000000] Command line: root=/dev/sda2  # 启动参数
# [    0.5xxxxx] VFS: Mounted root (ext4 filesystem)  # 挂载根文件系统
# [    1.2xxxxx] systemd[1]: Starting System Initialization...  # 进入初始化阶段

⑤常见问题排查:如果启动失败,可通过以下方式定位问题

内核找不到根目录:检查kernel行的root=/dev/sdaX是否正确(可用lsblk查看分区)
缺少模块:编译时未勾选必要驱动(比如文件系统驱动ext4),需重新make menuconfig开启
GRUB 路径错误:root=(hd0,X)填错,可在 GRUB 命令行用ls查看分区(启动时按c进入命令行)

6.3 核心配置解析
上述实战中,kernel选项是关键,它包含两个核心信息:

内核路径:/vmlinuz-5.15.100基于 GRUB 的根目录(root=(hd0,1)),对应实际/boot/vmlinuz-5.15.100
启动参数:root=/dev/sda2是给内核的关键指令,告诉内核「真正的根文件系统在哪个分区」,否则内核会因找不到系统文件而启动失败
通过这个实战,你可以直观看到:从 GRUB 加载内核,到内核解压、初始化硬件、挂载根目录,再到启动systemd的完整链条。如果想深入,还可以尝试添加init=/bin/sh参数(跳过初始化系统,直接进入 shell),观察内核启动的更早期阶段。

6.4 centos下安装内核源码包
一些系统级的应用软件在安装时需要用到内核源码,但并不是所有的系统中安装了对应的内核包。像centos6上一般就只需要有kernel-devel包安装了即可:yum install kernel-devel

如果安装好后仍不能解决问题,那就需要看下面的内容了。

On CentOS 6 there is only one kernel-devel package for both architectures x86_64 and i386 however on CentOS 5 there are three versions, if you are unsure on what version you should install then run the command uname -r which will display your kernel version you then need to match the kernel version you are running with the correct kernel-devel package below:

CentOS 5 install kernel-devel for i386 & x86_64:
yum install kernel-devel

Centos 5 install Xen kernel-devel for x86_64 & i386:
yum install kernel-xen-devel

And finally to install kerenl-devel for PAE:
yum install kernel-pae-devel

下面的方法适用于centos 5及6
CentOS Kernel source install (full) for CentOS 5 & 6

如果只是想用包来安装,解决软件对其的依赖而已,可用如下指令:

yum -y install dkms gcc kernel-headers-`uname -r` kernel-devel-`uname -r` kernel-`uname -r`

If install kernel-devel above does not work or you require the full CentOS kernel source for another reason you can install it on CentOS 5 & 6 with:
yum install rpm-build redhat-rpm-config unifdef

如果是非root用户:
mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros

安装centos 6的内核源码
To install the CentOS 6 kernel source

The following command will install the kernel source for CentOS 6.2, you can install it for other version of CentOS 6 simply by using the URL for the alternative version.

rpm -i http://vault.centos.org/6.2/updates/Source/SPackages/kernel-2.6.32-220.7.1.el6.src.rpm 2>&1 | grep -v mockb

安装centos 5.x的内核源码
How To install the CentOS 5.x kernel source

The following will install the full kernel source on CentOS 5.8, again the same applies – you can install the kernel source for another version of CentOS 5 by swapping the URL.

rpm -i http://vault.centos.org/5.8/updates/SRPMS/kernel-2.6.18-308.1.1.el5.src.rpm 2>&1 | grep -v mockb

当安装好了对应的内核包,进行如下的操作:
cd ~/rpmbuild/SPECS

And then unpack the kernel source with:
rpmbuild -bp --target=`uname -m` kernel-2.6.spec 2> prep-err.log | tee prep-out.log

Make sure you copy and paste the commands above as the uname -m command will pull your kernel arch (e.g x86_64) and prep the source for your kernel, you can find the kernel source in your home dir in a directory called rpmbuild/BUILD/ to access the CentOS kernel source install dir type the following as the logged in user you built the kernel source with ~/rpmbuild/BUILD/

七、Linux内核源码构建简介

在Linux内核的编译过程中,.config文件是整个构建系统的核心配置输入。它决定了内核的每一个功能是编译进内核内部(=y),还是编译成独立的模块(=m),或是完全忽略(=n)本节相比于第六节更通用一些。

7.1 生成.config

有以下几种方法:
1)、如果是桌面系统,可以复制/boot下的config-xxx文件,重命名为.config。

2)、使用预定义配置,这些配置文件存放在内核源码的 arch/<架构>/configs/ 目录下

使用命令make ARCH=xxx yyy_deconfig即可将yyy_deconfig复制为.config。

3)、通过make menuconfig手动配置,执行后会出现下面的菜单界面,可以浏览所有内核功能并修改状态,修改后选择 Save,就会保存到 .config 文件中。

交叉编译时每次输入ARCH=xxx CROSS_COMPILE=xxx比较麻烦,可以用以下几种办法解决

1、把这俩参数添加到.bashrc中
export ARCH=arm64
export CROSS_COMPILE=/home/freeoa/workspace/tools/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-

2、创建专用构建脚本

创建build.sh脚本,把固定的参数(上面所提及的)写进去,然后通过这个脚本来编译。

优点:灵活性极高,可以在一个源码目录里放多个不同平台的构建脚本(比如 build_arm.sh、build_x86.sh),互不干扰。同时脚本里还可以添加其他常用选项。

3、直接添加到Makefile中
ARCH?=arm64
CROSS_COMPILE ?= /home/freeoa/workspace/tools/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-

这样可以直接使用make而不需要指定这两个参数了。

7.2 内核make常用目标

帮助
make help:打印出所有的命令帮助信息

清除
clean、mrproper 和 distclean 是三个常用的清理目标,它们依次删除越来越多的生成文件,使源码树回到不同的“干净”程度。

make clean:删除大部分编译过程中生成的中间文件,但保留配置文件(如 .config)和一些辅助文件。

make mrproper:在 clean 的基础上,进一步删除配置文件、备份文件等,使源码树恢复到打补丁前的状态(但保留原始源码)。

make distclean:在 mrproper 的基础上,删除几乎所有非源码文件,包括编辑器备份、补丁文件、外部模块生成的文件等,使源码树与原始发布包基本一致。

日常开发中常用 make clean 重新编译;需要全新配置时用 make mrproper;需要打包源码时用 make distclean。

配置
make menuconfig:基于文本菜单的交互式配置界面,是最常用和推荐的配置方式。它需要 ncurses 库的支持。

make defconfig:为当前架构生成默认配置。如果之前没有 .config 文件,这是一个快速开始的捷径。

make oldconfig:基于现有的 .config 文件进行更新。它会保留所有旧配置,并以交互方式询问用户关于新内核版本中引入的配置选项。

构建
make 或 make all:这是默认目标。它会编译出内核镜像(vmlinux)和所有被配置为模块()的内核模块。

make vmlinux:只编译内核镜像本身,不编译模块。

make modules:只编译内核模块。当你修改了某个驱动代码,只想快速验证时,这个命令非常有用。

make modules_install:将编译好的内核模块安装到系统目录中(通常是 /lib/modules/<内核版本>/)。这个操作通常需要 root 权限。

make install:将编译好的内核镜像(如 arch/x86/boot/bzImage)安装到 /boot 目录,并可能更新引导加载程序(如 GRUB)的配置文件。这个操作通常也需要 root 权限。

7.3 内核make常用变量

make V=1:进行详细输出,在编译时打印出实际执行的每一个完整命令。对于调试编译问题非常有帮助。

make -jN:并行编译。-j 后面的数字 N 指定同时进行的编译任务数,通常可以设为 CPU 核心数的两倍左右,能极大加快编译速度。例如 make -j8。

make O=/path/to/build/dir:将编译生成的所有文件输出到指定目录,而不是混在源码目录中。这样可以保持源码目录的清洁,并且方便为不同配置维护多个编译版本。

make C=1或make C=2:在编译时使用 Sparse 工具进行代码静态检查。C=1 只检查重新编译的文件,C=2 检查所有文件。

make M=path/to/external/module:编译外部内核模块。这让你可以在内核源码树之外开发、编译自己的内核模块。

Makefile中的几个赋值方法的区别
=
递归扩展赋值,定义一个递归扩展变量。变量的值在每次被展开(即被使用时)才进行替换,并且如果值中引用了其他变量,这些引用会递归地展开。

?=
条件赋值,仅当该变量之前没有被定义(即尚未赋值)时,才执行赋值操作;如果变量已经存在(即使值为空),?= 不会改变它。提供默认值,避免覆盖用户通过环境变量或命令行传入的值。

+=
追加赋值,将右侧的文本追加到变量原有值的后面,并在两者之间自动插入一个空格(如果原变量非空)。

:=
简单扩展赋值,定义一个简单扩展变量。变量的值在定义时立即展开(即右值中的变量引用在赋值那一刻就被替换成当时的值),此后该变量的值就是那个固定的字符串。

下文在上面的基础上分8步来介绍其启动过程,下图展示了其启动步骤:


步骤 1 - 按下电源开机后,BIOS(基本输入输出系统)或 UEFI(统一可扩展固件接口)固件会从非易失性存储器中加载,并执行 POST(上电自检)。

步骤 2 - BIOS/UEFI 会检测系统连接的各类硬件设备,包括 CPU、内存(RAM)和存储设备。

步骤 3 - 选定用于启动操作系统的引导设备,可选设备包括硬盘、网络服务器或光盘驱动器(CD-ROM)。

步骤 4 - BIOS/UEFI 运行引导加载程序(GRUB),该程序会提供选择菜单,供用户选择要启动的操作系统或内核功能项。

步骤 5 - 内核加载就绪后,系统切换至用户空间。内核会启动 systemd 作为第一个用户空间进程,该进程负责管理系统进程与服务、检测剩余所有硬件、挂载文件系统,并启动桌面环境。

步骤 6 - 系统启动时,systemd 会默认激活 default.target(默认目标单元),同时执行其他配套单元。

步骤 7 - 系统运行一系列启动脚本,完成系统环境的配置。

步骤 8 - 系统为用户展示登录界面,至此系统启动完成,进入可用状态。

步骤1:BIOS/UEFI固件加载与POST上电自检

1.1 BIOS/UEFI固件的唤醒机制
电源键触发后,电源管理单元向主板提供稳定供电,固化于主板ROM芯片中的BIOS(基本输入输出系统)或UEFI(统一可扩展固件接口)固件随即启动,作为硬件与操作系统的底层交互接口,承担系统启动的初始化引导任务。

BIOS作为传统固件方案,诞生于20世纪70年代,长期承担计算机启动引导功能。其运行于16位实模式,引导过程完全依赖MBR(主引导记录)。MBR位于硬盘第一个扇区,占用512字节存储空间,包含分区表信息与引导代码,BIOS读取并执行该引导代码后,方可推进后续启动流程。随着计算机技术的发展,尤其是操作系统向32位、64位升级,BIOS的局限性日益凸显,16位模式及1MB内存地址空间的限制,已无法满足现代计算机的启动需求。

UEFI作为BIOS的继任者,是由UEFI论坛发布的标准固件接口,采用32位或64位保护模式,支持2TB及以上大容量硬盘,可通过GPT(GUID分区表)替代传统MBR。相较于MBR,GPT分区表不仅突破硬盘容量限制,还具备更可靠的分区结构及更多分区数量支持。此外,UEFI自带图形用户界面,提升启动过程中的交互便捷性,其固件代码维护性更强,开机检测耗时更短,可显著提升系统启动效率。

1.2 POST上电自检的核心任务
BIOS/UEFI固件启动后,立即执行POST(Power-On Self-Test,上电自检)流程。POST的核心目标是全面验证核心硬件的正常运行状态,规避硬件故障导致的启动中断。

POST自检优先对CPU进行检测,核查CPU寄存器、缓存的运行状态,确认其型号、频率等关键参数符合设计标准。CPU作为计算机的核心运算单元,其稳定性直接决定系统整体运行可靠性。内存作为数据存储与交换的核心组件,同样是POST检测的重点内容,系统通过快速读写测试,验证内存控制器与内存条的通信稳定性,以及内存寻址、存储功能的完整性,内存检测通过是系统正常存储与读取数据的前提。

显卡是POST检测的关键环节之一,固件将初始化显卡控制器并尝试输出显示信号。若显卡存在接口松动、硬件损坏等问题,将出现黑屏、花屏等显示异常,直接影响系统输出信息的获取。

此外,POST还将检测键盘、鼠标等输入设备,以及硬盘、SSD、光驱等存储设备,确认各类设备与主板连接正常且可被系统识别。存储设备若无法被识别,将导致操作系统无法加载,启动流程终止。

若某一硬件检测失败,系统将通过蜂鸣声代码或屏幕提示输出故障信息,例如“NO VIDEO”提示指向显卡故障,“Hard Drive Not Found”提示硬盘未被识别。不同厂商的BIOS/UEFI蜂鸣声代码定义存在差异,可通过查阅主板手册定位具体故障原因。所有硬件检测通过后,系统将发出短促提示音,标志POST自检完成,启动流程继续推进。

步骤2:BIOS/UEFI的全量硬件检测

POST完成核心硬件初步验证后,BIOS/UEFI将开展全量硬件检测,覆盖主板芯片组、各类扩展设备及外部设备,确保硬件生态的稳定性,为操作系统加载及运行提供可靠支撑。

主板芯片组作为硬件系统的核心枢纽,负责协调CPU、内存、存储设备及各类外部设备的数据传输与通信,是本次检测的重点。BIOS/UEFI将全面核查芯片组的型号、版本及功能完整性,例如检测北桥芯片与CPU、内存、显卡等高速设备的通信通道通畅性,北桥芯片故障将严重影响系统性能;同时验证南桥芯片对USB控制器、SATA控制器等低速设备的控制能力,保障数据传输的稳定性与可靠性。

USB控制器作为外部USB设备的连接核心,其检测至关重要。BIOS/UEFI将核查USB控制器的端口数量、传输速率,以及设备识别与枚举能力,USB控制器故障可能导致U盘无法读取、USB鼠标键盘失灵等问题。检测过程中,固件通过向USB控制器发送指令,验证其对不同类型USB设备的兼容性与响应效率。

除USB设备外,BIOS/UEFI还将检测键盘、鼠标、打印机等常见外部设备:对键盘,检测按键扫描码的正常性,判断是否存在按键粘连或损坏;对鼠标,检测其移动、点击功能及与主板的通信稳定性。设备故障时,固件将通过屏幕提示输出故障信息,便于故障定位。

与POST仅聚焦核心硬件的检测模式不同,本次全量检测覆盖主板全组件及各类外部设备,可有效规避操作系统启动后因硬件兼容性或故障导致的运行异常。所有硬件检测通过后,BIOS/UEFI进入下一启动环节。

步骤3:选定系统启动设备

BIOS/UEFI完成全量硬件检测并确认正常后,进入启动设备选择环节。该环节将依据预设启动顺序(Boot Order),从各类存储设备中筛选可引导的存储介质,作为操作系统加载的载体。

BIOS/UEFI选择启动设备时遵循严格的优先级规则,通常将硬盘(HDD/SSD)设为首选启动设备。硬盘作为操作系统的主流存储载体,具备数据稳定性高、读取速度快的优势,可保障系统快速启动。若硬盘中安装多个操作系统,固件将按内置规则优先启动默认操作系统所在分区。

除硬盘外,U盘、网络服务器、光盘驱动器(CD-ROM)均为常见启动设备。U盘广泛应用于系统安装、维护场景,将U盘制作成启动盘并调整启动顺序后,系统可从U盘读取引导程序执行相关操作。网络服务器启动主要应用于无盘工作站、瘦客户机等特殊场景,此类设备无本地存储,需通过网络从服务器获取操作系统镜像及相关文件完成启动,实现集中化管理与部署。光盘驱动器虽已逐步淘汰,但在部分旧设备及特定软件安装场景中仍可发挥作用。

传统BIOS模式下,选定启动设备后,固件将读取该设备的MBR扇区。MBR仅占用512字节,包含三部分核心内容:前446字节为主引导程序,负责加载操作系统核心;中间64字节为分区表,记录硬盘各分区的起始位置与大小;最后2字节为有效标志符“55AA”,用于验证MBR有效性。BIOS读取MBR后,先验证标志符,验证通过则执行主引导程序,继续推进操作系统加载。

UEFI模式的启动流程更为简洁高效,固件直接读取EFI系统分区(ESP)中的引导程序。ESP分区采用FAT32文件系统,存储.efi后缀的引导文件,固件将查找“bootx64.efi”“grubx64.efi”等核心引导文件并执行,进而启动操作系统。相较于BIOS,UEFI不仅支持更大容量硬盘,还可提供更灵活的启动配置。

实际应用中,可根据需求在BIOS/UEFI设置界面手动调整启动顺序。例如从U盘安装系统时,开机按下对应快捷键(通常为Del、F2、F10等,具体因主板型号而异),进入“Boot”选项界面,将U盘设为第一启动项,保存配置并重启后,即可实现从U盘启动。

步骤4:BIOS/UEFI 运行引导加载程序 (GRUB)

4.1 GRUB的分阶段引导原理
BIOS/UEFI选定引导设备后,将加载引导程序。Linux系统中,应用最广泛的引导程序为GRUB(GRand Unified Bootloader)。GRUB作为硬件与操作系统内核的衔接核心,采用分阶段引导设计,有效解决BIOS/UEFI无法直接识别Ext4、LVM、XFS等复杂Linux文件系统的问题。

GRUB第一阶段(Stage1)驻留于硬盘MBR或EFI分区,该阶段引导程序高度精简,核心功能为定位并加载下一阶段引导程序。由于MBR存储空间有限(仅512字节,需容纳分区表及启动标志),留给引导程序的空间不足446字节,无法承载完整功能的GRUB引导器,因此Stage1仅完成基础硬件初始化,包括屏蔽中断、关闭处理器缓存等,为后续引导程序加载创造稳定环境。

GRUB第二阶段(Stage1.5)包含文件系统驱动程序,是GRUB与Linux文件系统实现通信的核心。借助该阶段的驱动程序,GRUB可读取/boot/grub目录下的相关内容。Stage1.5由Stage1根据预设信息从硬盘特定位置读取至内存,完成与下一阶段引导的衔接准备。

GRUB第三阶段(Stage2)为引导程序核心,采用C语言编写。该阶段将加载图形化启动菜单,提供可视化交互界面;同时读取grub.cfg配置文件,获取内核文件路径、根文件系统位置、启动参数等关键信息。随后,GRUB将Linux内核文件(通常命名为vmlinuz)与初始化内存盘(initrd或initramfs)加载至内存,完成内核启动的准备工作。

4.2 GRUB的核心功能实现
Stage2加载完成并读取grub.cfg配置文件后,GRUB核心功能正式发挥作用。grub.cfg作为GRUB的核心配置文件,包含启动菜单项、默认启动项、超时时间、内核启动参数等关键配置。例如,“set default=0”用于将第一个菜单项设为默认启动项,“set timeout=5”用于设置启动菜单显示时长为5秒。

多操作系统环境中,GRUB的多系统引导功能具备极高实用性。开机后,用户可通过键盘上下箭头键在GRUB启动菜单的不同操作系统选项间切换。以Linux与Windows双系统为例,GRUB将在菜单中列出两个系统的启动选项,选定对应选项并确认后,GRUB将按配置加载相应系统内核:针对Windows系统,GRUB通过“chainloader +1”指令将引导权移交至Windows引导程序;针对Linux系统,GRUB读取配置文件中指定的内核路径,同时传递根文件系统路径、只读挂载、启动参数等关键信息。

GRUB支持启动时临时修改内核启动参数,在启动菜单界面按下“e”键即可进入编辑模式,对内核命令行进行调整。例如,添加“initcall_debug”参数可用于硬件调试,添加“nouveau.modeset=0 rd.driver.blacklist=nouveau”参数可禁用nouveau驱动,为NVIDIA驱动安装提供支持。临时修改仅对本次启动有效,若需永久修改,需编辑/etc/default/grub文件,调整参数后执行对应命令:Debian/Ubuntu系统执行“update-grub”,RHEL/CentOS/Fedora系统执行“grub2-mkconfig -o /boot/grub2/grub.cfg”,使修改生效。

用户选定启动项并确认后,GRUB按配置将Linux内核与初始化内存盘载入内存。内核作为Linux系统核心,包含操作系统核心代码,负责系统资源管理、进程调度及硬件驱动提供;初始化内存盘为临时文件系统,存储启动根文件系统所需的驱动程序与工具。加载完成后,GRUB将系统控制权移交至内核,引导使命完成,系统进入内核初始化阶段。

步骤5:切换用户空间与systemd启动

GRUB完成内核与初始化内存盘的加载后,Linux系统进入内核接管阶段,标志着系统从引导加载阶段正式过渡至内核初始化与用户空间构建阶段。

5.1 内核空间与用户空间的微妙转换
32位Linux系统中,每个进程拥有4GB虚拟地址空间,其中低3GB(0x00000000至0xBFFFFFFF)为用户空间,用于用户进程运行;高1GB(0xC0000000至0xFFFFFFFF)为内核空间,存储内核代码与数据结构,供所有进程共享。

内核空间作为操作系统的核心控制区域,运行在内核态的代码具备最高权限,可直接访问硬件设备、掌控系统资源;用户空间为普通应用程序的运行区域,运行在用户态的程序权限受限,无法直接操作硬件,需通过系统调用向内核请求服务。这种空间划分可有效隔离内核与用户程序,避免用户程序错误操作导致系统崩溃,显著提升系统稳定性与安全性。

系统从GRUB引导进入内核初始化阶段后,完成从引导程序环境至内核空间的切换。此时内核全面接管系统,依次完成中断处理机制初始化、内存管理体系构建、进程调度框架搭建等核心操作,为系统稳定高效运行奠定基础。

5.2 systemd:用户空间的“领航者”
内核完成初始化并挂载根文件系统后,将启动systemd作为首个用户空间进程,其进程ID(PID)固定为1。systemd作为用户空间的核心管理组件,承担系统进程与服务管理、剩余硬件检测、文件系统挂载、桌面环境启动等关键职责,是用户空间运行的核心枢纽。

systemd采用单元(Unit)管理模式,将系统中的服务、套接字、挂载点、设备等各类资源抽象为独立单元,实现统一管理。每个单元对应独立配置文件,详细定义单元属性、依赖关系及启动、停止方式。

以Nginx服务为例,其对应的systemd服务单元配置文件通常位于/etc/systemd/system/nginx.service或/usr/lib/systemd/system/nginx.service路径。配置文件中,[Unit]部分描述服务基本信息与依赖关系,例如“Description=The nginx web and reverse proxy server”定义服务描述,“After=network-online.target remote-fs.target nss-lookup.target”明确Nginx服务需在网络、远程文件系统、名称服务查找等目标单元启动后启动。

[Service]部分定义服务运行方式:“Type=forking”表示服务采用fork方式启动(主进程创建子进程后退出,子进程负责实际运行);“ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf”指定服务启动命令及配置文件路径;“ExecReload=/usr/sbin/nginx -s reload”与“ExecStop=/usr/sbin/nginx -s quit”分别定义服务配置重载与停止命令。

[Install]部分用于设置服务开机自启与依赖关系,“WantedBy=multi-user.target”表示该服务在多用户模式下被依赖,系统切换至multi-user.target目标时,Nginx服务自动启动。通过灵活的配置设计,systemd可精准控制各服务的启动顺序与运行状态,保障各类服务协同工作。

并行启动是systemd的核心特性之一,可显著提升系统启动效率。传统初始化系统中,服务需按顺序逐个启动,单个服务故障可能导致后续服务启动中断;systemd可分析服务依赖关系,实现多个独立服务的并行启动,充分利用多核处理器优势,缩短启动耗时。例如,启动Nginx、MySQL、Redis等服务时,systemd可实现三者并行启动,无需等待前一服务完成,大幅提升启动效率。

systemd启动过程中,将按预设规则依次激活sysinit.target、basic.target、multi-user.target等目标单元:sysinit.target负责系统基础初始化,包括系统时钟设置、内核模块加载等;basic.target完成系统基本配置,包括基础文件系统挂载、日志服务启动等;multi-user.target为多用户模式目标,激活后启动用户登录、网络等相关服务,提供完整多用户工作环境。若系统启用图形界面,systemd将额外激活graphical.target目标单元,启动GDM、SDDM等显示管理器,呈现桌面环境,完成从内核空间至用户空间的过渡,为用户使用提供支撑。

步骤6:systemd的default.target统筹调度

systemd进程启动后,default.target目标单元成为系统服务启动与配置的核心,其激活标志着系统进入服务调度关键阶段。

systemd管理体系中,default.target替代传统SysVinit的运行级别概念,本质为符号链接,根据系统环境不同,通常指向multi-user.target(多用户文本模式)或graphical.target(图形界面模式)。服务器环境中,为保障稳定性与资源利用率,default.target多指向multi-user.target,系统启动后直接进入文本模式,专注于网络服务提供与后台任务处理;桌面环境中,default.target指向graphical.target,启动后自动加载显示管理器,呈现图形界面,满足用户便捷操作需求。

systemd激活default.target时,将递归处理其依赖关系,按顺序激活所需目标单元,如basic.target与sysinit.target。其中,sysinit.target负责系统基础初始化,包括内核模块加载(提供硬件支持)、交换空间启用(内存不足时存储临时数据)、本地文件系统挂载(保障系统数据访问与管理);basic.target在sysinit.target基础上完成系统进一步配置,包括日志服务启动(记录系统运行事件,便于故障排查)、系统消息总线启动(实现系统组件间高效通信)。

除激活依赖目标单元外,default.target还将启动一系列相关服务单元。这些服务单元以符号链接形式存放在/etc/systemd/system/default.target.wants目录,指向/usr/lib/systemd/system目录下的实际服务配置文件。例如,network.service负责网络接口配置,搭建系统通信通道;sshd.service提供安全远程登录服务,支持管理员远程管理系统。各服务将按自身配置与依赖关系有序启动,协同保障系统正常运行。

systemd的并行启动特性在本阶段得到充分体现。与传统SysVinit的顺序启动模式不同,systemd通过分析服务依赖关系,实现独立服务的并行启动,充分利用多核处理器优势,缩短系统启动耗时。例如,启动Nginx、MySQL、Redis等服务时,无需等待前一服务启动完成,可并行推进,加快系统进入可用状态的速度。

实际应用中,可通过systemctl命令查看default.target的依赖关系与服务状态:“systemctl list-dependencies default.target”可清晰展示default.target的依赖单元及层次关系;“systemctl status 服务名.service”可查看具体服务的运行状态,判断服务是否正常启动及是否存在故障,为启动过程中的故障排查提供支撑。

步骤7:环境配置!执行系统启动脚本

default.target目标单元激活后,systemd按配置依次执行rc.sysinit、rc.local等启动脚本。此类脚本承担系统环境变量设置、网络参数配置、swap分区启用、用户自定义服务加载等关键任务,为系统正常运行提供基础保障。

rc.sysinit脚本作为系统初始化的核心脚本,率先执行一系列基础关键任务。环境变量设置方面,该脚本将常用命令路径、系统配置参数等存入环境变量,便于系统与应用程序随时调用,例如设置PATH环境变量,实现可执行文件的快速查找,无需输入完整路径。网络参数配置方面,rc.sysinit读取/etc/sysconfig/network等配置文件,获取IP地址、网关等关键信息,完成网络初始化,搭建系统通信通道,保障系统正常接入网络。

swap分区启用是rc.sysinit的重要职责。swap分区作为系统虚拟内存,当物理内存不足时,系统将临时不用的数据存入swap分区,避免系统卡顿或崩溃。rc.sysinit将检查/etc/fstab文件中的swap分区配置,激活swap分区,为系统提供额外内存支持,保障系统运行流畅性。

rc.local脚本主要用于执行用户自定义启动任务,为用户提供个性化启动配置入口。用户可将自定义脚本或命令添加至rc.local,实现系统启动时的自动执行,例如自动挂载额外文件系统、启动自定义服务等。需注意,rc.local脚本需具备可执行权限,且命令与路径配置准确,否则将导致脚本执行失败。

不同Linux发行版的启动脚本执行顺序与配置方式存在差异。以CentOS系统为例,rc.sysinit位于/etc/rc.d/目录,启动早期执行,完成系统基础初始化;rc.local默认无执行权限,需通过“chmod +x /etc/rc.d/rc.local”命令赋予可执行权限,方可正常运行。Ubuntu系统中,rc.sysinit的功能分散至多个upstart脚本与systemd单元,rc.local通过rc-local.service服务管理,可通过“systemctl enable rc-local.service”命令启用自动执行功能。

各类启动脚本协同工作,完成系统环境的全面配置,确保系统运行所需的参数与服务全部到位,使Linux系统启动后快速进入最佳工作状态,提供高效可靠的服务支撑。

步骤8:呈现登录界面与系统就绪

所有启动脚本执行完毕后,systemd启动终端或图形界面服务,呈现登录界面,标志着Linux系统完成从通电至可用的全流程启动,正式进入可使用状态。

文本模式下,systemd启动getty服务,为各终端设备创建登录提示。用户输入正确的用户名与密码后,系统执行身份验证,验证通过后进入用户shell环境,用户可通过命令行执行各类操作,开展日常工作。

图形界面模式下,systemd激活display-manager.service服务,启动GDM、SDDM等显示管理器。显示管理器呈现登录界面,提供用户切换、语言选择等功能,用户输入账号密码并通过验证后,进入GNOME、KDE等桌面环境,通过图形化操作实现系统使用。

Linux启动流程各环节紧密衔接、环环相扣,协同运转。任一环节出现问题,均可能导致启动失败:例如BIOS/UEFI无法检测硬件,将导致引导设备无法找到;GRUB配置错误,将导致内核无法加载;systemd服务依赖冲突,将影响系统整体功能。

深入掌握Linux启动流程,不仅可实现启动故障的快速定位与高效解决,还可根据实际需求优化系统:服务器环境中,可精简不必要的服务、优化启动脚本,提升启动速度与稳定性;桌面环境中,可选择适配的显示管理器与桌面环境,构建个性化系统。