Linux日志系统整体架构

1 为什么需要Kernel日志系统

为什么需要?因为用户空间都是需要通过系统调用(System Call)才能进入内核使用内核服务,内核也只能通过日志暴露问题,比如我们之前提到SPI,驱动probe失败时,用户只知道设备不能工作,不知懂哪里出了问题。

我们可以注入类似

1
dev_err(dev,"init failed\n");

在内核启动时将问题暴露出来。

所以Kernel日志系统是Linux内核调试、故障定位和运行状态观察的重要基础设施。

2 Linux日志体系整体架构

整个日志可以分为三层,其结构如下

  • 第一层是日志生产者,比如我们之前SPI调用的dev_info。这里用户程序主要会调用内核提供的接口函数
  • 第二层是printk日志核心,内核提供的printk 子系统,源码位于kernel/printk/,会把日志记录到环形缓冲区ring buffer
  • 第三层是日志消费者,写入内核的日志我们还需要读出来

3 一条Kernel日志生命周期

一条内核日志是怎么走到日志消费者阶段的?

比如我们调用

1
2
3
pr_info("device probe success\n");
#define pr_info(fmt, ...) \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)

pr_info()是一个宏,不是独立的底层日志系统,就是对printk的封装,其中KERN_INFO表示日志等级。

在printk子系统对日志进行格式化并添加等级、时间戳、序号等信息,然后存入ring buffer。

先对整个流程大致了解,接下来我们会对每一部分详细讲解。

4 printk是什么

用过C语言的都知道printf函数,而printk就是Kernel自己的printf。区别就是printk不会简单地写向 stdout,而是进入内核日志系统。

基本格式

1
printk(KERN_INFO "hello\n");

拆成两部分日志等级+正文。

日志等级主要有以下

1
2
3
4
5
6
7
8
9
10
11
#define KERN_SOH  "\001"		/* ASCII 头部起始标志 */
#define KERN_SOH_ASCII '\001'

#define KERN_EMERG KERN_SOH "0" /* 系统不可用,最高紧急级别 */
#define KERN_ALERT KERN_SOH "1" /* 必须立即采取行动 */
#define KERN_CRIT KERN_SOH "2" /* 严重情况(如硬件错误) */
#define KERN_ERR KERN_SOH "3" /* 错误条件(如驱动失败) */
#define KERN_WARNING KERN_SOH "4" /* 警告条件(非致命问题) */
#define KERN_NOTICE KERN_SOH "5" /* 正常但值得注意的事件 */
#define KERN_INFO KERN_SOH "6" /* 一般信息消息 */
#define KERN_DEBUG KERN_SOH "7" /* 调试级别消息,最低优先级 */

其具体含义如上,数字越小,优先级越高、问题越严重。

则上面的例子会被展开为

1
2
printk(KERN_INFO "hello\n");
printk("\001" "6" "hello\n");

pr_*()又是什么?就是用宏对printk的封装,省略了日志等级的参数:

1
2
3
4
5
6
7
8
pr_emerg(fmt, ...)    → printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
pr_alert(fmt, ...) → printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
pr_crit(fmt, ...) → printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
pr_err(fmt, ...) → printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
pr_warn(fmt, ...) → printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
pr_notice(fmt, ...) → printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
pr_info(fmt, ...) → printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
pr_debug(fmt, ...) → printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)

日志等级有什么用?主播目前的理解就是控制台筛选。

我们查看控制台等级

1
cat /proc/sys/kernel/printk

第一个数字就是控制台的等级,比整个等级小的会显示到控制台,一样的要看具体配置,比其高的不会显示。

5 ring buffer

ring buffer被叫做内核日志环形缓冲区,是为了专门保存printk() 日志记录的一块内存区域。

它的作用是解决日志产生速度和输出速度的差异。

我们看一他的工作原理

当空间不足时,为新日志腾出空间,最旧日志被覆盖。

6 总结

Linux Kernel日志系统采用”日志产生、日志存储、日志读取”三层架构。内核代码通过printk系列接口产生日志,日志首先被保存到Kernel Log Buffer中,用户空间通过dmesg、/dev/kmsg或者Android logcat读取这些信息。这种设计避免了日志输出阻塞内核执行,同时提供了强大的调试能力。

7 互动

  1. Linux为什么需要日志系统?
  2. printk打印出来的日志存在哪里?
  3. dmesg为什么能看到kernel日志?
  4. Android中的logcat和kernel日志有什么关系?
  5. 一条日志完整生命周期是什么?

Linux日志系统整体架构
https://cj0510.github.io/2026/08/09/Linux Kernel/Logging subsystem/Linux日志系统整体架构/
作者
CJ1018
发布于
2026年8月9日
许可协议
CC BY-NC-SA 4.0