Linux 内核调试工具Printk介绍

文摘   科技   2023-12-04 07:31   广东  

点击上方蓝字关注我们

本文将介绍Linux 内核调试工具Printk,对于调试过驱动还是内核开发人员,都应该是用过该打印,这里将介绍Printk的相关原理和使用方法。
  • cpu: i.mx8mq
  • OS:Android 11
  • Kernel version:kernel 5.10

1. 初识Printk

printk是Linux内核中用于输出日志消息的核心函数。作为内核调试和信息记录的关键工具,printk提供了一种在内核空间中输出信息的机制。printk与平时常用的printf的用法非常相似,主要用于调试、性能分析以及记录内核运行时的各种信息,帮助开发人员理解系统行为和解决问题。

图1.1 printk 流程

从图中可以看到prink主要有两个方面:

  • 一部分是输出Loglog_buf这里是指将所有的级别的log都会存储到缓冲区当中,该缓冲区在/proc/kmsg,缓冲区是有大小,当缓冲区满了将会覆盖旧的部分,缓冲区大小设置通过配置CONFIG_LOG_BUF_SHIFT
  • 另外一部分是输出LogConsole,这里的所有的日志不一定将会输出,而是由loglevel进行控制了。日志到了Console可以通过串口进行输出,串口的打开关闭也就控制了串口的日志输出,另外还可以到达RAM Console,这对于在内核启动过程中、在控制台不可用的情况下以及在非交互性环境中记录日志非常有用。

2. Printk的日志级别以及设置

2.1 日志级别

根据内核源码:

"include/linux/kern_levels.h"

Printk的日志级别一共有8种,从0~7。其中KERN_EMERG是级别最高的,KERN_DEBUG等级是最低的,最高级别的日志信息需要特别关注。在内核配置中可以通过配置参数CONFIG_MESSAGE_LOGLEVEL_DEFAULT进行配置输出等级,一般默认为4

2.2 日志级别设置

方法1:
日志级别设置通过/proc/sys/kernel/printk修改,
先查看当前信息
cat /proc/sys/kernel/printk7       4       1       7
  • 第一个数字(console loglevel):控制台消息的显示级别,这里显示7表示控制台消息级别;
  • 第二个数字( default message log level):默认消息的显示级别,这里显示为4;
  • 第三个数字( minimum allowed console log level ):允许的最小控制台消息级别,设置为1;
  • 第四个数字( default console log level):默认的控制台消息级别,级别为7;
假如当前需要将console loglevel设置为6,debue 信息不需要打印那么设置为:
echo "6 4 1 7" > /proc/sys/kernel/printk
方法2::
方法1是临时的,但是如果需要永久设置那么久需要编辑/etc/sysctl.conf,添加或修改以下行:
kernel.printk = "6 4 1 7"

在运行命令:

sysctl -p

日志设置将会在重新启动后生效。

方法3:

可以通过uboot 设置bootargs来传递内核启动参数,这里可以采用在UBoot中进行设置:

setenv bootargs ${bootargs} loglevel=Xsaveenv
setenv bootargs ${bootargs} loglevel=X  X表示日志级别;

3. 常见使用方法

在开发的过程中,会发现linux 内核中很多不是直接使用prink这个函数,而是重新定义这个函数,但都是基于printk去实现。为什么这样,主要是可以更加方便,对于驱动设备可以方便打印模块,有的是为了简化日志的的级别。

3.1 pr_xx系列

定义了pr_notice、pr_info、pr_warn、pr_err 等接口。使用这些 pr_xxx 接口,就可以省去指定消息级别的麻烦。

3.2 dev_xx系列
"./include/linux/dev_printk.h"
/* * #defines for all the dev_<level> macros to prefix with whatever * possible use of #define dev_fmt(fmt) ... */
#define dev_emerg(dev, fmt, ...) \ _dev_emerg(dev, dev_fmt(fmt), ##__VA_ARGS__)#define dev_crit(dev, fmt, ...) \ _dev_crit(dev, dev_fmt(fmt), ##__VA_ARGS__)#define dev_alert(dev, fmt, ...) \ _dev_alert(dev, dev_fmt(fmt), ##__VA_ARGS__)#define dev_err(dev, fmt, ...) \ _dev_err(dev, dev_fmt(fmt), ##__VA_ARGS__)#define dev_warn(dev, fmt, ...) \ _dev_warn(dev, dev_fmt(fmt), ##__VA_ARGS__)#define dev_notice(dev, fmt, ...) \ _dev_notice(dev, dev_fmt(fmt), ##__VA_ARGS__)#define dev_info(dev, fmt, ...) \ _dev_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
这个系列应该是在驱动里面见的比较多的,使用这个接口将会自动打印本身模块名称,能够快速定位到那个模块。

4. 总结

本文简单介绍了Prink相关知识,无论是在内核还是驱动方面都是比较重要,使用率非常高的,对于帮助分析问题查找问题。

往期回顾:

Linux 内核调试工具devmem介绍

驱动相关:
Linux 外设屏幕背光介绍
Linux I2C通用接口介绍(二)
Linux I2C通用接口介绍(二)
Linux I2C通信接口介绍(一)
Linux SPI 通信接口介绍(二)
Linux SPI通信接口介绍
Linux Touch Screen (触摸屏)介绍
Linux MIPI DSI显示屏介绍
Linux 嵌入式外设屏接口介绍

兴趣加入嵌入式软件交流群

码思途远
一位码农的日常分享,探索软件技术知识与新闻的数字十字路口。