Linux驱动编程 - printk内核打印

目录

一、printk 介绍

1、打印级别

2、查看、修改printk打印等级

2.1 查看 printk 打印等级

2.2 配置menuconfig修改默认打印等级

2.3 Linux系统命令修改printk打印等级

3、代码中使用 printk

二、dev_xxx 系列函数

1、dev_xxx源码

2、测试结果

三、修改日志输出位置


一、printk 介绍


在Linux内核中,使用 printk 能将打印信息输出到控制台。

printk函数主要做两件事情:

  • 将信息记录log_bug中(一般为/var/log/message);
  • 调用控制台驱动来将log_bug信息输出到控制台;

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

static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);

1、打印级别


Linux 内核共提供了八种不同的消息级别,分为级别 0~7。数值越大,表示级别越低。宏定义在 include/linux/kern_levels.h 中:

#define    KERN_EMERG     "<0>"    /* system is unusable            */
#define    KERN_ALERT     "<1>"    /* action must be taken immediately    */
#define    KERN_CRIT      "<2>"    /* critical conditions            */
#define    KERN_ERR       "<3>"    /* error conditions            */
#define    KERN_WARNING   "<4>"    /* warning conditions            */
#define    KERN_NOTICE    "<5>"    /* normal but significant condition    */
#define    KERN_INFO      "<6>"    /* informational            */
#define    KERN_DEBUG     "<7>"    /* debug-level messages            */

说明:

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

注意:无论当前控制台日志级别是何值,即使没有在控制台打印出来,都可以通过下面两种方法查看日志:

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

2、查看、修改printk打印等级


2.1 查看 printk 打印等级

通过 /proc/sys/kernel/printk 文件可以查看、修改 printk 的打印等级

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

4个数字功能如下: 

  • 控制台日志级别:优先级高于该值的消息将被打印至控制台;
  • 默认的消息日志级别:将用该优先级来打印没有优先级的消息(即 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);

2.2 配置menuconfig修改默认打印等级

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

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

如图: 

2.3 Linux系统命令修改printk打印等级

修改 /proc/sys/kernel/printk 中的值来改变内核打印效果。例如:屏蔽掉所有的内核 printk 打印,只需要把第一个数值调到最小值1或者0

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

3、代码中使用 printk


printk(KERN_ERR "debug info\n");    // 打印信息以 KERN_ERR 等级输出

二、dev_xxx 系列函数


        Linux 提供了一些驱动模型诊断宏,例如dev_err、dev_warn、dev_info等等。使用它们,不仅可以按标记的消息级别打印,还会打印对应的设备和驱动信息。目前在Linux内核驱动代码中,不再建议直接使用printk添加打印信息,而是使用dev_info、dev_dbg、dev_err之类的函数代替。

dev_xxx函数的本质还是使用printk打印的,但是相比起printk区别:

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

1、dev_xxx源码


下面简述下这几个dev_xxx函数的基本使用规则,以及动态调试使用方式。

  • dev_info():启动过程、或者模块加载过程等“通知类的”信息等,一般只会通知一次,例如probe函数;
  • dev_dbg():一般使用在普通错误,如-EINVAL、-ENOMEM等errno发生处,用于调试;
  • dev_err():一般使用在严重错误,尤其是用户无法得到errno的地方,或者程序员不容易猜测系统哪里出了问题的地方;
/* drivers/base/core.c */
#define define_dev_printk_level(func, kern_level)		\
void func(const struct device *dev, const char *fmt, ...)	\
{								\
	struct va_format vaf;					\
	va_list args;						\
								\
	va_start(args, fmt);					\
								\
	vaf.fmt = fmt;						\
	vaf.va = &args;						\
								\
	__dev_printk(kern_level, dev, &vaf);			\
								\
	va_end(args);						\
}								\
EXPORT_SYMBOL(func);

define_dev_printk_level(_dev_emerg, KERN_EMERG);
define_dev_printk_level(_dev_alert, KERN_ALERT);
define_dev_printk_level(_dev_crit, KERN_CRIT);
define_dev_printk_level(_dev_err, KERN_ERR);
define_dev_printk_level(_dev_warn, KERN_WARNING);
define_dev_printk_level(_dev_notice, KERN_NOTICE);
define_dev_printk_level(_dev_info, KERN_INFO);


/* include/linux/device.h */
#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__)

2、测试结果


dev_warn(&rtc->dev, "invalid alarm value: %ptR\n", &alarm->time);  // %ptR输出时间

输出日志如下:

rtc rtc0: invalid alarm value: 1900-02-01T00:00:00

三、修改日志输出位置


uboot的bootargs里配置“console=ttySA0,115200”,将会将日志信息输出在串口UART0上。同样如果配置“console=tty1 console=ttySA0,115200”,将会同时将将日志信息输出到串口UART0以及LCD屏幕上。


### Linux 驱动程序中使用 `printk` 函数打印调试信息 在开发 Linux 驱动程序时,通常需要记录调试信息以便于排查问题。为此,内核提供了专门用于打印消息的函数——`printk()`。以下是关于如何在驱动程序中正确使用 `printk` 的详细介绍。 #### 1. 基本功能与用途 `printk` 是一种类似于标准 C 库中的 `printf` 的函数,但它专为 Linux 内核设计,在内核空间运行并支持特定的日志级别控制[^1]。它主要用于向系统日志(通常是 `/var/log/syslog` 或者通过命令 `dmesg` 查看)写入信息。 #### 2. 日志级别的定义 为了区分不同重要程度的消息,`printk` 支持多种日志级别前缀。这些前缀决定了消息的重要性和显示方式: | 宏名 | 数值 | 描述 | |---------------------|------|----------------------------------------| | KERN_EMERG | 0 | 系统不可用 | | KERN_ALERT | 1 | 必须立即采取行动 | | KERN_CRIT | 2 | 关键条件 | | KERN_ERR | 3 | 错误条件 | | KERN_WARNING | 4 | 警告条件 | | KERN_NOTICE | 5 | 正常但重要的条件 | | KERN_INFO | 6 | 提示性信息 | | KERN_DEBUG | 7 | 调试级信息 | 每条消息都应带有相应的日志级别宏来标记其严重程度。例如: ```c printk(KERN_INFO "This is an informational message.\n"); ``` #### 3. 示例代码展示 下面是一个简单的例子,演示如何在驱动程序初始化和退出过程中使用 `printk` 输出调试信息: ```c #include <linux/module.h> #include <linux/kernel.h> static int __init my_module_init(void) { printk(KERN_INFO "My module initialized successfully\n"); return 0; } static void __exit my_module_exit(void) { printk(KERN_INFO "My module exiting cleanly\n"); } module_init(my_module_init); module_exit(my_module_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple example of using printk in a driver."); ``` 上述代码会在加载模块时输出一条提示信息,并在卸载模块时再次通知用户操作完成情况。 #### 4. 解决常见问题 如果发现 `printk` 发送的内容未能正常出现在终端上,则可能是由于以下几个原因造成的[^3]: - **日志级别过高**:当前系统的默认日志等级可能低于所使用的 `KERN_*` 定义值。 - **缓冲区溢出**:当大量数据被快速发送至环形缓存而未及时读取时会发生丢失现象。 - **TTY 设置不当**:某些情况下,串口或其他 TTY 设备并未开启接收来自 kernel log 的转发服务。 针对这些问题可以通过调整参数或者修改启动选项加以改善。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值