Linux内核之 printk 打印

  • 前言
  • 一、printk 介绍
    • 1. printk 消息级别
    • 2.内核 printk 文件
  • 二、调整打印级别
    • 1.在 menuconfig 中修改
    • 2.在系统中修改(常用)
  • 三、使用示例
  • 四、查看打印
    • 1.dmesg
    • 2./proc/kmsg
  • 五、其他常见的用法
    • 1.pr_xx 用法
    • 2.dev_xxx 用法
    • 3.动态打印调试方法

前言

printk 在内核源码中用来记录日志信息的函数,只能在内核源码范围内使用,用法类似于 printf 函数。

printk 函数主要做两件事情:

1、将信息记录到 log 中;
2、调用控制台驱动来将信息输出。


一、printk 介绍

printk 将内核信息输出到内核信息缓冲区中,内核缓冲区在 kernel/printk/printk.c 中定义:

static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);

1. printk 消息级别

Linux 内核共提供了八种不同的消息级别,分为级别 0~7。数值越大,表示级别越低,对应的消息越不重要。相应的宏定义在 include/linux/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 */

说明:

  • KERN_EMERG 表示紧急事件,一般是系统崩溃之前提示的消息;
  • KERN_ALERT 表示必须立即采取行动的消息;
  • KERN_CRIT 表示临界状态,通常涉及严重的硬件或软件操作失败;
  • KERN_ERR 用于报告错误状态,设备驱动程序会经常使用该级别来报告来自硬件的问题;
  • KERN_WARNING 对可能出现问题的情况进行警告,这类情况通常不会对系统造成严重的问题;
  • KERN_NOTICE 表示有必要进行提示的正常情形,许多与安全相关的状况用这个级别进行汇报;
  • KERN_INFO 表示内核提示信息,很多驱动程序在启动的时候,用这个级别打印出它们找到的硬件信息;
  • KERN_DEBUG 用于调试信息。

2.内核 printk 文件

通过 /proc/sys/kernel/printk 文件可以调节 printk 的输出等级,该文件有 4 个数字值。例如,在 Ubuntu 上的值如下:

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

四个数值的含义如下:
控制台日志级别:优先级高于该值的消息将被打印至控制台;
默认的消息日志级别:将用该优先级来打印没有优先级的消息(即 printk 没有指定消息级别);
最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级);
默认的控制台日志级别:控制台日志级别的缺省值。

这四个值是在 kernel/printk/printk.c 中被定义的,如下:

int console_printk[4] = {CONSOLE_LOGLEVEL_DEFAULT,       /* console_loglevel */MESSAGE_LOGLEVEL_DEFAULT,       /* default_message_loglevel */CONSOLE_LOGLEVEL_MIN,           /* minimum_console_loglevel */CONSOLE_LOGLEVEL_DEFAULT,       /* default_console_loglevel */
};
EXPORT_SYMBOL_GPL(console_printk);

二、调整打印级别

调整 printk 打印级别可以使得在印 Log 时控制输出 Log 的信息量以及控制自己想要输出的信息级别,下面则介绍调整 printk 打印级别的几种方法:

1.在 menuconfig 中修改

修改 CONFIG_MESSAGE_LOGLEVEL_DEFAULT 的值,然后重新编译,更新内核。menuconfig 配置路径如下:

Kernel hacking  --->printk and dmesg options  --->(4) Default message log level (1-7)

2.在系统中修改(常用)

在系统运行期间,可以通过修改 /proc/sys/kernel/printk 中的值来改变内核打印效果。例如,屏蔽掉所有的内核 printk 打印,只需要把第一个数值调到最小值1或者0,此时可以敲如下 cmd:

echo 1       4       1      7 > /proc/sys/kernel/printk

echo 1 > /proc/sys/kernel/printk # 表示只修改第一个值

三、使用示例

在使用 printk 时我们会将消息级别放到最开始的位置,例如:

printk(KERN_EMERG   "GetIot: KERN_EMERG\n");
printk(KERN_ALERT   "GetIot: KERN_ALERT\n");
printk(KERN_CRIT    "GetIot: KERN_CRIT\n");
printk(KERN_ERR     "GetIot: KERN_ERR\n");
printk(KERN_WARNING "GetIot: KERN_WARNING\n");
printk(KERN_NOTICE  "GetIot: KERN_NOTICE\n");
printk(KERN_INFO    "GetIot: KERN_INFO\n");
printk(KERN_DEBUG   "GetIot: KERN_DEBUG\n");

如果没有设置消息的日志级别,默认使用 default_message_loglevel 级别(该值可由上述两种方式修改,即第二个数值)。

当 printk 中的消息日志级别小于当前控制台的日志级别(console_printk[0])时,printk 的信息就会在控制台上显示。但无论当前控制台日志级别是何值,即使没有在控制台打印出来,都可以通过下面两种方法查看日志:

  • 第一种是使用 dmesg 命令打印;
  • 第二种是通过 cat /proc/kmsg 来打印。

另外如果配置好并运行了 syslogd 或 klogd,没有在控制台上显示的 printk 的信息也会追加到 /var/log/messages.log 中。

四、查看打印

将上述示例代码添加到 最简单的内核模块 的 hello_init 函数中,编译并执行 insmod hello.ko 安装模块。

1.dmesg

执行 dmesg | tail 将会看到如下打印:

[20120.194934] module init success
[20120.194935] GetIot: KERN_EMERG
[20120.194937] GetIot: KERN_ALERT
[20120.194938] GetIot: KERN_CRIT
[20120.194939] GetIot: KERN_ERR
[20120.194940] GetIot: KERN_WARNING
[20120.194940] GetIot: KERN_NOTICE
[20120.194940] GetIot: KERN_INFO
[20120.194941] GetIot: KERN_DEBUG

2./proc/kmsg

也可以执行 cat /proc/kmsg 等待内核打印:

$ sudo cat /proc/kmsg
<6>[20120.194934] module init success
<0>[20120.194935] GetIot: KERN_EMERG
<1>[20120.194937] GetIot: KERN_ALERT
<2>[20120.194938] GetIot: KERN_CRIT
<3>[20120.194939] GetIot: KERN_ERR
<4>[20120.194940] GetIot: KERN_WARNING
<5>[20120.194940] GetIot: KERN_NOTICE
<6>[20120.194940] GetIot: KERN_INFO
<7>[20120.194941] GetIot: KERN_DEBUG

五、其他常见的用法

1.pr_xx 用法

在 Linux 内核中除了直接使用 printk 加消息级别的方式,在 <linux/printk.h> 中还定义了 pr_notice、pr_info、pr_warn、pr_err 等接口。使用这些 pr_xxx 接口,就可以省去指定消息级别的麻烦。

#define pr_emerg(fmt, ...)     printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert(fmt, ...)     printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit(fmt, ...)      printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...)       printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warning(fmt, ...)   printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn pr_warning
#define pr_notice(fmt, ...)    printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...)      printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)#ifdef DEBUG
#define pr_devel(fmt, ...) \printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_devel(fmt, ...) \no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
#if defined(CONFIG_DYNAMIC_DEBUG)
#define pr_debug(fmt, ...)          \dynamic_pr_debug(fmt, ##__VA_ARGS__)
#elif defined(DEBUG)
#define pr_debug(fmt, ...) \printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_debug(fmt, ...) \no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif

如上各 pr_xx 定义,都是对 printk + 级别的封装,故都能和 prink 一样在 Linux 内核中直接使用,但是对于 pr_devel() 需要在定义了 DEBUG 宏,pr_debug() 需要在定义了 DEBUGCONFIG_DYNAMIC_DEBUG 宏才有实意。

2.dev_xxx 用法

对于 Linux 驱动程序,在 <linux/dev_printk.h> 里也提供了一些驱动模型诊断宏,例如 dev_errdev_warndev_info 等等。使用它们,不仅可以按标记的消息级别打印,还会打印对应的设备和驱动信息,这对于驱动调试来说相当重要。

#define dev_emerg(dev, fmt, ...) \dev_printk_index_wrap(_dev_emerg, KERN_EMERG, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_crit(dev, fmt, ...) \dev_printk_index_wrap(_dev_crit, KERN_CRIT, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_alert(dev, fmt, ...) \dev_printk_index_wrap(_dev_alert, KERN_ALERT, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_err(dev, fmt, ...) \dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_warn(dev, fmt, ...) \dev_printk_index_wrap(_dev_warn, KERN_WARNING, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_notice(dev, fmt, ...) \dev_printk_index_wrap(_dev_notice, KERN_NOTICE, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_info(dev, fmt, ...) \dev_printk_index_wrap(_dev_info, KERN_INFO, dev, dev_fmt(fmt), ##__VA_ARGS__)#if defined(CONFIG_DYNAMIC_DEBUG) || \(defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
#define dev_dbg(dev, fmt, ...)                      \dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
#elif defined(DEBUG)
#define dev_dbg(dev, fmt, ...)                      \dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__)
#else
#define dev_dbg(dev, fmt, ...)                      \
({                                  \if (0)                             \dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \
})
#endif

如上 dev_xx 定义,其和 pr_xx 类似,都可以直接理解为 printk + 级别的封装,一般在 Linux 驱动程序中使用,同样需要注意,dev_dbg() 也不是无条件打印的,除非定义了 DEBUG 或设定了 CONFIG_DYNAMIC_DEBUG。一个相关约定是在已经开启了 DEBUG 时,使用 VERBOSE_DEBUG 来添加 dev_vdbg()。

补充:
menuconfig 中开启动态调试:

Kernel hacking  --->printk and dmesg options  --->[*] Enable dynamic printk() support

目前在 kernel 驱动代码中,都不再建议直接使用 printk 直接添加打印信息,而是使用 dev_info、dev_dbg、dev_err 之类的函数代替,虽然这些 dev_xxx 函数的本质还是使用 printk 打印的,但是相比起 printk,dev_xxx 的好处是:

  • 支持打印模块信息、dev 信息;
  • 支持动态调试(dynamic debug)方式。

3.动态打印调试方法

打开内核动态调试开关,make menuconfig 选中 CONFIG_DYNAMIC_DEBUG 以及 CONFIG_DEBUG_FS。Linux 启动后,使用命令行挂载上 dbgfs。

mkdir /mnt/dbg
mount -t debugfs none /mnt/dbg

使用下面方式控制你想输出 dev_dbg() 信息

  • 控制某个文件所有 dev_dbg()
echo -n "file xxx.c +p" > /mnt/dbg/dynamic_debug/control
  • 控制某个函数所有 dev_dbg()
echo -n "func xxx +p" > /mnt/dbg/dynamic_debug/control

运行程序,使用 dmesg 则可以看到相应 dev_dbg() 的输出信息。当调试结束,不再想输出 dev_dbg() 信息了,使用下面命令关闭即可。

echo -n "file xxx.c -p" > /mnt/dbg/dynamic_debug/control
echo -n "func xxx -p" > /mnt/dbg/dynamic_debug/control

动态打印调试的原理是,当编译选项 CONFIG_DYNAMIC_DEBUG 打开的时候,在编译阶段,kernel 会把所有使用 dev_dbg() 的信息记录在一个 table 中,这些信息我们可以从 /mnt/dbg/dynamic_debug/control 解析出来。

  • 开发版本,打开 CONFIG_DYNAMIC_DEBUGCONFIG_DEBUG_FS,配合 dbgfs 动态观测和调试内核代码;
  • 正式版本,关闭 CONFIG_DYNAMIC_DEBUGCONFIG_DEBUG_FS,所有 dbgfs 以及 dev_dbg 信息都从编译阶段自动移除。

参考链接

Linux内核之 printk 打印相关推荐

  1. linux内核中printk的打印级别

    内核中预定义的内核log等级 // include/linux/kern_levels.h#define KERN_SOH "\001" /* ASCII Start Of Hea ...

  2. 修改Linux内核的printk缓冲区(log缓冲区)大小

    点击打开链接 我们可以用printk打印kernel的日志信息(即log信息),根据时间戳可以判断内核新打印的log会覆盖掉以前打印的log.原因是内核用环形缓冲区存放打印的log信息.那么如何增大缓 ...

  3. linux内核最低等级打印信息如何打开?

    1.近期在调试一个驱动,该驱动是直接编译在内核中加载的,因此,若想要看到驱动加载过程中的打印信息,需要在内核启动阶段将内核的打印信息的等级设为比较低等级的. 可以下kernel/printk.c中将控 ...

  4. linux设置printk打印级别,内核printk打印等级

    为了确认内核打印等级以及prink 参数对打印的分级,在led驱动初始化代码[以及exit出口]加入如下代码. 每次insmod .rmmod led模块时,根据打印等级的设置,得到不同的打印结果: ...

  5. (十)linux内核时钟

    目录 一.什么是内核时钟 二.HZ 三.jiffies 四.linux中的延时函数 五.内核的动态定时器 六.附录 一.什么是内核时钟 1.内核时钟 操作系统的内核都需要一个系统时钟才可以工作,这个系 ...

  6. 永久linux修改内核打印级别,终端下更改printk打印级别

    如何去更改printk的打印级别? 1.查看当前控制台的打印级别 # cat /proc/sys/kernel/printk 该文件有4个数字值,它们根据日志记录消息的重要性,定义将其发送到何处,上面 ...

  7. 如何将linux内核的带级别控制的printk内容打印出来

    要将linux内核的带级别控制的printk内容打印出来,在命令行 输入 dmesg -n 8 就将所有级别的信息都打印出来 Linux命令:dmesg 功能说明:显示开机信息. 语 法:dmesg ...

  8. Linux 内核 printk打印

    调整内核打印信息级别: cat  /proc/sys/kernel/printk 7       7       1       7          其中第一个"7"表示内核打印 ...

  9. linux 内核打印函数 printk 用法

    printk在内核源码中用来记录日志信息的函数,只能在内核源码范围内使用.用法和printf非常相似 printk函数主要做两件事情:第一件就是将信息记录到log中,而第二件事就是调用控制台驱动来将信 ...

最新文章

  1. 不允许所请求的注册表访问权
  2. AHOI2013 Round2 Day2 简要题解
  3. 滴滴Uber合并?光大是不行的
  4. 篇二:Eclipse安装配置Maven
  5. 数据库唯一主键如何实现幂等性?
  6. C语言中内存分布及程序运行中(BSS段、数据段、代码段、堆栈)
  7. 智能合约从入门到精通:完整范例
  8. matlab 判断两个矩阵有元素相等_如何使用MATLAB对Excel中的多参数进行计算?
  9. 【Redis】Redis 五大基本数据类型
  10. Numpy 字符串处理
  11. 案例分享|数据可视化下的驱动业务增长
  12. Linux 死机时抢救措施
  13. 使用SoapUI生成wsdl文件客户端(二)
  14. 新塘单片机烧写器_ICP Programming Tool
  15. 支付宝小程序下单支付接口:40004 ACQ.INVALID_PARAMETER
  16. java刮刮乐,20行JS代码实现网页刮刮乐效果
  17. 如何设计qPCR引物序列
  18. 零基础学软件测试难吗?小白怎么半年内成为测试工程师
  19. php中::双冒号作用
  20. 9. 数仓开发之 DWD 层

热门文章

  1. 振铃的消除仿真实验(计控实验七simulink)
  2. 京东AI走过双十一:在技术时代建立信赖样本
  3. 对硬币、销钉、大米进行图像分割(附代码)
  4. java 获取法定节假日+周末+排除补班
  5. Java 工作时长计算(除去午休时长,周六周日)
  6. 【华为上机真题】按照身高体重排队
  7. [RK3399][Android8.1] 调试记录 --- LT9611驱动调试
  8. 成都迎来史诗级架构运维峰会,三大女神降临,送海量技术图书(8折优惠仅剩一周)...
  9. 029 高阶导数之sinx(n)、cosx(n)及Leibniz公式
  10. 饶少阳:运营商急需网络大数据能力