printk机制基础

1 printk 是什么

内核中没有 printf,用户空间使用的 printf 是由 C lib 库提供的,只存在于用户空间。那内核空间是怎么使用的呢?

这条链路是这样的:

最后通过内核把内容写到终端或者日志里面。

内核运行在内核态,直接驱动硬件,所以需要提供打印的服务。

1
printk("Hello, kernel!\n");

2 printk 日志等级

Linux 内核的日志,并不是所有信息都一样重要。有些信息必须第一时间看到,有些信息只在开发调试的时候才有用。

比如当内核崩溃的时候,必须立即看到;Debug 开发调试,这种信息平时并不重要,调试完就可以不管。

如果所有信息都一视同仁,紧急的崩溃信息很可能被大量调试信息淹没。所以内核设计了**日志等级(log level)**机制,用等级来区分每条消息的重要程度。

前一章我们讲了,内核等级大致分为如下:

1
2
3
4
5
6
7
8
#define KERN_EMERG   "<0>"
#define KERN_ALERT "<1>"
#define KERN_CRIT "<2>"
#define KERN_ERR "<3>"
#define KERN_WARNING "<4>"
#define KERN_NOTICE "<5>"
#define KERN_INFO "<6>"
#define KERN_DEBUG "<7>"

数字越小,优先级越高。

平时写驱动,很少直接写 printk + 宏,而是用内核封装好的函数:

例如,驱动初始化失败:

常用的封装函数有:

1
2
3
4
5
6
7
8
pr_emerg(...)   // 0
pr_alert(...) // 1
pr_crit(...) // 2
pr_err(...) // 3
pr_warn(...) // 4
pr_notice(...) // 5
pr_info(...) // 6
pr_debug(...) // 7

3 printk 调用流程

现在进入核心,整个 printk 调用流程如下:

我们逐个来看,先看 printk,这是入口,其定义如下:

1
2
3
4
5
6
#else
static inline int printk(const char *fmt, ...)
{
return 0;
}
#endif

它就是个壳,本身不干什么,就是接受可变参数,然后把参数传下去。

接着是 vprintk 函数,主要负责解析参数并进行格式化。

继续向下就是 vprintk_emit(),先调用 vprintk_store 把日志写入 ring buffer,再决定是否显示到控制台。

最后就是环形缓冲区。

4 总结

printk 收参数、格式化、写 ring buffer、必要时打印到控制台,这就是它的全部工作。


printk机制基础
https://cj0510.github.io/2026/08/09/Linux Kernel/Logging subsystem/printk机制基础/
作者
CJ1018
发布于
2026年8月9日
许可协议
CC BY-NC-SA 4.0