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