先说结论:dmesg 是 Linux 系统排障过程中最值得优先查看的工具,没有之一。
我在日常运维中遇到系统异常,第一反应往往不是翻各种应用日志,而是先敲一条 dmesg 看看内核说了什么。硬件有没有被识别、驱动加载是否正常、磁盘有没有报 I/O 错误、内存是否触发 OOM,这些问题内核基本都会在日志里留下痕迹,只是很多人还没找到正确的位置去看。
这篇文章不打算写成翻译 man 手册的形式,而是从实际排查的角度,把 dmesg 的工作原理、常用参数、输出解读,以及我踩过的坑和总结的实战经验一次讲清楚。无论你是刚接触 Linux 的新人,还是有一定经验的运维工程师,都能从中找到可以直接上手的内容。
1. dmesg 的整体设计与核心思路
1.1 内核环形缓冲区:飞机黑匣子的 Linux 版
要理解 dmesg 为什么"什么都知道",关键得先明白内核日志的存储机制。
Linux 内核在启动和运行过程中会产生大量消息:检测到哪块硬盘、哪个 PCI 设备加载了哪个驱动、哪个文件系统挂载失败、哪个进程触发了内核异常,等等。这些消息并不是直接写入磁盘文件,而是写入内存中的一块环形缓冲区(ring buffer)。环形缓冲区的特点是空间固定,写满之后新的消息会覆盖最旧的消息,就像视频网站的弹幕列表一样,永远只保留最近的一段内容。
这个设计有个天然优势:系统启动早期,存储设备还没初始化,磁盘文件系统也未必可用,内核日志如果非要写文件,反而会因为依赖未就绪而丢失。写到内存缓冲区就完全不存在这个问题,不管后面系统处于什么状态,只要内存还在,就能通过 dmesg 读出来。这也是为什么很多"启动起不来""开机黑屏"的问题,最终要靠 dmesg 来定位——它记录了从内核第一个动作开始的所有信息。
1.2 为什么不用 journalctl 代替 dmesg
现在很多用 systemd 的发行版(CentOS 7+、Ubuntu 16.04+ 等)都有 journalctl,它也能查看内核日志,所以有人会问:那还有必要单独学 dmesg 吗?
有必要,而且很有必要。两者的本质区别在于:
- dmesg 直接读取内核环形缓冲区,不依赖任何用户态服务,拿到的是最原始的内核输出。
- journalctl 读的是 systemd-journald 服务持久化后的日志,它虽然也包含内核消息,但经过了用户态服务的采集和加工。
这带来两个实际影响:第一,如果系统在非常早期的阶段就崩溃,systemd 可能还没来得及运行,journalctl 里自然什么都没有,但 dmesg(或者在 GRUB 里配置串口输出后)依然能看到内核的信息;第二,dmesg 的输出更原始、更直接,做内核层面的故障定位时效率更高。
当然,dmesg 也有明显的短板:环形缓冲区容量有限,新消息会覆盖旧消息。如果系统运行时间很长,启动早期的日志可能已经被冲掉。而 journalctl 可以落盘保存很久,适合追溯历史。所以我的习惯是两者配合使用:现场快速排查看 dmesg,追溯历史问题查 journalctl。
1.3 dmesg 最擅长的排查场景
根据我多年的运维经验,下面这些情况几乎都是 dmesg 的主场:
- 硬件识别异常:U 盘插上没反应、新加的 PCIe 网卡系统不识别、外接显卡驱动加载失败。
- 存储设备故障:磁盘报 I/O 错误、RAID 卡掉盘、SATA 线缆接触不良、文件系统挂载失败。
- 驱动问题:自己编译的内核模块加载后系统异常,或驱动与硬件不匹配。
- 内核级资源问题:内存不足触发 OOM killer、进程被系统强制杀掉。
- 网络设备异常:物理网卡反复 up/down、链路协商失败、网卡队列中断异常。
只要你怀疑"问题发生在操作系统内核这一层",优先看 dmesg 基本没错。它能帮你把"硬件的锅"和"软件的锅"快速区分开,避免在业务层白费力气。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心用法与参数解析
2.1 基础命令:先学会这几条就够用
dmesg 的基本用法非常简单,新手掌握下面几条就可以应对大部分场景:
bash复制# 查看全部内核日志(最常用)
dmesg
# 查看最新 20 条日志
dmesg | tail -20
# 实时监控新产生的内核日志(类似 tail -f)
dmesg -w
# 按关键字过滤日志
dmesg | grep -i usb
dmesg | grep -i error
dmesg | grep -i "out of memory"
# 分页查看,方便阅读
dmesg | less
这里我特别想强调 dmesg -w 的实用价值。当你插入一个 U 盘、接入一个新硬件,或者进行某个可能导致内核报错的操作时,这种"先开启监听、再触发操作"的方式能让你精准地捕捉到问题发生瞬间的内核输出,而不是在几百行历史日志里大海捞针。
2.2 时间戳换算:-T 参数为什么重要
默认情况下,dmesg 输出的时间戳是相对系统启动的时间,单位是秒:
code复制[12345.678901] usb 1-1: USB disconnect, device number 3
这个数字看起来不直观,要换算成具体时间得自己算。如果你更习惯看人类可读的时间格式,用 -T 参数即可:
bash复制dmesg -T
输出会变成类似:
code复制[Fri Oct 13 10:24:33 2025] usb 1-1: USB disconnect, device number 3
这里有个细节值得注意:dmesg 的 -T 是按当前系统时间来计算相对时间戳的,所以它只对当前会话中的日志比较准确。如果系统时钟在运行过程中被 NTP 调整过,早期日志换算出的绝对时间可能会有偏差。排查时不要死抠日志里的绝对时间,结合 uptime 显示的运行时长来判断事件发生的相对位置,往往更可靠。
2.3 按级别和类型过滤:-l 和 -f 参数
内核日志是有"级别"和"类型"之分的,dmesg 给你提供了精确的过滤手段。
- 按级别过滤(日志的重要程度):
dmesg -l err只看错误级别,dmesg -l emerg,alert,crit,err组合查看更严重的级别。 - 按类型过滤(日志的来源子系统):
dmesg -f kern只查看内核自身的消息,dmesg -f daemon查看用户态守护进程的消息。
实际使用中,我最常用的组合是:
bash复制# 只看错误级别,且不看用户态消息
dmesg -l err -f kern
# 只看与硬件相关的警告和错误
dmesg -l warn,err | grep -iE "usb|pci|sda|nvme"
日志级别从高到低分别是:emerg(系统不可用)、alert(必须立即处理)、crit(严重)、err(错误)、warn(警告)、notice(正常但重要)、info(信息)、debug(调试)。日常排查重点关注 err 和 warn 级别基本就够了,info 级别太多,debug 级别平时更是看不到。
2.4 读取权限:为什么有的机器 dmesg 报错
在某些新版本系统上,普通用户执行 dmesg 可能会看到类似 dmesg: read kernel buffer failed: Operation not permitted 的报错。这不是命令坏了,而是内核参数 kernel.dmesg_restrict 被设置为 1,限制了非特权用户读取内核日志。
如果你确定系统安全策略允许,临时关闭限制可以用:
bash复制sudo sysctl -w kernel.dmesg_restrict=0
永久修改则写入 /etc/sysctl.conf 或 /etc/sysctl.d/ 下的配置文件:
bash复制echo "kernel.dmesg_restrict=0" > /etc/sysctl.d/99-dmesg.conf
sysctl -p /etc/sysctl.d/99-dmesg.conf
从安全角度看,限制 dmesg 读取确实能避免内核日志中的敏感信息泄露,生产环境建议保持默认严格策略,确需排查时用 sudo 执行即可。
3. 实战案例:用 dmesg 定位系统问题
3.1 案例一:U 盘插入无反应,如何锁定故障点
有一次我在一台 CentOS 7 服务器上插入 U 盘,系统完全没有反应,lsblk 里看不到任何新设备,桌面环境也没有弹窗。遇到这种情况,第一件事就是开启 dmesg 实时监听,再重新插拔 U 盘:
bash复制dmesg -w
重新插拔后输出如下:
code复制[ 1234.567890] usb 2-1: new high-speed USB device number 5 using ehci-pci
[ 1234.678901] usb 2-1: device descriptor read/64, error -71
[ 1234.789012] usb 2-1: device descriptor read/64, error -71
[ 1234.901234] usb 2-1: new high-speed USB device number 6 using ehci-pci
[ 1234.912345] usb 2-1: device descriptor read/64, error -71
看到连续多个 error -71 就要注意了。错误码 -71 对应的是 EPROTO(协议错误),说明 USB 设备在枚举阶段没能正确响应主机的请求。
这一类故障,原因通常有两种:一是 U 盘本身主控异常或者供电不足;二是主板的 USB 控制器或线缆有问题。当时我换了一个 U 口,日志立刻变成了正常的枚举流程:
code复制[ 1235.012345] usb 2-2: new high-speed USB device number 7 using ehci-pci
[ 1235.234567] usb 2-2: New USB device found, idVendor=0781, idProduct=5581
[ 1235.345678] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
问题定位完成:不是服务器的问题,而是那个 USB 口供电或接触不良。整个排查不到两分钟,如果没有 dmesg,你可能得花很长时间猜测是系统问题还是硬件问题。
实操要点:遇到 USB 设备不识别,按这个顺序排查——先看 dmesg 有没有"new USB device"这行枚举日志;没有,说明物理链路就没通;有但报错,确认错误码(-71 常见、-110 是超时、-32 是断连);最后换口换线换设备,逐层对比日志差异。
3.2 案例二:磁盘 I/O 错误与文件系统异常
磁盘问题是最让人头疼的,表现形式非常多样:应用卡顿、系统日志报错、文件读取失败。但 dmesg 往往能一针见血。
有一次我负责的一台数据库服务器突然出现大量慢查询,应用日志里没有任何业务异常,但系统整体负载偏高。我执行 dmesg -T | tail -50,很快发现了线索:
code复制[Fri Oct 13 14:32:11 2025] sd 1:0:0:0: [sdb] Spinning up disk...
[Fri Oct 13 14:32:11 2025] sd 1:0:0:0: [sdb] Read Reconfiguration failed
[Fri Oct 13 14:32:11 2025] sd 1:0:0:0: [sdb] Spinning up disk...
[Fri Oct 13 14:32:11 2025] sd 1:0:0:0: [sdb] Read Reconfiguration failed
[Fri Oct 13 14:32:11 2025] mpt2sas0: aborting command: scsi 1:0:0:0
[Fri Oct 13 14:32:11 2025] sd 1:0:0:0: [sdb] tag#21 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
[Fri Oct 13 14:32:11 2025] sd 1:0:0:0: [sdb] Sense Key : Hardware Error
[Fri Oct 13 14:32:11 2025] sd 1:0:0:0: [sdb] Add. Sense: Internal target failure
[Fri Oct 13 14:32:12 2025] sd 1:0:0:0: [sdb] Write(10) failed: Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
Hardware Error 和 Internal target failure 两个关键词,基本坐实了物理磁盘存在问题。后面进一步用 smartctl -a /dev/sdb 确认,果然硬盘 reallocated sector count 已经大量增长,属于典型的坏道增多。最终更换硬盘后,系统负载恢复正常,慢查询也消失了。
实操要点:磁盘相关常见错误码和含义值得背下来——Hardware Error 一般说明物理层有问题,I/O error 可能是线缆、也可能是盘片问题,Medium Error 基本确定是坏道或介质损坏,No Media 则说明没检测到介质(比如光驱里没盘)。看到 FAILED Result 这一行时,重点看 Sense Key 和 Add. Sense 两项描述,那是厂商返回的具体原因。
3.3 案例三:OOM Killer 日志解读
内存被耗尽时,内核的 OOM(Out Of Memory)机制会杀掉占用内存最多的进程来保住系统。很多时候业务进程莫名其妙没了,就是被 OOM killer 干掉的。
bash复制dmesg -T | grep -i "out of memory"
输出可能长这样:
code复制[Fri Oct 13 03:17:45 2025] java: invoked oom-killer: gfp_mask=0x100cca, order=0, oom_score_adj=0
[Fri Oct 13 03:17:45 2025] java: cpuset=/ mems_allowed=0
[Fri Oct 13 03:17:45 2025] Out of memory: Killed process 23456 (java) total-vm:8345678kB, anon-rss:3456789kB, file-rss:1234kB, shmem-rss:0kB
[Fri Oct 13 03:17:45 2025] oom-killer: gfp_mask=0x100cca, order=0, oom_score_adj=0
invoked oom-killer 说明某个进程申请内存时触发了 OOM 逻辑,Killed process 则明确告诉你哪个进程被杀了。上面这个例子就是 Java 进程吃掉了大量内存,最终系统选择杀掉它来释放资源。
这里有个很容易被忽视的点:OOM killer 的触发条件不是物理内存用完,而是内存耗尽到无法满足当前进程的申请请求。有时候是物理内存确实不够,有时候是 /proc/sys/vm/overcommit_memory 策略导致申请失败。看到 OOM 日志,先 free -h 看当前内存使用,再 ps aux --sort=-rss | head 看谁占内存最多,基本能判断是扩容还是调参的问题。
实操要点:如果 OOM 频繁出现,不要只靠 dmesg 反复确认,要主动评估是否需要调整 JVM 堆内存参数、优化应用内存占用,或者给系统增加 swap。dmesg 的任务只是帮你快速定位"发生了什么",后续治理还得靠其他手段。
3.4 案例四:持续监控内核日志
故障不一定是爆发式的,也可能是间歇性的。比如网络设备每隔一段时间就掉线一次,每次都只持续几秒,人工盯着 dmesg 看根本盯不住。
这种场景我通常会写一个简单的监控脚本,把内核错误持续重定向到文件:
bash复制#!/bin/bash
while true; do
dmesg -T | grep -iE "error|fail|timeout" >> /var/log/kernel-monitor.log 2>&1
sleep 30
done
更轻量的方式是直接使用 dmesg -w 配合 ts 工具:
bash复制dmesg -w -T | while read line; do
echo "$(date +%F_%T) $line" >> /var/log/kernel-live.log
done
这种做法在嵌入式开发和无人值守服务器上很有用,日志文件积累一段时间后再集中分析,往往能找到规律。比如我之前排查一个偶发性的网卡丢包问题,就是在连续两天的 /var/log/kernel-live.log 中,发现每次掉线前都有一条 link down 记录,而掉线时间点恰好和机房空调压缩机的启动时间吻合,最终确认是温控设备干扰了电源质量。
4. 常见问题与排查技巧实录
4.1 日志太多,怎么快速定位关键信息
dmesg 的输出量可能非常大,尤其在硬件丰富的机器上,一次 dmesg 可能刷出几千行。盲目去翻不但效率低,还容易漏掉关键错误。
我的过滤思路是分两步走。
第一步,先按严重级别过滤,只保留 warn 和 err:
bash复制dmesg -l warn,err > /tmp/kernel-errors.txt
wc -l /tmp/kernel-errors.txt
第二步,在过滤结果里按子系统关键字搜索:
bash复制grep -iE "usb|pci|sda|nvme|eth|wlan" /tmp/kernel-errors.txt
这样能把几百行压缩到几十行,关键信息一目了然。如果系统启动过程中就有问题,比如驱动加载失败,建议先看完整的 dmesg | less,加上时间戳后用 / 搜索 fail|error|WARNING 逐个定位。
4.2 环形缓冲区溢出,日志被覆盖怎么办
dmesg 最大的局限在于缓冲区容量。对于繁忙的生产系统,如果内核消息产生速度很快,早期的重要日志可能在一两个小时内就被覆盖掉。碰到这种情况,有几种补救办法。
一是调整缓冲区大小。内核参数 kernel.printk 控制消息传递的级别,但环形缓冲区大小由 CONFIG_LOG_BUF_SHIFT 编译选项决定,运行时不方便直接改。不过可以通过 sysctl 查看相关参数,部分新版内核支持动态调整 dmesg_restrict 之外的行为。
二是提前做好日志持久化。这是更推荐的方案:让内核消息实时写入文件。在 systemd 环境下,journalctl -k 会持久化内核日志;在非 systemd 环境,可以通过调整 syslog 服务收集 kern.* 级别的消息到 /var/log/kern.log(Debian/Ubuntu 默认就有,CentOS 默认 /var/log/messages 中也会包含)。
三是启用串口或 netconsole。对于嵌入式开发和关键服务器,把内核日志通过串口输出到另一台机器,或者用 netconsole 发送到远端日志服务器,即使本地系统崩溃,日志也还在。这个配置稍复杂,但一旦用上,排查疑难杂症时的价值非常大。
4.3 时间戳和实际时间对不上
dmesg 默认用的是"开机以来的秒数",-T 把它换算成绝对时间。但如果系统休眠过、或者 NTP 进行了大幅时间调整,换算结果就可能不准。另外,个别虚拟机在恢复快照后,内核时钟和宿主机时间可能出现偏差,也会导致换算异常。
我在实际使用中,习惯用 dmesg | head -1 看一下第一条日志的时间戳,然后和 uptime -s(系统启动时间)对比。如果偏差明显,就别纠结绝对时间了,用相对时间戳定位事件发生的先后顺序更靠谱。比如:
bash复制[ 1234.567] device eth0 link down
[ 1234.590] device eth0 link up
这两条日志间隔只有 0.023 秒,说明链路抖了一下又恢复了,结合 uptime 计算的大致时刻,就能判断大概是什么时间发生的。
4.4 慎用 dmesg -c 清空缓冲区
dmesg -c 可以在输出日志的同时清空环形缓冲区。这个命令在某些场景(比如你不希望别人看到之前的日志)有点用,但日常排查时我强烈建议不要用。
原因很简单:一旦清空,系统启动早期的所有内核信息就彻底丢了,任何后续分析都只能基于清空后产生的日志。而且如果你在生产环境的共享机器上执行,就等于销毁了排障依据,严重时会影响同事乃至整个团队的定位工作。
如果你确实需要"从某个时间点开始记录",更安全的做法是记录当前 dmesg 的行数标记,而不是清空缓冲区。比如:
bash复制wc -l < <(dmesg)
# 记录当前行数,后续只看新增部分
4.5 内核模块加载失败怎么查
有时候我们自己编译了一个内核模块(.ko 文件),insmod 时却提示失败,什么信息都没有。这时候看 dmesg,答案往往就藏在最后几行:
bash复制dmesg | tail -10
可能的输出:
code复制[ 1234.567] mymodule: disagrees about version of symbol module_layout
[ 1234.567] mymodule: Unknown symbol __kmalloc (err -2)
disagrees about version of symbol 说明模块内核版本不匹配,需要重新用当前内核头文件编译;Unknown symbol 说明模块依赖的某个函数在当前内核里不存在,可能是内核配置缺少相关选项,也可能是模块依赖的其他模块没有先加载。
这类问题在嵌入式开发和内核驱动调试中非常常见。我自己的经验是,编译模块时一定要确保用的是目标内核版本对应的头文件,交叉编译时更要注意 ARCH 和 CROSS_COMPILE 环境变量是否配置正确。dmesg 在这里就是最直接的"裁判",它告诉你内核到底为什么不接受这个模块。
5. 几个容易踩的坑与补充建议
5.1 误把 dmesg 当普通命令行工具
dmesg 本身是一个用户态程序,但它读取的内容直接来自内核。这意味着:dmesg 永远只反映内核自身的状态,不代表用户态程序的行为。比如你启动了一个 Java 服务,日志显示 OutOfMemoryError,但 dmesg 里并没有对应的 OOM 记录——因为这是 JVM 堆内存不足,而不是系统物理内存不足,内核没有参与,当然不会有日志。
这个区别非常重要。很多新手把 dmesg 当作万能日志,什么都往里找,找不到就怀疑系统有问题。实际上,dmesg 的核心定位是"内核诊断工具",用户态程序的复杂业务逻辑,它管不着。
5.2 dmesg 输出中的重复信息未必是故障
在某些场景下,dmesg 会输出大段看起来"重复"的内容,比如连续多次的 usb 2-1: device descriptor read/64, error -71。这种重复不一定表示故障恶化,而可能是内核在自动重试。USB 设备枚举失败后,内核通常会尝试多次,每次尝试都会产生一条日志。判断是否为真故障,要看最终的枚举结果有没有成功。
同样,RAID 卡在某些情况下会周期性刷新状态,dmesg 里偶尔出现一两条 notice 级别的消息也很正常。看日志要抓重点,别被重复信息带偏。
5.3 内核日志里的设备名不一定稳定
dmesg 看到的设备名(如 sda、sdb)在内核里是按发现顺序命名的,不一定和物理位置绑定。比如机器上有多块磁盘,重启后因为 BIOS 枚举顺序变化,sda 和 sdb 可能互换身份。这在排查磁盘故障时特别容易误判。
要准确对应物理设备,建议用 /dev/disk/by-id/ 或 /dev/disk/by-uuid/ 下的符号链接来定位。比如:
bash复制ls -l /dev/disk/by-id/ | grep sda
然后再回到 dmesg 里对照设备的序列号(serial)信息,才能确定到底坏的是哪一块物理盘。
5.4 内核日志级别的优先级需要理解
内核日志级别的高低影响它能否被写到控制台,也影响会不会出现在某些日志文件里。默认情况下,kernel.printk 的值通常是 4 4 1 7,意思是:控制台只显示优先级小于 4(即 err、crit、alert、emerg)的消息,而所有级别都会记录到缓冲区。所以你在 dmesg 里看到的 info 级别消息,控制台上可能根本不会显示。
如果你想在系统启动时看到更多调试信息,可以通过 GRUB 启动参数 loglevel=7 或 ignore_loglevel 来调整。这个技巧在排查启动早期问题时很实用,尤其是那些"开机到一半就卡死"的场景。
写在最后的一点经验
回到开头那句话:dmesg 是我排查 Linux 系统问题时最信任的起点信息源。这几年的工作下来,我形成了一套固定的排障节奏——遇到内核层、硬件层的异常,先 dmesg -T | tail -50 看最近发生的消息,再 dmesg -l err -f kern 挑出错误级别的关键信息,结合 uptime、free、df 等基础命令形成初步判断,最后再决定是否深入排查。
日常使用中我还建议养成一个习惯:每隔一段时间手动翻一翻 dmesg 的输出,哪怕系统看起来一切正常。很多硬件隐患,比如磁盘坏道增多、内存纠错次数上升、网卡收发错误率异常,在 dmesg 里早有苗头。等到业务真正受影响再去查,往往已经很被动了。
最后再分享一个实用的小技巧:dmesg 的输出可以配合 ts 命令(来自 moreutils 包)加上更精确的时间戳,方便做日志对比。不过这只是锦上添花,真正让你少走弯路的,还是对内核日志的敏感度和持续的排查实践。
