Linux内核基础篇——printk调试

简介: Linux内核基础篇——printk调试

很多内核开发者喜欢的调试工具是printk,在Linux内核中,使用printk()函数来打印信息,它与C库的printf()函数类似。

printk()与printf()的一个重要区别是:printk()提供输出等级。内核会根据这个等级来判断是否在终端或者串口中输出。

printk等级

路径:include/linux/kern_levels.h

#define KERN_EMERG KERN_SOH "0"  /* 最高输出等级,系统可能处于不可用的状态 */
#define KERN_ALERT KERN_SOH "1"  /* 紧急和理科需要处理的输出 */
#define KERN_CRIT KERN_SOH "2"  /* 紧急情况 */
#define KERN_ERR KERN_SOH "3"  /* 发生错误的情况 */
#define KERN_WARNING KERN_SOH "4" /* 警告 */
#define KERN_NOTICE KERN_SOH "5"  /* 重要的提示 */
#define KERN_INFO KERN_SOH "6"  /* 提示信息 */
#define KERN_DEBUG KERN_SOH "7"  /* 调试输出 */

Linux内核为printk定义了8个输出等级,KERN_EMERG等级最高,KERN_DEBUG等级最低。在配置内核时,由一个宏来设置系统默认的输出等级CONFIG_MESSAGE_LOGLEVEL_DEFAULT,通常这个默认输出等级为4,因此只有输出等级高于4时才会输出到终端或者串口,即只有KERN_EMERG~KERN_ERR满足这个条件。

通常在产品开发阶段,会把系统默认等级设置为最低,以便在开发测试阶段可以暴露更多的问题和调试信息,在发布产品时再把输出等级设置为0或者4

修改printk等级

# cat /proc/sys/kernel/printk  //printk默认有4个等级
7  4  1  7

四个数字分表代表:

控制台输出等级

默认消息等级

最低输出等级

默认控制台输出等级

在系统运行时,我们也可以修改系统的输出等级。打开所有的内核输出:

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

另外,还可以通过在启动内核时传递commandline给内核的方法来修改系统默认的输出等级。例如,使用uboot引导内核时,可以在uboot传参的bootargs参数上,加上“loglevel=8”,这样在系统启动时,就打开了所有内核输出。

printk的输出格式

在实际调试中,printk()可以和printf()一样,直接输出一条字符串。

不过为了更好的显示一些调试信息,可以加上函数名字(__func__)和代码行号(__LINE__),例如:

printk(KERN_EMERG"figo:%s, %d", __func__, __LINE__);

在双引号""前加上输出等级KERN_EMERG,代表输出等级为0

另外,在使用printk()的时候需要注意输出格式,否则在编译时会出现很多的警告。printk的输出格式:

数据类型 printk格式符
int %d或%x
unsigned int %u或%x
long %ld或%lx
long long %lld或%llx
unsigned long long %llu或%llx
size_t %zu或%zx
size_t %zd或%zx
函数指针 %pf

pr_xx( )封装

在使用printk的时候需要手动添加输出等级KERN_INFO、KERN_WARNING等,这样还是有些麻烦。因此,Linux内核也对printk进行了进一步的封装。

Linux内核将每一个输出等级封装为pr_xx()函数,例如,输出等级KERN_INFO封装为pr_info(),输出等级KERN_WARNING封装为pr_warn()。具体如下:

#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_warn(fmt, ...) \
 printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#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_err(fmt, ...) \
 printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)

这里对输出等级为KERN_DEBUG的封装是比较特殊的,因为debug等级比较常用,内核对pr_debug()分为了三种情况:

如果设置了 CONFIG_DYNAMIC_DEBUG,则此pr_debug()扩展为 dynamic_pr_debug(),主要用于动态输出。否则,如果定义了 DEBUG宏,则它等同于具有 KERN_DEBUG 日志级别的 printk。如果未定义 DEBUG,则它什么都不做

pr_debug()的定义如下:

/* If you are writing a driver, please use dev_dbg instead */
#if defined(CONFIG_DYNAMIC_DEBUG) || \
 (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
#include <linux/dynamic_debug.h>
/**
 * pr_debug - Print a debug-level message conditionally
 * @fmt: format string
 * @...: arguments for the format string
 *
 * This macro expands to dynamic_pr_debug() if CONFIG_DYNAMIC_DEBUG is
 * set. Otherwise, if DEBUG is defined, it's equivalent to a printk with
 * KERN_DEBUG loglevel. If DEBUG is not defined it does nothing.
 *
 * It uses pr_fmt() to generate the format string (dynamic_pr_debug() uses
 * pr_fmt() internally).
 */
#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

end

猜你喜欢

Linux内核基础篇——initcall

RISC-V SiFive U64内核——HPM硬件性能监视器

RISC-V SiFive U64内核——L2 Prefetcher预取器

RISC-V SiFive U54内核——PMP物理内存保护

RISC-V SiFive U54内核——PLIC平台级中断控制器

RISC-V SiFive U54内核——CLINT中断控制器

RISC-V SiFive U54内核——中断和异常详解

实战 | RISC-V Linux入口地址2M预留内存优化

RISC-V Linux启动之页表创建分析

RISC-V Linux汇编启动过程分析

RISC-V 入门笔记(新手必看!)

教你在QEMU上运行RISC-V Linux

OpenSBI三种固件的区别

写给新手的MMU工作原理

内核调试之devmem直接读写寄存器

相关文章
|
16天前
|
存储 Linux 调度
深入理解Linux内核:从用户空间到内核空间的旅程
【8月更文挑战第4天】在这篇文章中,我们将探索Linux操作系统的核心—内核。通过了解内核如何管理硬件资源,以及它是如何在用户空间和内核空间之间架起桥梁的,我们可以更好地理解操作系统的工作原理。本文将介绍一些关键概念,并通过代码示例来揭示这些概念是如何在实际中应用的。无论你是开发者、系统管理员还是对操作系统感兴趣的爱好者,这篇文章都将为你提供一个深入了解Linux内核的机会。让我们开始这段旅程吧!
|
2月前
|
Linux API 调度
技术笔记:Linux内核跟踪和性能分析
技术笔记:Linux内核跟踪和性能分析
|
2天前
|
Ubuntu Linux Windows
如何在WSL中的ubuntu编译Linux内核并且安装使用ebpf?
请注意,在WSL1中可能会由于内核架构限制而无法成功进行以上过程,WSL2对于Linux内核的完整支持更为合适。此外,部分步骤可能因不同的Linux发行版或内核版本而异。
9 4
|
1天前
|
Linux 开发者
Linux的诞生:Linus Torvalds的“惊天一敲”与Linux内核的“首秀”
在科技界璀璨星辰中,Linus Torvalds以一次“惊天一敲”悄然点燃了革命之火——Linux就此诞生。1991年,不满现状的Linus决定创造更好的操作系统,这一敲不仅开启了个人传奇,更奏响了技术革新的序章。他将Linux内核低调发布网络,随即吸引了全球开发者的目光与贡献,使之迅速成长为开源世界的巨星。Linus的故事告诉我们:伟大创举常源于微小想法,也许下一个改变世界的“一敲”就出自你手。
14 1
|
16天前
|
Ubuntu Linux 开发工具
深入探索Linux内核模块编程
【8月更文挑战第4天】在这篇文章中,我们不仅将探讨Linux内核模块的基础知识,还将通过一个实际的例子来展示如何编写一个简单的内核模块。我们将从理论出发,逐步过渡到动手实践,最终实现一个可以在Linux系统上运行的模块。文章的目标是为读者提供足够的信息和知识,以便他们能够自己编写内核模块,从而对操作系统的内部工作原理有更深入的了解。
|
17天前
|
Linux
Linux系统如何查看版本信息,内核、发行版、cpu、所有版本
Linux系统如何查看版本信息,内核、发行版、cpu、所有版本
|
18天前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。
|
14天前
|
存储 Unix Linux
揭秘Linux硬件组成:从内核魔法到设备树桥梁,打造你的超级系统,让你的Linux之旅畅通无阻,震撼体验来袭!
【8月更文挑战第5天】Linux作为顶级开源操作系统,凭借其强大的功能和灵活的架构,在众多领域大放异彩。本文首先概述了Linux的四大核心组件:内核、Shell、文件系统及应用程序,并深入探讨了内核的功能模块,如存储、CPU及进程管理等。接着介绍了设备树(Device Tree),它是连接硬件与内核的桥梁,通过DTS/DTB文件描述硬件信息,实现了跨平台兼容。此外,还简要介绍了Linux如何通过本地总线高效管理硬件资源,并阐述了文件系统与磁盘管理机制。通过这些内容,读者可以全面了解Linux的硬件组成及其核心技术。
30 3
|
20天前
|
运维 Java Linux
(九)JVM成神路之性能调优、GC调试、各内存区、Linux参数大全及实用小技巧
本章节主要用于补齐之前GC篇章以及JVM运行时数据区的一些JVM参数,更多的作用也可以看作是JVM的参数列表大全。对于开发者而言,能够控制JVM的部分也就只有启动参数了,同时,对于JVM的性能调优而言,JVM的参数也是基础。
|
15天前
|
缓存 网络协议 Unix
Linux 内核参数
Linux 内核参数
17 1

热门文章

最新文章