1. 内核日志基础:printk 函数原型、日志级别与控制台配置

内核日志是网络协议栈调试的“第一现场”。无论是链路层丢包、TCP 重传异常,还是路由查找失败,printk 都是最直接、最底层的观测手段。本节从函数原型、日志级别到控制台配置,建立内核日志的完整认知框架。

1.1 printk 函数原型与调用约定

printk 是内核态下的格式化输出函数,其原型定义在 <linux/printk.h> 中:

// 核心原型(简化)
int printk(const char *fmt, ...);

// 带日志级别的标准调用格式
printk(KERN_INFO "eth0: link up, speed %d Mbps\n", speed);

// 不带日志级别的调用(默认使用 KERN_WARNING)
printk("memory allocation failed\n");
特性 说明
调用上下文 进程上下文、中断上下文、软中断、NMI 均可调用
输出目标 内核环形缓冲区(log_buf) + 控制台(console)
格式化支持 与 printf 兼容,额外支持 %p(指针)、%pI4(IPv4)、%pI6(IPv6)
原子安全 自旋锁持有期间可安全调用,不会导致死锁
性能开销 高——每条消息涉及格式化、锁竞争、控制台输出,生产环境需节制
调试技巧:网络协议栈中常用 printk(KERN_DEBUG "%s:%d: skb len=%u\n", __func__, __LINE__, skb->len); 快速定位函数入口和关键数据。

1.2 日志级别:从 KERN_EMERG 到 KERN_DEBUG

内核定义了 8 个日志级别宏,每个宏对应一个数字优先级(0~7)。数字越小,优先级越高,消息越紧急。

宏名称 数字值 含义 典型场景
KERN_EMERG 0 紧急事件,系统不可用 内核崩溃、致命硬件错误
KERN_ALERT 1 必须立即处理 严重内存损坏、文件系统损坏
KERN_CRIT 2 临界条件 网卡 DMA 失败、协议栈断言
KERN_ERR 3 错误条件 skb 分配失败、路由查找异常
KERN_WARNING 4 警告,可能存在问题 校验和错误、重传超时
KERN_NOTICE 5 正常但重要的事件 网络接口 up/down、邻居表变化
KERN_INFO 6 信息性消息 驱动加载、协议注册、统计信息
KERN_DEBUG 7 调试信息 函数入口/出口、变量值、流程分支
常见误区:不要在生产内核中大量使用 KERN_DEBUG 级别的 printk。即使控制台不显示,消息仍会写入环形缓冲区,导致 log_buf 快速填满,覆盖早期关键日志。
printk 日志级别优先级与控制台过滤 0 EMERG 1 ALERT 2 CRIT 3 ERR 4 WARNING 5 NOTICE 6 INFO 7 DEBUG 优先级递减(数字越大,优先级越低) console_loglevel 过滤线(默认 7 = 显示所有) 示例:console_loglevel = 4 只显示 0~4(EMERG ~ WARNING),5~7(NOTICE/INFO/DEBUG)被过滤,不输出到控制台 环形缓冲区(log_buf)始终记录所有级别的消息,不受 console_loglevel 影响 通过 dmesg 命令可查看完整日志,不受控制台过滤限制

1.3 控制台日志级别配置:/proc/sys/kernel/printk

内核通过 /proc/sys/kernel/printk 文件暴露 4 个整数值,控制日志的过滤与输出行为。这是调试网络协议栈时必须掌握的核心配置。

# 查看当前配置
$ cat /proc/sys/kernel/printk
7       4       1       7

# 各字段含义(从左到右):
# [0] console_loglevel  = 7  — 控制台日志级别,优先级高于此值的消息才输出
# [1] default_loglevel  = 4  — 未指定级别的 printk 默认使用 KERN_WARNING
# [2] minimum_loglevel  = 1  — 控制台可设置的最低级别(不可低于此值)
# [3] default_console_loglevel = 7 — 系统启动时的默认控制台级别
字段 索引 含义 网络调试建议值
console_loglevel 0 控制台输出阈值,只有优先级 ≤ 此值的消息才会打印到控制台 调试时设为 8(显示所有),生产环境设为 4
default_loglevel 1 未指定日志级别的 printk 使用的默认级别 保持 4(KERN_WARNING),避免遗漏重要警告
minimum_loglevel 2 console_loglevel 可设置的最小值(安全保护) 通常为 1,不建议修改
default_console_loglevel 3 系统启动时的默认 console_loglevel 保持 7,启动阶段可见所有消息

1.4 动态调整控制台日志级别

在调试网络协议栈时,经常需要临时提升日志可见性,而不重新编译内核或重启系统。以下方法可动态调整:

  • 通过 /proc 文件系统:
    # 显示所有日志(包括 KERN_DEBUG)
    echo 8 > /proc/sys/kernel/printk
    
    # 只显示 KERN_ERR 及以上(0~3)
    echo 3 > /proc/sys/kernel/printk
  • 通过 sysctl 命令:
    # 查看当前值
    sysctl kernel.printk
    
    # 设置 console_loglevel = 8
    sysctl -w kernel.printk="8 4 1 7"
  • 通过 dmesg 控制:
    # 设置控制台级别为 8(显示所有)
    dmesg -n 8
    
    # 设置控制台级别为 3(只显示错误)
    dmesg -n 3
网络调试实战:当怀疑网卡驱动或协议栈在特定路径下打印了 KERN_DEBUG 消息时,执行 echo 8 > /proc/sys/kernel/printk 即可在串口或 SSH 控制台上实时看到这些调试输出。调试结束后务必恢复为 echo 4 > /proc/sys/kernel/printk,避免生产环境日志洪流。
printk 消息流:从调用到控制台输出 printk(fmt, ...) 提取日志级别(0~7) 级别 ≤ console_loglevel ? (数字越小优先级越高) 是 否 控制台输出 串口/显示器 仅环形缓冲区 dmesg 可查看 关键结论 环形缓冲区记录所有消息;控制台只输出优先级 ≤ console_loglevel 的消息

1.5 网络协议栈调试中的典型配置场景

场景 console_loglevel 说明
生产环境 3 或 4 只输出 ERR 和 WARNING,避免日志淹没正常业务
驱动开发调试 8 显示所有 DEBUG 消息,跟踪网卡收发包路径
协议栈异常排查 6 显示 INFO 及以上,观察连接状态变化、路由事件
内核崩溃分析 2 只保留 CRIT 和 EMERG,减少干扰,聚焦致命错误
效率提示:在调试网络协议栈时,建议在 /etc/sysctl.conf 中添加 kernel.printk = 8 4 1 7,并在调试完成后注释掉该行。这样重启后自动进入调试模式,避免每次手动设置。