
一、printk 说明
在 Linux 内核驱动开发或调试时,我们常通过日志 API 跟踪程序流程。printk() 是内核日志的核心函数,几乎所有日志功能都基于它实现。以下是它的运作方式及注意事项
1、日志输出主要分两条路径

串口控制台(右侧路径)
自动显示在前台:系统运行时,日志会直接打印到当前终端屏幕(如开发板串口输出)。
支持
**dmesg**命令查看:可以通过终端命令dmesg随时提取内核日志。日志级别控制:系统会根据日志等级(如
(KERN_INFO)或(KERN_DEBUG))过滤日志,但你的自定义日志可能不受限制(需注意配置)。
ADB/USB 登录(左侧路径)
通过
**dmesg**查看:连接设备后,用dmesg命令获取日志。两个主要问题:
日志缓冲区大小有限:内核日志存储在一个固定大小的内存区域(
log_buf),存满后会覆盖旧数据。环形缓冲区机制:新日志会覆盖最早的数据,导致历史记录丢失(比如长时间运行后,早期日志会被新日志替换)。
2、**printk**的流程大致可以分为两步:
我们打印的日志最终会去两个地方:
日志缓冲区(Log Buffer) 所有日志都会先存到内核里的一个循环存储区(地址叫
log_buf),这个区域像一个循环的存储带,存满后会从头开始覆盖。用户可以通过/proc/kmsg文件查看这里的日志内容。控制台(Console) 如果日志的优先级符合要求,它还会被输出到控制台。目前我们用的控制台有两种:
串口控制台(UART Console):日志会从对应的物理串口输出,比如调试时常用的串口打印。
内存控制台(RAM Console):日志会保存在内存里的某个区域。
二、printk 日志等级设置
Linux 内核为 printk 设置了 8 个日志等级,最高级是 KERN_EMERG,最低级是 KERN_DEBUG。在配置内核时,有一个选项(CONFIG_MESSAGE_LOGLEVEL_DEFAULT)用于设定默认日志等级,通常设为 4。这样,只有日志等级比 4 高的信息才会在终端或串口显示。
#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 用于调试信息 */2
3
4
5
6
7
8
内核日志的打印级别共有 8 个等级,编号从 0 到 7,数字越大优先级越低。通常可以通过修改 /proc/sys/kernel/printk 文件来调整日志的输出设置。
cat /proc/sys/kernel/printk
7 4 1 7
//关闭所有的内核打印
echo 1 > /proc/sys/kernel/printk
cat /proc/sys/kernel/printk
1 4 1 72
3
4
5
6
7
8

四个日志级别参数的含义:
console_loglevel(控制台过滤级别) 控制台显示日志的最低门槛。只有比这个级别"更紧急"的日志(比如"错误"比"提示"更紧急)才会显示在终端上。
default_message_loglevel(默认打印等级) 当程序员调用
printk函数时,如果没有特别指定日志等级,就默认使用这个等级输出信息。minimum_console_loglevel(控制台最大允许等级) 用户通过命令行设置控制台日志级别时,不能超过这个等级的上限。例如如果此值为 3,用户就不能把控制台等级设为 4 或更高。
default_console_loglevel(控制台默认等级) 系统启动时,控制台默认使用的日志等级。开机后如果没手动调整,就按这个等级显示日志。
简单总结: 这四个参数控制日志的"显示门槛"和"默认行为",而 dmesg 是查看历史日志的万能工具。
三、屏蔽等级日志控制机制
屏蔽如下代码片段: 文件目录:kernel\kernel\printk\printk.c
不建议这么改。
/*
* Call the console drivers, asking them to write out
* log_buf[start] to log_buf[end - 1].
* The console_lock must be held.
*/
static void call_console_drivers(int level, const char *text, size_t len)
{
struct console *con;
trace_console(text, len);
/*
if (level >= console_loglevel && !ignore_loglevel)
return;
if (!console_drivers)
return;
#ifndef CONFIG_DYNAMIC_DEBUG
if (!perf_mode_console)
return;
#endif
*/
for_each_console(con) {
if (exclusive_console && con != exclusive_console)
continue;
if (!(con->flags & CON_ENABLED))
continue;
if (!con->write)
continue;
if (!cpu_online(smp_processor_id()) &&
!(con->flags & CON_ANYTIME))
continue;
con->write(con, text, len);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
四、printk 打印常用方式
- 打印对应函数名(
func)及对应行数(LINE) (快速定位日志位置)
printk("[me]%s[%d].\n",__func__,__LINE__);
printk 的打印指定
printk("This is an example\n"); ------ KERN_WARNING------- (不建议,对自己日志含义明白)
printk(KERN_ERR"This is an example\n"); == pr_err(为了简洁 pr_err)
printk("\0014This is an example\n"); ----- 不建议(对其他人不友善)printk("\0014""This is an example\n");----- 不建议(对其他人不友善)
printk 函数与用户空间的 printf 函数格式完全相同,它所打印的字符串头部可以加入“\001n”样式的字符。
其中 n 为 0~7,表示这条信息的记录级别,n 数值越小级别越高。
五、printk 打印格式
六、printk 执行过程
printk 不能随便用,因为它会拖慢系统性能。所以需要动态控制打印(比如通过开关决定是否输出)。
printk
// linux 4.9: kernel/printk/internal.h
// linux 5.4: kernel/printk/printk_safe.c
vprintk_func
vprintk_default(fmt, args);
vprintk_emit
vprintk_store // 把要打印的信息保存在log_buf中
log_output
preempt_disable();
if (console_trylock_spinning())
console_unlock();
preempt_enable();
console_unlock
for (;;) {
msg = log_from_idx(console_idx);
if (suppress_message_printing(msg->level)) {
/* 如果消息的级别数值大于console_loglevel, 则不打印此信息 */
}
printk_safe_enter_irqsave(flags);
call_console_drivers(ext_text, ext_len, text, len);
printk_safe_exit_irqrestore(flags);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
其中:printk_safe_enter_irqsave --> local_irq_save local_irq_save 的调用把当前的中断状态(开或关)保存到 flags 中,然后禁用当前处理器上的中断。