pstore(Persistent Storage)是 Linux 内核中的一个重要功能,专为应对系统崩溃而设计。当发生 panic 或 oops 这类严重错误时,pstore 能够将内核的日志信息(包括控制台输出、用户空间消息和函数跟踪信息等)保存到非易失性存储介质中。系统重启后,开发者可以检索这些“现场”日志,这对于分析嵌入式产品中难以复现的崩溃原因至关重要。
pstore 支持多种后端存储方式,常见的有:
- ramoops:将日志保存到一段预留的 RAM 区域。这是嵌入式系统中最常用的方式,因为它无需额外存储设备且速度快。其前提是硬件支持或通过设计保证系统重启时该段 RAM 内容不会丢失。
- flash:将日志保存到 MTD 设备,如 NOR Flash。
- block 设备:将日志保存到块设备的指定分区,如 eMMC 或 SD 卡。
- efi:在 UEFI 系统中,利用 UEFI 固件提供的变量存储。
pstore 子系统包含几个主要部分:console 用于保存内核控制台日志;pmsg 用于保存通过 pstore 字符设备写入的用户空间日志;ftrace 则用于保存函数跟踪信息。启用后,通常可以在 /sys/fs/pstore 目录下找到保存的日志文件。
对于 STM32MP257 这类嵌入式平台,通常首选 ramoops 作为后端。下面我们来看看如何在基于 Linux 6.1.82 内核的 STM32MP257 平台上配置并启用它。
内核配置
首先进入内核源码目录进行配置:
cd linux-6.1.82
make ARCH=arm multi_v7_defconfig
make menuconfig
在配置界面中,需要确保以下关键选项被启用:
File systems --->
Miscellaneous filesystems --->
<*> Persistent store support
<*> Log kernel console messages
<*> Log user space messages
<*> Persistent function tracer
<*> Log panic/oops to a RAM buffer
(1024) Maximum RAM buffer size (in KiB) for panic/oops log
(1024) Maximum RAM buffer size (in KiB) for general messages
Log panic/oops to a RAM buffer (in a region)
Device Drivers --->
Block devices --->
<*> Block device for pstore backend (如果计划使用eMMC/SD卡作为后端)
Memory Technology Devices (MTD) --->
Caching block device access to MTD devices (如果计划使用NOR/NAND Flash作为后端)
设备树配置
配置设备树是正确保留内存和声明 ramoops 节点的关键步骤。
保留内存区域
需要在设备树中预留一段物理内存供 pstore 使用,确保它不会被系统正常分配。在 reserved-memory 节点中添加:
// 在保留内存区域中添加
/reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
pstore_reserved: pstore@C0000000 {
reg = <0xC0000000 0x00100000>; // 在地址0xC0000000处保留1MB空间
no-map;
};
};
这里的地址和大小(0xC0000000 和 0x00100000)需要根据你的具体硬件内存布局进行调整,务必确保该区域不与其他驱动或系统内存冲突。
Ramoops 节点配置
在设备树的根节点下添加 ramoops 节点,并引用上面保留的内存区域:
/ {
ramoops {
compatible = "ramoops";
memory-region = <&pstore_reserved>;
record-size = <0x20000>; // 每个记录128KB
console-size = <0x40000>; // 控制台日志区256KB
pmsg-size = <0x20000>; // 用户消息区128KB
ftrace-size = <0x20000>; // 函数跟踪区128KB
max-reason = <3>; // 记录panic (2) 和 oops (3)
};
};
参数说明:
record-size:每次崩溃事件记录的大小。
console-size、pmsg-size、ftrace-size:分别为不同日志类型分配的空间。
max-reason:设定记录哪些级别的内核错误。<3> 表示记录 KMSG_DUMP_OOPS 和 KMSG_DUMP_PANIC。
使用 eMMC 作为后端(可选)
如果希望将崩溃日志持久化到 eMMC 等块设备,配置会稍有不同,例如可以启用 ECC 校验:
pstore: pstore {
compatible = "ramoops";
memory-region = <&pstore_reserved>;
record-size = <0x20000>;
console-size = <0x40000>;
pmsg-size = <0x20000>;
ecc-size = <16>; // 启用ECC
status = "okay";
};
不过,对于大多数嵌入式调试场景,ramoops 已经足够。
内核启动参数
在 U-Boot 引导阶段,需要在启动参数中指定 pstore 的后端:
setenv bootargs “console=ttySTM0,115200 root=/dev/mmcblk0p5 rootwait rw pstore.backend=ramoops”
挂载 pstore 文件系统
系统启动后,需要挂载 pstore 文件系统才能访问日志。可以创建一个启动脚本自动完成:
# /etc/init.d/pstore-mount
#!/bin/sh
mkdir -p /sys/fs/pstore
mount -t pstore pstore /sys/fs/pstore
当然,也可以选择手动挂载进行测试:
mount -t pstore pstore /sys/fs/pstore
验证配置
完成内核编译、设备树更新并系统启动后,可以通过以下命令验证 pstore 是否工作正常:
# 检查pstore相关配置是否已编译进内核
cat /proc/config.gz | gunzip | grep PSTORE
# 查看/sys/fs/pstore目录,如果挂载成功,发生崩溃后这里会有文件
ls -la /sys/fs/pstore/
# 警告:此命令会立即触发内核panic,仅用于测试,慎用!
echo c > /proc/sysrq-trigger
触发 panic 测试并重启后,检查 /sys/fs/pstore/ 目录下是否生成了新的日志文件(如 console-ramoops-0),这是验证整个配置是否成功的最直接方法。
常见问题解决
- 内存区域冲突:系统无法启动或出现异常。请仔细检查为 pstore 保留的内存地址是否与其它驱动(如 GPU、DSP 预留内存)或 Linux 内核可用内存范围重叠。这涉及到对硬件内存映射的深入理解。
- 记录不保存:崩溃后重启,
/sys/fs/pstore 目录为空。首先检查设备树中的 max-reason 参数是否设置为记录 panic。其次,确认硬件在重启时是否真的保持了预留 RAM 区域的内容,这取决于具体的板级设计(如是否有备用电源维持 RAM)。
- 文件系统挂载失败:提示文件系统类型错误或找不到。请确保在内核配置中不仅启用了
CONFIG_PSTORE,还启用了 CONFIG_PSTORE_FS 以支持文件系统接口。
完整的内核配置参考
以下是确保 pstore 核心功能及常用压缩算法可用的配置选项,供参考:
CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_FTRACE=y
CONFIG_PSTORE_RAM=y
CONFIG_PSTORE_BLK=y
CONFIG_PSTORE_ZLIB_COMPRESS=y
CONFIG_PSTORE_LZO_COMPRESS=y
CONFIG_PSTORE_LZ4_COMPRESS=y
CONFIG_PSTORE_ZSTD_COMPRESS=y
总而言之,pstore 是嵌入式 Linux 开发中一个极其强大的调试工具,它能捕获到系统崩溃前一瞬间的宝贵内核状态信息。正确配置后,相当于为你的设备安装了一个“黑匣子”。虽然本文以 STM32MP257 和 Linux 6.1.82 为例,但其原理和步骤对于大多数嵌入式 Linux 平台都具有通用参考价值。在复杂的嵌入式C/C++项目调试中,善用 pstore 能极大提升解决棘手崩溃问题的效率。如果你在配置过程中遇到其他问题,欢迎在技术社区如云栈社区的运维与测试板块与更多开发者交流探讨。