[linux]内核启动加载驱动文件的流程

Linux内核启动时,加载驱动文件并非简单地"扫描目录",而是遵循一个分阶段、有优先级的精密流程。

整个过程大致可以分为两大阶段:首先是内置驱动的静态初始化 ,然后是可加载模块的动态按需加载

第一阶段:内置驱动的静态"点名"

这部分驱动在编译内核时就被直接整合进了内核镜像 (vmlinuz)。它们会在内核启动的早期,按一个预先设定好的"点名册"顺序,被依次调用初始化函数。

1. 核心机制:initcall 优先级

Linux 内核定义了一个包含 14 个等级的优先级表(数字越小,优先级越高),每个驱动初始化函数在编写时,都会被指定一个等级。

优先级 初始化宏 典型用途
最高 pure_initcall 最基础的、不依赖其他服务的初始化
core_initcall 核心子系统(如调度、内存管理)
postcore_initcall 核心子系统之后的初始化
arch_initcall 架构相关的特定初始化
subsys_initcall 子系统(如 PCI, USB 总线驱动)
fs_initcall 文件系统相关的初始化
device_initcall 绝大多数设备驱动的默认等级
最低 late_initcall 所有其他初始化完成后才执行的驱动

你平时编写驱动时使用的 module_init(foo),在没有特别指定的情况下,就等同于 device_initcall,处于第 6 级。这就保证了像 USB 总线驱动这种核心框架(通常使用 subsys_initcall),一定会在具体的 USB 鼠标驱动(使用 device_initcall)之前被加载和初始化。

2. 同一优先级内的顺序:链接顺序决定

优先级相同的驱动,加载顺序由编译和链接的顺序 决定。内核构建系统 (Makefile) 中 obj-y 列表里排在前面的文件,其初始化函数会更早地被放入内存中的特定段里,因此也会更早被调用。所以,调整 Makefile 中文件的顺序,是精细控制同级别驱动加载顺序的有效方法

第二阶段:模块的动态"按需加载"

对于大量的设备驱动(尤其是非启动必需的硬件),Linux 会将其编译成独立的 .ko 文件 (kernel object),存放在 /lib/modules/ 目录下。这些模块由用户空间的 udev (设备管理器) 配合内核的热插拔机制来动态加载。

当内核检测到一个新设备(例如,你插入一个 U 盘)时,会通过热插拔事件 通知用户空间的 udevd 守护进程。udevd 根据预设的规则,调用 modprobe 命令去加载相应的驱动模块。modprobe 很智能,它会分析模块间的依赖关系,自动按正确的顺序加载所有必需的依赖模块。这种方式确保了只有在硬件实际存在时,其驱动才会被加载,极大地减少了内核的运行时体积。

阶段衔接:initramfs 的桥梁作用

这里存在一个"先有鸡还是先有蛋"的问题:根文件系统(存放 /lib/modules/sbin/init 等关键程序)本身就可能位于一个需要特殊驱动的设备上(如 SCSI 硬盘或 LVM 逻辑卷)。内核在没有加载根文件系统驱动的情况下,是无法挂载它并读取其中的模块的。

解决方案就是 initramfs (早期叫 initrd)。它本质上是一个很小的、包含必要驱动模块和初始化脚本的 cpio 压缩包。Bootloader (如 GRUB) 会将内核和 initramfs 一同加载到内存中。内核会先挂载这个内存中的虚拟根文件系统,在这个"迷你系统"里加载 SCSI 等必要的驱动模块,然后才能找到并挂载真正的根文件系统,最后启动 /sbin/init 进程,完成整个系统的启动。


总结:完整流程一览

  1. 开机BIOS/UEFI 自检,加载 Bootloader (GRUB)
  2. Bootloader内核initramfs 加载到内存。
  3. 内核解压并运行,执行 do_initcalls(),严格按照 initcall 优先级 列表调用所有内置驱动的初始化函数。数字越小,执行越早。
  4. 在调用到 subsys_initcall 级别时,会初始化各类总线驱动(如 pci_bus_type),为设备匹配做好准备。
  5. 总线驱动注册时,或设备被枚举到时,会触发 matchprobe 机制,完成设备与驱动的绑定,设备由此可用。
  6. 为了加载根文件系统 ,内核利用 initramfs 中的驱动,并挂载真正的根文件系统。
  7. 根文件系统挂载后,内核启动 /sbin/init 进程(通常是 systemd),系统进入用户空间启动阶段。
  8. 用户空间的 udev 守护进程根据内核发现的硬件事件,动态调用 modprobe 加载 /lib/modules/ 下的可加载模块,完成后续所有硬件的驱动加载。
相关推荐
一拳一个娘娘腔2 小时前
CVE-2026-31431 — “Copy Fail“ 深度拆解
linux·安全
Code-keys2 小时前
ARM NEON SIMD 编程实战:从音频信号处理到AI算子研发实战
arm开发·音视频·信号处理
麦麦麦当劳大王2 小时前
Linux SSH服务端配置指南
linux·运维·服务器·ssh
Yiyaoshujuku2 小时前
化学谱图数据API接口,数据字段一览!
linux·服务器·前端
__Witheart__2 小时前
make menuconfig 使用全流程
linux·ubuntu·rockchip
2601_951645783 小时前
Linux 编程语言全解析:C、C++、Python、Go、Rust 谁更强?
linux·python·go·c·编程语言
阿坤带你走近大数据3 小时前
Linux中管道符的作用
java·linux·服务器
Fcy6483 小时前
Linux下 进程信号初识和信号的产生
linux·运维·信号的产生
爱装代码的小瓶子3 小时前
安工大Linux考点分类真题解析(含知识点是试卷原题了)
linux·服务器·网络·c