linux 内核 printk 使用

本文详细介绍了Linux内核中的printk函数及其不同消息级别,用于在内核启动和调试时输出信息。printk函数使用环形缓冲区管理日志,并提供多个级别以区分消息的重要程度。此外,还提到了在系统早期阶段使用的early_printk函数,它能够在终端初始化之前打印信息,特别适用于启动过程的调试。内核开发者通常依赖这些工具进行驱动程序和内核组件的调试。
摘要由CSDN通过智能技术生成

在linux 中,内核打印语句 printk() 会将内核信息输出到内核信息缓冲区中。内核信息缓冲区是一个环形缓冲区(ring buffer),因此,如果插入的信息过多,就会将之前的信息冲刷掉。

printk() 定义了8个消息级别,分为级别0 ~ 7,级别越低(数值越大)的消息越不重要,第0级是紧急事件,

第7级是调试级,在内核中 include/linux/printk.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    */

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

调试时,通常使用封装了 printk 的更高级的宏,其中可替代 printk 的宏如下:

#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__)
#define pr_cont(fmt, ...) \
    printk(KERN_CONT fmt, ##__VA_ARGS__)

/* pr_devel() should produce zero code unless DEBUG is defined */
#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

在打印信息时,如果要打印所出其所在函数,可以使用 __FUNCTION__,

printk("%s: Incorrect IRQ %d from %s\n", __FUNCTION, irq, devname);

对于做Linux内核开发的人来说,printk实在是再熟悉不过了。内核启动时显示的各种信息大部分都是通过她来实现的,在做内核驱动调试的时候大部分 时候使用她就足矣。她之所以用得如此广泛,一个是由于她使用方便,还有一个重要的原因是她的健壮性。它使用范围很广,几乎是内核的任何地方都能调用它。你既可以在中断上下文、进程上下中调用她,也可以在任何持有锁时调用她,更可以在SMP系统中调用她,且调用时连锁都不必使用。这样好的适应性来源于她的设计,一个由三个指针控制的简单“ring buffer”。

注意上面说到的是:“几乎”在内核的任何地方都可以使用。那什么地方使用会有“问题”?那就是在系统启动过程的早期,终端初始化之前的某些地方虽然可以使用,但是在终端和控制台被初始化之前所有信息都被缓存在printk的简单的ring buffer(环形缓冲区)中,直到终端和控制台被初始化之后,所有缓存信息都被一并输出。

如果你要调试的是启动过程最开始的部分(如setup_arch()),可以依靠此时能够工作的硬件设备(如串口)与外界通信,使用printk()的变体early_printk()函数。她在启动过程初期就具有在终端上打印的能力,功能与prink()类似,区别在于:

(1)函数名
(2)能够更早地工作(输出信息)
(3)她有自己的小缓存(一般为512B)
(4)一次性输出到硬件设备,不再以ring buffer的形式保留信息。
但该函数在一些构架上无法实现,所以这种办法缺少可移植性。(大多数构架都可以,包括x86和arm)。
所以,除非要在启动初期在终端上输出,否则我们认为printk()在任何情况下都能工作。这点从内核的启动代码中就可以看出,在已进入start_kernel不久就通过printk打印内核版本信息了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值