printk 说明

在开发Linux device Driver或者跟踪调试内核行为的时候经常要通过Log APItrace整个过程,Kernel API printk()是整个Kernel Log机制的基础API,几乎所有的Log方式都是基于printk来实现的。
当然,利用printk还有一些需要注意的地方,在详细讲述之前先分析一下printk()实现,大致流程如下图所示:

enter image description here

从上图可以看出,printk的流程大致可以分为两步:

  • 将所有Log输出到内核的Log Buffer,该Log Buffer是一个循环缓冲区,其地址可以在内核中用log_buf变量访问

  • 根据设定的Log级别决定是否将Log输出到Console

基于以上内容,我们打印的Log最终会走向两个位置:

  • Log Buffer:该Buffer里面的内容可以存储在/proc/kmsg

  • ConsoleConsole的实现可以有很多,目前我们用到的有UART Console(串口)和RAM Console。通向UART ConsoleLog会在对应的UART端口打印出来。

对于Console Log,不可避免的对系统性能有影响。所以对于Console Log设置了两道关卡

  • 第一个是对Log级别进行过滤,只能输出高优先级的Log

  • 第二个是为UART设置单独的开关,在不必要的时候可以将其关闭以提高系统性能。这里提到了Log优先级,那什么是Log优先级呢?

printk 日志等级设置

Linux 内核为printk定义了8个打印等级,KERN_EMERG等级最高,KERN_DEBUG等级最低。在内核配置时,有一个宏来设定系统默认的打印等级 CONFIG_MESSAGE_LOGLEVEL_DEFAULT,通常该值设置为4,那么只有打印等级高于4时才会打印到终端或者串口。
kern_levels.h

#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 用于调试信息 */ 
```一个有`8`个等级,从`0``7`,优先级依次降低。
通常通过修改`/proc/sys/kernel/printk`来设置`printk`打印。
cat /proc/sys/kernel/printk
7       4       1       7

//打开所有的内核打印
echo 8 > /proc/sys/kernel/printk

cat /proc/sys/kernel/printk
8       4       1       7

4个值的含义依次如下:

  • console_loglevel:当前consolelog级别,只有更高优先级的log才被允许打印到console

  • default_message_loglevel:当不指定log级别时,printk默认使用的log级别;

  • minimum_console_loglevelconsole能设定的最高log级别;

  • default_console_loglevel:默认的consolelog级别。

另外,关于printk格式化字符串形式,参考printk-formats.txt
使用dmesg命令,可以显示之前所有的打印信息,常配合grep来查找历史纪录。

屏蔽等级日志控制机制

屏蔽如下代码片段:
文件目录: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);
        }
}

printk打印常用方式

打印对应函数名(func)及对应行数(LINE

printk("[me]%s[%d].\n",__func__,__LINE__);
作者:SteveChen  创建时间:2025-03-30 18:05
最后编辑:SteveChen  更新时间:2025-03-30 18:07