| 级别 | 名称 | 值 | 说明 |
|---|---|---|---|
| 0 | KERN_EMERG | 0 | 系统崩溃,系统不可用 |
| 1 | KERN_ALERT | 1 | 需要立即处理的紧急情况 |
| 2 | KERN_CRIT | 2 | 临界条件,严重硬件/软件故障 |
| 3 | KERN_ERR | 3 | 错误条件,驱动报告错误 |
| 4 | KERN_WARNING | 4 | 警告条件,可能出现问题 |
| 5 | KERN_NOTICE | 5 | 正常但重要的信息 |
| 6 | KERN_INFO | 6 | 信息级别,普通提示 |
| 7 | KERN_DEBUG | 7 | 调试级别,详细调试信息 |
驱动开发中的使用示例
#include <linux/kernel.h>
static int __init hello_init(void)
{
printk(KERN_EMERG "Fatal: system is down!\n"); // 0级 - 极度紧急
printk(KERN_ALERT "Alert: hardware failure!\n"); // 1级 - 紧急
printk(KERN_CRIT "Critical: CPU overheating!\n");// 2级 - 严重
printk(KERN_ERR "Error: failed to read register\n"); // 3级 - 错误
printk(KERN_WARNING "Warning: buffer near full\n"); // 4级 - 警告
printk(KERN_NOTICE "Notice: config updated\n"); // 5级 - 注意
printk(KERN_INFO "Info: driver initialized\n"); // 6级 - 信息
printk(KERN_DEBUG "Debug: function x() called\n"); // 7级 - 调试
return 0;
}
简写形式(常用)
实际开发中通常省略前缀数字,使用预定义常量:
printk(KERN_EMERG "System崩溃!\n");
printk(KERN_ALERT "硬件故障!\n");
printk(KERN_CRIT "严重错误!\n");
printk(KERN_ERR "驱动错误: 寄存器读取失败\n");
printk(KERN_WARNING "警告: 缓冲区接近满\n");
printk(KERN_NOTICE "通知: 配置已更新\n");
printk(KERN_INFO "信息: 驱动加载成功\n");
printk(KERN_DEBUG "调试: 入口函数被调用\n");
日志级别的控制机制
1. 当前日志级别
系统有一个当前日志级别,只有级别 >= 当前级别的消息才会输出到控制台。
# 查看当前日志级别
cat /proc/sys/kernel/printk
# 输出示例:6 4 1 7
# | | | └── 最低级别(引导时的级别)
# | | └────── 默认级别
# | └────────── 控制台日志级别
# └────────────── 当前日志级别
2. 级别阈值的作用
假设当前日志级别为 6:
| 消息级别 | 是否输出到控制台 | 原因 |
|---|---|---|
| 0 (EMERG) | 是 | 0 < 6 |
| 1 (ALERT) | 是 | 1 < 6 |
| 2 (CRIT) | 是 | 2 < 6 |
| 3 (ERR) | 是 | 3 < 6 |
| 4 (WARNING) | 是 | 4 < 6 |
| 5 (NOTICE) | 是 | 5 < 6 |
| 6 (INFO) | 是 | 6 = 6 |
| 7 (DEBUG) | 否 | 7 > 6 |
级别数字越小越紧急,越优先显示。
3. 临时调整日志级别
# 临时设为 8(显示所有消息)
echo 8 > /proc/sys/kernel/printk
# 设为 3(只显示 ERR 及以上级别)
echo 3 > /proc/sys/kernel/printk
# 恢复默认值 4
echo 4 > /proc/sys/kernel/printk
4. 永久修改日志级别
# 在 /etc/sysctl.conf 中添加
vi /etc/sysctl.conf
# 写入以下内容(内核参数格式)
kernel.printk = 8 4 1 7
# 使其生效
sysctl -p
驱动开发中的最佳实践
1. 使用条件编译控制调试信息
#ifdef DEBUG
#define DPRINTK(fmt, args...) printk(KERN_DEBUG "[DRIVER] " fmt, ##args)
#else
#define DPRINTK(fmt, args...) do {} while(0)
#endif
static int __init hello_init(void)
{
printk(KERN_INFO "Hello driver loaded\n");
DPRINTK("Debug: init called\n"); // DEBUG 关闭时不输出
return 0;
}
2. 统一的日志宏
#define DRV_INFO(fmt, args...) printk(KERN_INFO "[MYDRV] " fmt, ##args)
#define DRV_ERR(fmt, args...) printk(KERN_ERR "[MYDRV ERROR] " fmt, ##args)
#define DRV_WARN(fmt, args...) printk(KERN_WARNING "[MYDRV WARN] " fmt, ##args)
#define DRV_DEBUG(fmt, args...) printk(KERN_DEBUG "[MYDRV DBG] " fmt, ##args)
static int __init hello_init(void)
{
DRV_INFO("Driver initialized\n");
DRV_ERR("Failed to register device\n");
DRV_WARN("Using legacy mode\n");
DRV_DEBUG("GPIO pin %d configured\n", pin);
}
3. 根据错误级别决定返回值
static int __init hello_init(void)
{
int ret;
ret = register_chrdev(0, "hello", &hello_fops);
if (ret < 0) {
printk(KERN_ERR "hello: chrdev registration failed\n");
return ret;
}
printk(KERN_INFO "hello: driver registered, major=%d\n", ret);
return 0;
}
在 menuconfig 中配置 DEBUG
内核提供了通用的 DEBUG 宏选项:
Kernel hacking --->
[*] Kernel debugging
(8) Default console log level (0-7)
驱动中可以这样使用:
#include <linux/device.h>
#define DEBUG
#ifdef DEBUG
#define DBG(fmt, args...) printk(KERN_DEBUG fmt, ##args)
#else
#define DBG(fmt, args...) do {} while(0)
#endif
常用场景对照表
| 场景 | 推荐级别 | 示例 |
|---|---|---|
| 模块加载成功 | KERN_INFO | printk(KERN_INFO "driver loaded\n"); |
| 模块卸载 | KERN_INFO | printk(KERN_INFO "driver removed\n"); |
| 打开/关闭设备 | KERN_INFO | printk(KERN_INFO "device opened\n"); |
| 读取数据成功 | KERN_DEBUG | printk(KERN_DEBUG "read %d bytes\n", count); |
| 寄存器读取失败 | KERN_ERR | printk(KERN_ERR "failed to read reg\n"); |
| 内存分配失败 | KERN_ERR | printk(KERN_ERR "kmalloc failed\n"); |
| 缓冲区溢出警告 | KERN_WARNING | printk(KERN_WARNING "buffer overflow\n"); |
| 硬件检测到异常 | KERN_CRIT | printk(KERN_CRIT "HW fault detected\n"); |