linux系统启动的硬件初始化耗时无法通过systemd工具直接记录,因为bios/uefi自检、pcie枚举、nvme复位等均发生在内核执行前;systemd-analyze time中的kernel时间仅到init进程创建为止,需借助uefi串口日志、printk.time=1、config_initcall_debug=y及清空dmesg缓冲区等方法捕获真实早期耗时。

Linux 系统启动阶段的硬件初始化耗时,不能靠 systemd 工具直接记录——它根本没接管那段流程。 你看到的 systemd-analyze time 里的 kernel 时间,只到 init 进程创建为止;而 BIOS/UEFI 自检、内存训练、PCIe 枚举、NVMe 控制器复位、USB 主机控制器初始化等动作,全在内核真正“开始执行”之前就完成了。想测这部分,得绕过 systemd,直连内核和固件层。
怎么拿到 BIOS/UEFI 和内核早期的时间戳
固件本身不输出可解析的耗时,但现代 UEFI 实现(如 Intel TianoCore、AMD AGESA)会在串口或内存日志区写入 TimeStamp 记录。你需要:
- 开机时按
F2或Del进 BIOS/UEFI 设置,打开Serial Port Console或Debug Log(名称因厂商而异),并确保波特率设为115200 - 用 USB-TTL 串口线接主板 debug header,
screen /dev/ttyUSB0 115200捕获从上电开始的原始输出 - 观察第一行是否类似
[0.000000] Linux version...—— 这个0.000000是内核时钟起点,不是通电时刻;真正的“硬件初始化完成”时间点,往往藏在前几行 UEFI 日志里,比如ExitBS: ExitBootServices() called或ACPI: SSDT ... loaded后紧跟着的毫秒计数
内核启动早期阶段怎么加时间标记
内核 start_kernel() 之前的时间不可编程干预,但你可以让内核在关键子系统初始化时打更细的时间戳:
- 启用
printk.time=1内核参数:让所有printk输出带绝对时间戳(单位:秒.纳秒),再配合dmesg -t | head -50查看前 50 行,找ACPI:、PCI:、NVMe:、usb:等关键字首次出现的位置 - 编译内核时打开
CONFIG_PRINTK_TIME=y和CONFIG_INITCALL_DEBUG=y:后者会让每个__initcall函数执行前后都打印耗时,比如pci_subsys_init+0x0/0x80 returned 0 after 12439 usecs - 不要依赖
systemd-analyze blame查硬件驱动耗时——它连pci_bus_add_device这类动作都看不到,只统计 unit 级别
为什么 systemd-analyze time 的 kernel 时间不准
这个值只反映从内核第一条指令跳转到 rest_init() 创建 init 进程的时间,中间跳过了:
- 内核镜像解压(尤其压缩率高的
vmlinuz在慢闪存上可能耗几百毫秒) - 内核重定位(
relocate_kernel阶段,ARM64 上常见于 UEFI 启动) - early_printk 初始化串口前的盲区(
[ 0.000000]之前那几十毫秒完全无日志) - Secure Boot 验证签名、TPM 测量 PCR 等可信启动开销,这些发生在内核加载前,
dmesg根本不记录
实操中容易被忽略的三个硬伤
多数人以为 dmesg 就是全部,其实:
-
dmesg缓冲区默认只有 16MB,早期内核日志可能被后续输出刷掉——用sudo dmesg -D; sudo dmesg -C; sudo dmesg -E清空并锁定缓冲区再重启,才能保全最开头的日志 - 某些主板在 Fast Boot 开启时会屏蔽大部分 UEFI 日志,即使开了 Serial Console 也只输出极简信息;必须进 BIOS 关掉
Fast Boot才能看到完整硬件枚举过程 - Intel 平台上的
TSX、SGX等扩展初始化可能单独耗时 200ms+,但不会出现在任何标准日志里,需用rdmsr 0x34(IA32_TSC_DEADLINE)等 MSR 寄存器读取,普通用户几乎无法介入











