引言
首先,要知道,内核空间是没有printf
函数的,printf
函数是是用户空间的标准 I/O 函数,而不是内核空间中的。
所以在运行于内核空间的程序中(比如驱动程序),是不能使用printf
函数的,但有时候我们又需要打印输出一些信息,怎么办呢?
此时通常大家会选用内核中的printk
函数,其详细介绍下:
printk
是 Linux 内核中用于打印日志信息的核心函数。它是内核日志系统的基础,允许开发者将信息写入内核环形缓冲区,供调试或系统管理使用。
printk
函数的详细介绍
printk
函数的原型
int printk(const char *fmt, ...);
-
返回值
返回实际写入缓冲区的字符数。 -
参数
fmt
一个格式化字符串,类似于printf
的格式说明符。它支持大多数标准 C 格式化选项(如%d
、%s
、%x
等),也支持一些特定于内核的格式扩展。...
可变参数,提供给格式化字符串使用的具体数据。
printk
函数的日志级别与日志级别前缀
printk
的格式字符串可以包含一个 日志级别前缀,用于指定消息的重要性。这些日志级别以宏的形式定义:
宏名 | 描述 | 数值 | 示例 |
---|---|---|---|
KERN_EMERG | 紧急:系统不可用 | 0 | "<0>" |
KERN_ALERT | 警报:需要立即采取行动 | 1 | "<1>" |
KERN_CRIT | 严重情况 | 2 | "<2>" |
KERN_ERR | 错误 | 3 | "<3>" |
KERN_WARNING | 警告 | 4 | "<4>" |
KERN_NOTICE | 通知:重要但非错误 | 5 | "<5>" |
KERN_INFO | 信息 | 6 | "<6>" |
KERN_DEBUG | 调试信息,仅用于开发 | 7 | "<7>" |
示例:
printk(KERN_ERR "Error occurred: %d\n", error_code);
上述代码将打印一条错误信息到内核日志中,并标记为错误级别。
printk
函数的几个使用示例
- 简单打印
不带日志级别的调用将使用默认日志级别(default_message_loglevel
)。
printk("Hello, kernel world!\n");
-
带日志级别
明确指定日志的重要性。printk(KERN_INFO "System initialized successfully\n");
-
格式化字符串
支持标准格式说明符。int value = 42; printk(KERN_DEBUG "Debug value: %d\n", value);
printk
函数的日志输出到哪里?
-
内核环形缓冲区
printk
的信息会被写入内核的环形缓冲区,可以通过dmesg
或/var/log/messages
查看。 -
控制台输出
日志输出到控制台的行为由/proc/sys/kernel/printk
的console_loglevel
控制。只有日志级别小于等于console_loglevel
的消息会被打印到控制台。
printk
函数适应内核的格式控制扩展
内核的 printk
支持一些标准 printf
中没有的扩展:
-
指针打印
%p
: 打印指针地址%pK
: 打印指针地址,受安全保护%px
: 指针以十六进制打印
-
内核特定对象
%pf
: 打印函数名称%ps
: 打印内核符号
-
定制输出
%pb
: 位掩码%ph
: 打印十六进制数组
示例:
void *ptr = &some_variable;
printk("Pointer address: %p\n", ptr);
使用printk
函数的注意事项
-
性能开销
过多的printk
调用会增加内核日志的处理负担,影响性能。特别是在高频路径中应避免大量日志输出。 -
同步与中断上下文
printk
是线程安全的,但在中断上下文中调用可能引发延迟问题。- 从 Linux 3.x 起,
printk
的行为被优化以减少中断阻塞,但在复杂场景下仍需注意。
-
调试日志
调试信息通常使用KERN_DEBUG
级别,并配合动态调试机制启用或禁用:echo 'module driver_name +p' > /sys/kernel/debug/dynamic_debug/control
-
控制好控制台的日志级别
设置过高(如 7)会导致大量调试信息输出到终端,可能影响可读性。
一般情况下,推荐使用较低级别(如 4 或 3)以减少干扰。
如果设置printk
函数的默认日志级别和是否输出到终端控制台
不管怎么样,printk
函数的日志都会写到内核的环形缓冲区,可以通过命令 dmesg
或 /var/log/messages
查看。
比如下面这个:
dmesg
但不一定能显示到我们的终端控制台中,如果没有输出到终端中,而又想输出其信息到终端中,请看下面的内容中。
在Linux系统中,/proc/sys/kernel/printk
文件中记录了printk的日志级别的相关配置信息,主要就与终端控制台有与。
这个文件包含四个数值,依次表示:
- console_loglevel 当前终端控制台打印的日志级别。日志级别小于或等于该值的消息会被打印到控制台。
- default_message_loglevel 默认的日志级别。如果 printk 函数被调用时没有指定日志级别前缀,将使用该值作为printk 函数输出的日志信息的日志级别。
- minimum_console_loglevel 终端控制台的最低日志级别阈值。如果设置的 console_loglevel 小于该值,那么console_loglevel的值将被提升到此值。
- default_console_loglevel 系统启动时的默认终端控制台日志级别。
我们可以修改这几个值实现将一些没有打印显示到终端控制台上的信息显示出来。
我们可以用下面这条命令查看文件/proc/sys/kernel/printk
的当前值:
cat /proc/sys/kernel/printk
运行结果如下:
这是默认值,可见默认情况下终端上只显示级别为警告以下的日志信息:
如果我们要显示所有的日志信息,显然只需要修改值为7 4 1 7
就能打印所有的日志信息到终端上了。
以下是修改方法:
1 临时修改
echo "7 4 1 7" > /proc/sys/kernel/printk
运行上面的 echo
命令后,设置会立即生效,但重启后会恢复默认值,如果想恢复默认值,可值行下面的命令:
echo "4 4 1 4" > /proc/sys/kernel/printk
2 永久修改
如果希望永久修改,可以将设置写入 /etc/sysctl.conf
或创建启动脚本。
方法 1:编辑 sysctl.conf
在文件 /etc/sysctl.conf
中添加:
kernel.printk = 7 4 1 7
然后运行:
sysctl -p
方法 2:添加启动脚本
创建或编辑脚本 /etc/rc.local
或其他系统初始化脚本,添加:
echo "7 4 1 7" > /proc/sys/kernel/printk