内核编程七:Linux 内核日志的级别

Linux 内核日志(Kernel Log)有 8 个不同的级别(Severity Levels),用于表示消息的严重性。它们的定义在 include/linux/kern_levels.h 头文件中,并且可以用于 printk() 进行日志打印。

内核日志级别

级别数值 级别名称 说明
0 KERN_EMERG 紧急(Emergency),系统不可用,如内核崩溃
1 KERN_ALERT 警报(Alert),需要立即处理,如磁盘故障
2 KERN_CRIT 严重(Critical),严重错误,如硬件故障
3 KERN_ERR 错误(Error),常规错误,如驱动问题
4 KERN_WARNING 警告(Warning),可能影响系统运行但未导致错误
5 KERN_NOTICE 注意(Notice),需要关注但不是错误
6 KERN_INFO 信息(Informational),普通运行信息
7 KERN_DEBUG 调试(Debug),用于开发和调试的信息

日志级别在 printk() 中的使用

复制代码
printk(KERN_INFO "This is an informational message.\n"); 
printk(KERN_ERR "This is an error message.\n"); 

如果 printk() 没有指定级别,默认使用 DEFAULT_MESSAGE_LOGLEVEL,一般为 KERN_WARNING

如何查看当前日志

可以使用 dmesg 命令查看内核日志:

复制代码
dmesg 

或者按级别筛选:

复制代码
dmesg --level=err,warn 

level可以跟以下参数

emerg、alert 、 crit 、 err 、warn、notice、info、debug

/proc/sys/kernel/printk 文件中可以查看和修改 printk() 输出的默认级别:

复制代码
cat /proc/sys/kernel/printk 

通常返回四个数字,如:

复制代码
4 4 1 7 

它们的含义分别是:

  1. 当前控制台日志级别
  2. 默认日志级别
  3. 最低控制台日志级别
  4. 引导时的默认日志级别

你可以修改日志级别,比如设置默认日志级别为 KERN_INFO

复制代码
echo 6 > /proc/sys/kernel/printk 

include/linux/kern_levels.h文件

源码

以下代码是 Linux 内核头文件 kern_levels.h 中的所有内容,它用于定义内核日志的级别,并且与 printk() 日志系统相关。

复制代码
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __KERN_LEVELS_H__
#define __KERN_LEVELS_H__

#define KERN_SOH	"\001"		/* ASCII Start Of Header */
#define KERN_SOH_ASCII	'\001'

#define KERN_EMERG	KERN_SOH "0"	/* system is unusable */
#define KERN_ALERT	KERN_SOH "1"	/* action must be taken immediately */
#define KERN_CRIT	KERN_SOH "2"	/* critical conditions */
#define KERN_ERR	KERN_SOH "3"	/* error conditions */
#define KERN_WARNING	KERN_SOH "4"	/* warning conditions */
#define KERN_NOTICE	KERN_SOH "5"	/* normal but significant condition */
#define KERN_INFO	KERN_SOH "6"	/* informational */
#define KERN_DEBUG	KERN_SOH "7"	/* debug-level messages */

#define KERN_DEFAULT	""		/* the default kernel loglevel */

/*
 * Annotation for a "continued" line of log printout (only done after a
 * line that had no enclosing \n). Only to be used by core/arch code
 * during early bootup (a continued line is not SMP-safe otherwise).
 */
#define KERN_CONT	KERN_SOH "c"

/* integer equivalents of KERN_<LEVEL> */
#define LOGLEVEL_SCHED		-2	/* Deferred messages from sched code
					 * are set to this special level */
#define LOGLEVEL_DEFAULT	-1	/* default (or last) loglevel */
#define LOGLEVEL_EMERG		0	/* system is unusable */
#define LOGLEVEL_ALERT		1	/* action must be taken immediately */
#define LOGLEVEL_CRIT		2	/* critical conditions */
#define LOGLEVEL_ERR		3	/* error conditions */
#define LOGLEVEL_WARNING	4	/* warning conditions */
#define LOGLEVEL_NOTICE		5	/* normal but significant condition */
#define LOGLEVEL_INFO		6	/* informational */
#define LOGLEVEL_DEBUG		7	/* debug-level messages */

#endif

解读

1. 头文件保护

复制代码
#ifndef __KERN_LEVELS_H__ 
#define __KERN_LEVELS_H__ 
  • 这是 头文件防护(Include Guard),防止头文件被多次包含导致重复定义错误。
  • #ifndef 检查 __KERN_LEVELS_H__ 是否未定义,如果未定义,则 #define 它,并继续解析文件内容。

2. 定义 KERN_SOH(Start Of Header)

复制代码
#define KERN_SOH "\001" /* ASCII Start Of Header */ 
#define KERN_SOH_ASCII '\001' 
  • \001 是 ASCII 码 SOH(Start of Header,开始头部) ,数值为 1
  • KERN_SOH 被定义为 "\001",即一个包含 ASCII SOH 字符的字符串。
  • KERN_SOH_ASCII 直接定义为字符 '\001',用于非字符串场景。

作用:
KERN_SOH 作为日志级别的前缀,便于 printk() 解析日志级别。

3. 定义日志级别

复制代码
#define KERN_EMERG KERN_SOH "0" /* system is unusable */ 
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */ 
#define KERN_CRIT KERN_SOH "2" /* critical conditions */ 
  • KERN_EMERG 定义为 "\0010",表示日志级别 0(紧急)。
  • KERN_ALERT 定义为 "\0011",表示日志级别 1(警报)。
  • KERN_CRIT 定义为 "\0012",表示日志级别 2(严重)。

自动连接:

在 C 语言中,字符串常量连接时会自动去掉空格,把它们合并成一个大的字符串。因此:

KERN_SOH "0" 会展开为 "\0010"。

其中 "\001" 是一个包含控制字符 SOH 的字符串,而 "0" 是包含字符 '0' 的字符串

所以,最终的结果并不是 "\001 0"(中间有空格),而是 "\0010",即 SOH 字符和字符 '0' 连接成一个字符串。

关键点:

空格 在字符串常量之间并不直接保留,而是被忽略。如果想得到"\001 0"应该写成KERN_SOH " 0",引号中的0前面有空格

为什么前缀 KERN_SOH

  • printk() 解析日志字符串时,会识别 \001 作为日志级别标志,后面的数字决定级别。

  • 例如:

    复制代码
    printk(KERN_ALERT "This is an alert message.\n"); 

    等价于:

    复制代码
    printk("\0011This is an alert message.\n"); 

    这样,printk() 解析到 \0011,就知道这是 警报(Alert)级别 的日志。

后续定义类似,从 紧急 (0) 到 调试 (7),数值越大严重性越低。


总结

  1. #ifndef 保护防止头文件重复包含。
  2. KERN_SOH (\001) 作为日志级别标志,方便 printk() 解析日志级别。
  3. KERN_EMERGKERN_ALERT 等定义了不同日志级别,以 "\001N" 形式标识,其中 N 是级别数值。
  4. printk(KERN_ERR "Some error occurred\n"); 实际会传递 "\0013Some error occurred\n" 供内核日志系统处理。
相关推荐
不知几秋2 小时前
数字取证-内存取证(volatility)
java·linux·前端
珊珊而川3 小时前
ChatPromptTemplate创建方式比较
服务器·langchain
欧先生^_^5 小时前
Linux内核可配置的参数
linux·服务器·数据库
若风的雨5 小时前
【deekseek】P2P通信路由过程
服务器·网络协议·p2p
海尔辛5 小时前
学习黑客5 分钟读懂Linux Permissions 101
linux·学习·安全
Python私教5 小时前
征服Rust:从零到独立开发的实战进阶
服务器·开发语言·rust
王RuaRua6 小时前
[数据结构]5. 栈-Stack
linux·数据结构·数据库·链表
曼岛_6 小时前
[架构之美]linux常见故障问题解决方案(十九)
linux·运维·架构
tan180°6 小时前
Linux进程信号处理(26)
linux·c++·vscode·后端·信号处理
大神的风范6 小时前
从0开始学linux韦东山教程第三章问题小结(4)
linux·服务器