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
它们的含义分别是:
- 当前控制台日志级别
- 默认日志级别
- 最低控制台日志级别
- 引导时的默认日志级别
你可以修改日志级别,比如设置默认日志级别为 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
),数值越大严重性越低。
总结
#ifndef
保护防止头文件重复包含。KERN_SOH
(\001
) 作为日志级别标志,方便printk()
解析日志级别。KERN_EMERG
、KERN_ALERT
等定义了不同日志级别,以"\001N"
形式标识,其中N
是级别数值。printk(KERN_ERR "Some error occurred\n");
实际会传递"\0013Some error occurred\n"
供内核日志系统处理。