工业边缘设备突然重启、串口看不到输出、网关离线后自动恢复、USB 转串口设备消失、eMMC 写入异常、进程被 OOM Killer 杀掉,这些问题往往最早出现在内核日志里。dmesg 是查看 Linux 内核环形缓冲区的入口,适合快速拿到驱动、硬件、内存、块设备、网络和内核异常的第一现场。
但 dmesg 不是完整的日志系统。内核环形缓冲区容量有限、重启后丢失、日志可能被高噪声覆盖,容器里看到的也是宿主机全局内核日志。工程化使用时必须回答三个问题:怎么过滤出有效证据;怎么把关键日志持久化;怎么把内核日志与业务指标、告警和时间线关联起来。
本文按现场排障顺序整理 dmesg 的进阶用法,并结合 journalctl、rsyslog、sysctl 和工业边缘运维场景,构建一条从采集、过滤、留存到告警的闭环。
一、先理解 dmesg 能看到什么
dmesg 读取的是内核日志环形缓冲区,主要来源包括:
- 内核与驱动初始化信息;
- 硬件错误、MCE、EDAC、I/O 错误;
- 内存不足与 OOM Killer 记录;
- 网卡、USB、串口、块设备驱动事件;
- 内核 WARN / Oops / Panic;
- 通过
/dev/kmsg写入的用户态或容器日志。
它有几个重要边界:
| 边界 | 工程含义 |
|---|---|
| 环形缓冲区容量有限 | 高噪声场景会覆盖早期日志 |
| 默认保存在内存中 | 重启后 dmesg 现场通常丢失 |
| 不是完整系统日志 | systemd、应用、登录审计应看 journalctl 或应用日志 |
| 全局视角 | 容器内 dmesg 不能按容器隔离内核日志 |
| 权限可控 | kernel.dmesg_restrict 开启后需要 root 或相应 capability |
| 时间为单调时钟 | -T 转换的系统时间在休眠、时钟跳变场景要谨慎解读 |
因此,dmesg 适合现场快速判断,journalctl -k 与集中式日志才适合长期审计和事后追溯。
二、基础命令与常用参数
1. 快速查看
bash
# 查看内核环形缓冲区
dmesg
# 显示人类可读时间
dmesg -T
# 更适合人工阅读的输出
dmesg -H
# 只看内核 facility,通常与默认 dmesg 等价
dmesg -k
# 过滤错误级别
sudo dmesg -l err
# 过滤 warning 及以上常见级别,注意级别是列表匹配,不是数值区间
sudo dmesg -l emerg,alert,crit,err,warning
# 实时跟随
sudo dmesg --follow
# 只显示新产生的日志
sudo dmesg --follow-new
-l 的取值包括 emerg、alert、crit、err、warning、notice、info、debug。如果要看多个级别,需要显式列出;不同发行版的 util-linux 版本可能还有选项差异,可先执行:
bash
dmesg --help
dmesg --version
2. 参数口径
| 参数 | 含义 | 工程注意点 |
|---|---|---|
-T |
显示系统时间 | 受时钟跳变、休眠恢复影响 |
-H |
人类可读输出 | 常隐含分页,脚本中要谨慎 |
-k |
只看 kern facility | dmesg 默认也主要读取内核日志 |
-l |
按日志级别过滤 | 是列表匹配,不是"低于/高于" |
-f |
按 facility 过滤 | 可区分 kern、user、daemon 等 |
--since / --until |
按时间过滤 | 依赖 util-linux 版本,建议现场确认支持 |
--follow |
跟随读取 | 适合现场观察,不适合替代采集器 |
--follow-new |
只显示新日志 | 避免启动日志干扰 |
-n |
设置控制台日志级别 | 不改变缓冲区里已经记录的内容 |
-C |
清空缓冲区 | 排障前不要随手清空,会破坏证据 |
原文中把 dmesg -P 写成"不带前缀"是不准确的。-P 在 util-linux 中是 --nopager,用于禁止分页;-p 并不是现代 dmesg 的标准"err 及以上"过滤参数。过滤级别应使用 -l 或 journalctl -p。
3. 权限与安全
bash
# 查看是否限制非 root 读取
sysctl kernel.dmesg_restrict
# 读取 dmesg
sudo dmesg -T
工业设备不建议为了方便而长期关闭 kernel.dmesg_restrict。需要采集时,优先让日志采集组件以最小权限运行,或将内核日志统一交给 journald、rsyslog、Fluent Bit 等组件转发。
三、过滤出有效证据
1. 按时间过滤
如果当前系统安装的 util-linux 支持 --since / --until,可以直接过滤:
bash
dmesg -T --since "1 hour ago"
dmesg -T --since "2026-10-19 08:00:00"
dmesg -T --since "1 hour ago" --until "10 minutes ago"
更稳妥的跨启动方式是使用 journald:
bash
# 当前启动
sudo journalctl -k -b 0 --no-pager
# 上一次启动
sudo journalctl -k -b -1 --no-pager
# 今天以来的 warning 及以上
sudo journalctl -k --since today -p warning --no-pager
# 按正则搜索
sudo journalctl -k -b --grep "OOM|I/O error|Hardware Error" --no-pager
注意:journalctl -b -1 只有在 journald 持久化开启且历史日志未被清理时才可用。
2. 按级别过滤
bash
sudo dmesg -T -l emerg,alert,crit,err
sudo journalctl -k -b -p err --no-pager
sudo journalctl -k -b -p warning --no-pager
journalctl -p err 与 dmesg -l err 的语义不同。journalctl 的优先级过滤通常按阈值语义处理,而 dmesg -l 是显式级别列表匹配。脚本化采集时要统一口径,避免误以为二者完全等价。
3. 按 facility 过滤
bash
dmesg -f kern
dmesg -f user
dmesg -f kern,daemon
工业现场常见的干扰是应用或容器直接向 /dev/kmsg 写日志,导致真正的硬件错误被淹没。处理方式不是盲目调低内核日志级别,而是:
- 审计谁在写
/dev/kmsg; - 收敛应用直接写内核日志的行为;
- 对采集日志做级别与关键字分层;
- 将硬件错误、OOM、内核异常单独路由到高优先级通道。
四、典型故障排查
1. 硬件与 MCE 错误
bash
sudo dmesg -T | grep -iE 'hardware error|mce|machine check|edac|ecc'
sudo journalctl -k -b --grep 'Hardware Error|MCE|EDAC|ECC' --no-pager
重点看是否出现内存 ECC 错误、CPU Machine Check、PCIe AER、温度或电源相关异常。内核日志只能给出第一线索,还需要继续采集:
bash
sudo dmidecode -t memory
sudo ras-mc-ctl --summary
lspci -vv
sensors
如果只是偶发一次,需要结合发生时间、现场温度、供电、负载和机箱震动综合判断;如果错误持续增长,应按硬件故障处理。
2. OOM 与内存压力
bash
sudo dmesg -T | grep -iE 'out of memory|oom|killed process'
sudo journalctl -k -b --grep 'Out of memory|Killed process' --no-pager
# 查看 OOM 前后的完整上下文
sudo dmesg -T | grep -B 30 -A 40 -i 'killed process'
重点确认:
- 被杀进程是谁,是否是网关主进程;
- 进程 RSS 与系统可用内存;
- 是否存在 cgroup memory limit;
- 是否是页缓存、tmpfs、容器内存叠加导致;
- 是否有内存泄漏趋势;
- swap 是否存在以及是否适合该设备。
容器场景应同时查看 cgroup:
bash
cat /sys/fs/cgroup/memory.current
cat /sys/fs/cgroup/memory.max
cat /sys/fs/cgroup/memory.events
内核 OOM 与 cgroup OOM 的处理方向不同:前者通常要评估整机内存和现场负载,后者可能是单个容器 limit 配置过小。
3. 存储与 I/O 错误
bash
sudo dmesg -T | grep -iE 'i/o error|blk_update_request|medium error|nvme|mmc|ata|scsi'
sudo journalctl -k -b --grep 'I/O error|EXT4-fs error|XFS|mmc|nvme' --no-pager
需要同步确认设备健康和文件系统状态:
bash
lsblk -o NAME,TYPE,SIZE,ROTA,MOUNTPOINTS
df -hT
sudo smartctl -a /dev/sda
sudo dmesg -T --follow
工业边缘设备常见诱因包括 eMMC 磨损、SD 卡质量问题、供电不稳、高温、文件系统只读和写放大。不要只依据一条 I/O error 就更换硬件,要确认具体设备、扇区、错误频率和业务影响。
4. 网络链路异常
bash
sudo dmesg -T | grep -iE 'eth|link is|carrier|nic|phy|timeout|reset'
sudo journalctl -k -b --grep 'link|carrier|eth0|timeout' --no-pager
如果日志中出现反复 link down / link up、PHY reset、TX timeout,需要继续看:
bash
ip -s link
ethtool eth0
ethtool -S eth0
ss -s
重点区分硬件链路问题、驱动问题、电源休眠策略、交换机侧异常和现场网络抖动。
5. USB 与串口设备
bash
# 插入设备前先启动跟随
sudo dmesg -Tw
# 查看设备枚举与断开
sudo dmesg -T | grep -iE 'usb|tty|cp210|ftdi|ch34|cdc_acm'
工业现场要特别记录设备插入瞬间、断开瞬间和驱动绑定结果:
bash
lsusb -t
ls -l /dev/serial/by-id
udevadm info -a -n /dev/ttyUSB0
如果设备 ID 不断变化,应用应使用 /dev/serial/by-id 或 udev 规则绑定稳定路径,而不是硬编码 /dev/ttyUSB0。
6. 内核 Oops、WARN 与 Panic
bash
sudo dmesg -T | grep -iE 'bug:|oops:|panic|warning:|call trace|general protection'
sudo journalctl -k -b --grep 'BUG:|Oops:|panic|Call Trace' --no-pager
看到 Oops 或 Panic 时,先保存完整日志,再重启或恢复设备:
bash
sudo journalctl -k -b --no-pager -o short-precise > kernel-panic.log
uname -a > kernel-version.txt
如果配置了 kdump,还应保留 vmcore,并用 crash 或厂商工具进一步分析调用栈。
五、现场证据打包
排障时建议一次性保存以下信息,文件名包含设备编号和采集时间:
bash
TS=$(date +%Y%m%d-%H%M%S)
OUT=/tmp/edge-kernel-$TS
mkdir -p "$OUT"
sudo dmesg -T -P > "$OUT/dmesg.txt"
sudo journalctl -k -b --no-pager -o short-precise > "$OUT/kernel-journal.txt"
sudo journalctl -p err -b --no-pager > "$OUT/system-errors.txt"
uname -a > "$OUT/uname.txt"
uptime > "$OUT/uptime.txt"
ip -s link > "$OUT/ip-link.txt"
lsblk -o NAME,TYPE,SIZE,ROTA,MOUNTPOINTS > "$OUT/lsblk.txt"
df -hT > "$OUT/df.txt"
free -h > "$OUT/free.txt"
tar -czf "${OUT}.tar.gz" -C /tmp "edge-kernel-$TS"
这样能避免"重启以后证据消失"的被动局面。对无人值守站点,采集动作应内置到远程运维流程或故障自动上报机制中。
六、持久化:让内核日志跨重启留存
1. journald 持久化
推荐优先启用 journald 持久化,而不是自己写脚本长期执行 dmesg -w。
创建配置文件:
ini
# /etc/systemd/journald.conf.d/90-edge-kernel.conf
[Journal]
Storage=persistent
Compress=yes
SystemMaxUse=500M
MaxRetentionSec=30day
启用并检查:
bash
sudo mkdir -p /etc/systemd/journald.conf.d
sudo systemctl restart systemd-journald
# 确认持久化目录存在
ls -ld /var/log/journal
# 查看磁盘占用
journalctl --disk-usage
# 查询当前启动
journalctl -k -b 0 --no-pager | tail -n 20
如果 /var/log/journal 不存在,journald 在 Storage=auto 下通常只使用易失存储,重启后历史日志会丢失。存储空间有限的边缘设备要同时配置 SystemMaxUse 和保留周期,避免日志挤占业务数据空间。
2. rsyslog 单独落盘
如果站点已有 rsyslog 体系,可以把内核日志单独写入文件:
text
# /etc/rsyslog.d/90-edge-kernel.conf
kern.* /var/log/kern.log
重启并验证:
bash
sudo systemctl restart rsyslog
logger -p kern.warning "kernel log pipeline test"
tail -n 10 /var/log/kern.log
rsyslog 方案要同步配置轮转和磁盘上限:
text
/var/log/kern.log {
daily
rotate 14
compress
missingok
notifempty
copytruncate
}
对于大规模站点,更推荐将 journald 或 rsyslog 转发到集中式日志平台,本地保留短期证据,远端保留长期审计数据。
七、实时监测与告警
1. 现场实时观察
bash
sudo dmesg --follow-new
sudo journalctl -kf -n 0 --no-pager
长时间保存时不要用无节制的管道直接写文件:
bash
# 可用于短时间现场观察
sudo journalctl -kf -o short-precise | tee kernel-live.log
生产环境应使用 journald、rsyslog、Fluent Bit、Vector 等组件采集。它们自带缓冲、重试、轮转、格式化和集中转发能力,比手写 dmesg -w 管道可靠。
2. 简单关键字分层
bash
sudo journalctl -kf -n 0 -o short-precise |
while IFS= read -r line; do
case "$line" in
*"Hardware Error"*|*"Machine Check"*|*"EXT4-fs error"*|*"XFS"*"error"*|*"Out of memory"*|*"Killed process"*|*"BUG:"*|*"Kernel panic"*)
printf '%s\n' "[critical] $line"
;;
*"I/O error"*|*"link is down"*|*"usb"*"disconnect"*|*"over-current"*)
printf '%s\n' "[warning] $line"
;;
esac
done
真正接入告警时还需要增加:
- 关键字去重;
- 时间窗口内的频率限制;
- 设备、站点、版本标签;
- 值班升级策略;
- 恢复通知;
- 与 CPU、内存、I/O、网络、业务错误率联动判断。
否则一条周期性 USB 断连日志可能在深夜生成上百条告警,反而掩盖真正严重的硬件故障。
八、sysctl 与控制台级别
1. dmesg -n 的真实作用
bash
# 查看当前控制台日志级别
cat /proc/sys/kernel/printk
# 设置控制台日志级别为 7
sudo dmesg -n 7
# 只在控制台输出 emerg 级别,减少串口噪声
sudo dmesg -n 0
/proc/sys/kernel/printk 的四个数值分别对应:
| 位置 | 含义 |
|---|---|
| 第 1 个 | 当前控制台日志级别 |
| 第 2 个 | 未指定级别的默认消息级别 |
| 第 3 个 | 最小控制台日志级别 |
| 第 4 个 | 默认控制台日志级别 |
重点:dmesg -n 只影响哪些级别会输出到控制台,不会减少内核环形缓冲区记录,也不会改变 journald 采集到的日志。串口输出过多时可以调低控制台级别,但不建议为了"让屏幕干净"去清空环形缓冲区。
2. Panic 自动重启
ini
# /etc/sysctl.d/90-edge-recovery.conf
kernel.panic = 10
kernel.panic_on_oops = 1
应用配置:
bash
sudo sysctl --system
sysctl kernel.panic kernel.panic_on_oops
kernel.panic = 10 表示 panic 后延迟 10 秒重启;0 表示不自动重启。panic_on_oops = 1 会把 Oops 视为 panic 处理,能避免内核继续在不一致状态下运行,但可能把原本可恢复的问题变成服务重启,必须结合业务可用性要求评估。
3. 访问控制
ini
# /etc/sysctl.d/90-edge-dmesg.conf
kernel.dmesg_restrict = 1
开启后普通用户不能直接读取内核日志,有利于降低内核地址和设备信息暴露面。运维采集账号应有明确的授权和审计。
九、工业边缘的工程化实践
| 实践 | 建议做法 |
|---|---|
| 持久化优先 | 开启 journald persistent,并配置容量上限 |
| 集中转发 | 本地短期留存,平台侧长期审计 |
| 关键字分层 | 硬件、OOM、Panic、I/O、网络、USB 分级处理 |
| 保留上下文 | 不只保存 grep 结果,要保存前后日志和时间线 |
| 与业务关联 | 将内核日志与设备离线、指令失败、I/O 延迟对齐 |
| 保留版本信息 | 采集内核版本、驱动版本、硬件型号、系统启动时间 |
| 控制噪声 | 收敛应用直写 /dev/kmsg,告警做去重和限流 |
| 保护现场 | 排障前不清空 dmesg,重启前先保存证据 |
| 权限最小化 | 采集组件单独授权,不放开所有用户访问 |
| 定期演练 | 人为注入 USB 断连、网络抖动、内存压力,验证采集与告警链路 |
十、常见坑与修正
| 常见坑 | 实际影响 | 修正方式 |
|---|---|---|
| 以为 dmesg 日志重启后仍在 | 查上次故障时证据已丢 | 开启 journald 持久化或集中采集 |
dmesg -C 清空缓冲区 |
丢失最早硬件线索 | 排障前禁止清空,先保存日志 |
把 -n 7 当作"记录更多日志" |
它只影响控制台输出 | 调整日志级别、驱动 verbosity 或采集策略 |
| 只看 grep 后的单行结果 | 缺少调用栈、前后事件和设备上下文 | 保存 journalctl -k -b 全量日志 |
| 容器内直接看 dmesg | 内核日志是宿主机全局的 | 以宿主机采集为主,再关联容器 cgroup 与业务日志 |
| 告警关键字过宽 | 少量噪声就造成告警风暴 | 分级、去重、限流并联动业务指标 |
| 日志没有容量控制 | /var/log 写满导致服务异常 |
配置 SystemMaxUse、轮转和磁盘告警 |
| 忽略时间同步 | -T 时间无法与平台日志对齐 |
启用 NTP / PTP,并记录时钟同步状态 |
十一、TL;DR
Linux 内核日志排障的核心链路是:dmesg 看现场,journalctl -k 查启动与历史,journald 或 rsyslog 做持久化,集中日志平台做长期审计。常用入口包括 -T、-l、-f、--follow、--since;典型场景覆盖硬件错误、OOM、I/O 异常、网络抖动、USB 断连、Oops 和 Panic。
要特别记住:dmesg -n 控制的是控制台输出级别;dmesg -P 是禁止分页;跨启动日志依赖持久化;生产告警不能只靠一条 grep 管道。把内核日志与设备状态、业务指标、版本信息和远程运维流程关联起来,才能真正支撑工业边缘系统的长期稳定运行。
十二、下一步建议
- 开启 journald 持久化,并验证上次启动日志可查;
- 建立硬件、OOM、I/O、网络、USB、Panic 的关键字分层;
- 将内核日志接入集中式采集和告警平台;
- 为远程站点固化"故障证据自动打包"流程;
- 定期演练断网、断电、存储异常和内存压力场景。
如果在工业边缘运行时中评估内核日志采集、异常识别和远程运维能力,可以参考 Zenova EdgeOS 在日志留存、故障证据打包和运维审计方面的工程实践。