Linux 内核日志级别kern_levels与 dmesg 用法

1. 内核日志级别(Log Level)

Linux 内核定义了 8 个日志级别(定义在 include/linux/kern_levels.h):

级别 宏定义 含义 说明
0 KERN_EMERG 系统不可用 系统即将崩溃
1 KERN_ALERT 需要立即处理 必须立即采取行动
2 KERN_CRIT 严重错误 硬件/软件严重故障
3 KERN_ERR 错误 一般错误条件
4 KERN_WARNING 警告 可能出问题的情况
5 KERN_NOTICE 正常但重要 正常但值得注意的信息
6 KERN_INFO 信息 一般性信息
7 KERN_DEBUG 调试 调试级别的信息

数字越小,优先级越高。

2. 控制台日志级别

内核通过 /proc/sys/kernel/printk 控制哪些消息输出到控制台。该文件包含 4 个值:

bash 复制代码
cat /proc/sys/kernel/printk
# 输出示例: 4    4    1    7
#           |    |    |    |
#           |    |    |    +-- default_console_loglevel(新打开控制台的默认级别)
#           |    |    +------- minimum_console_loglevel(控制台最小允许级别)
#           |    +------------ default_message_loglevel(printk 默认消息级别)
#           +----------------- console_loglevel(当前控制台日志级别阈值)

只有级别值 < console_loglevel 的消息才会输出到控制台。

3. dmesg -n 命令

dmesg(display message)是用户空间读取内核 ring buffer 的工具。其 -n 选项可以在运行时动态修改上述 console_loglevel 的值,从而控制哪些级别的内核消息会输出到控制台,而无需重启系统或修改配置文件。

3.1 语法

bash 复制代码
sudo dmesg -n <level>

设置控制台日志级别阈值。只有严格小于 level 的消息才会打印到控制台。

3.2 常用场景

bash 复制代码
# 只显示 KERN_EMERG (级别0) 的消息,几乎屏蔽所有输出
sudo dmesg -n 1

# 显示 ERR 及以上 (级别 0-2)
sudo dmesg -n 3

# 显示 WARNING 及以上 (级别 0-3)
sudo dmesg -n 4

# 显示所有消息(包括 DEBUG)
sudo dmesg -n 8

# 恢复系统默认(通常是 4 或 7)
sudo dmesg -n 7

3.3 等效方法

bash 复制代码
# 方法1: 通过 /proc 接口
echo 1 | sudo tee /proc/sys/kernel/printk

# 方法2: 通过 sysctl
sudo sysctl -w kernel.printk="1 4 1 7"

4. 实际应用场景

场景1:内核死循环打印

当内核驱动(如 amdgpu)因 bug 持续打印日志刷屏时:

bash 复制代码
# 1. 立即止住控制台输出
sudo dmesg -n 1

# 2. 将日志重定向到文件分析
sudo dmesg > /tmp/kern_dump.log

# 3. 尝试恢复驱动状态
sudo modprobe -r amdgpu && sudo modprobe amdgpu

# 4. 如果模块卸载失败
sudo fuser -k /dev/kfd
sudo fuser -k /dev/dri/*
sudo modprobe -r amdgpu

# 5. 恢复日志级别
sudo dmesg -n 7

场景2:调试时开启详细日志

bash 复制代码
# 开启 amdgpu 驱动动态调试
echo 'module amdgpu +p' | sudo tee /sys/kernel/debug/dynamic_debug/control

# 同时确保控制台能看到 DEBUG 消息
sudo dmesg -n 8

场景3:只关注错误信息

bash 复制代码
# 只看 ERR 及以上级别
sudo dmesg -n 4

# 过滤 dmesg 中的错误
dmesg --level=err,crit,alert,emerg

dmesg 其他常用选项

bash 复制代码
# 持续监听内核消息(类似 tail -f)
sudo dmesg -w

# 带时间戳显示
dmesg -T

# 带级别标记显示
dmesg --decode

# 清空 ring buffer
sudo dmesg -C

# 只显示特定级别
dmesg --level=err
dmesg --level=warn,err

# 显示最后 N 行
dmesg | tail -50

5. Ring Buffer 机制与溢出处理

5.1 Ring Buffer 基本概念

内核日志存储在一个固定大小的环形缓冲区(ring buffer)中。当 buffer 满时,内核自动覆盖最旧的消息(FIFO 循环写入),这是设计行为,不需要手动干预。

5.2 查看 Ring Buffer 大小

bash 复制代码
# 查看当前 ring buffer 大小
dmesg | grep "log_buf_len"

# 或通过 sysfs(如果可用)
cat /sys/kernel/debug/tracing/buffer_size_kb

5.3 增大 Ring Buffer(防止日志丢失)

bash 复制代码
# 启动时通过 grub 参数设置(永久生效)
# 编辑 /etc/default/grub,在 GRUB_CMDLINE_LINUX 中添加:
log_buf_len=4M

# 然后更新 grub
sudo update-grub

5.4 日志持久化方案

Ring buffer 中的日志重启后丢失,且可能被覆盖。以下方法可持久化保存:

bash 复制代码
# 方法1: 通过 journald 持久化(systemd 系统)
# 确认配置: /etc/systemd/journald.conf
#   Storage=persistent
# 查看历史内核日志(包括之前的启动)
journalctl -k
journalctl -k -b -1    # 上一次启动的内核日志

# 方法2: 持续写入文件(实时捕获,不受 ring buffer 覆盖影响)
sudo cat /dev/kmsg > /tmp/kmsg_full.log &

# 方法3: 使用 dmesg -w 持续监听并保存
sudo dmesg -wT > /tmp/kern_continuous.log &

5.5 dmesg/dev/kmsg 的区别

dmesg /dev/kmsg
数据来源 ring buffer 快照 实时流式读取
读取方式 一次性输出当前所有内容 持续读取新消息
旧消息 包含 ring buffer 中所有现存消息 只读取打开后的新消息
典型用法 dmesg > log.txt cat /dev/kmsg > log.txt &

5.6 关键结论

  • Ring buffer 溢出是正常行为,不需要也无法阻止
  • 关键调试日志应通过 /dev/kmsgdmesg -w 实时持久化捕获
  • 不要依赖 ring buffer 保留完整历史,大量日志输出(如死循环打印)会快速覆盖有用信息

6. 注意事项

  1. dmesg -n 只影响控制台输出,不影响内核 ring buffer 中的日志记录

  2. 消息始终可通过 dmesg 命令或 /dev/kmsg 读取

  3. systemd 系统中,journalctl -k 也可查看内核日志

  4. 重启后日志级别恢复默认,如需持久化可修改 /etc/sysctl.conf

    复制代码
    kernel.printk = 4 4 1 7
  5. Ring buffer 满时自动覆盖旧消息,调试时应提前用 /dev/kmsgdmesg -w 持久化捕获

相关推荐
A小辣椒13 小时前
TShark:Wireshark CLI 功能
linux
A小辣椒17 小时前
TShark:基础知识
linux
AlfredZhao19 小时前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao1 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush42 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5202 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩2 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言