前言
讲几篇常用内核驱动调试的方法,先从我最常用的printk的使用方法开始讲起, printk在内核源码中用来记录日志信息的函数,方便我们调试追踪代码,只能在内核源码范围内使用。 本篇内核采用5.10版本
日志级别
很多内核开发者最喜欢的调试工具之一是printk(),printk()是内核提供的格式化输出函数,它和C标准库提供的printf()函数类似。printk()函数和printf()函数的一个重要区别是前者提供了输出等级,内核根据这个等级来判断是否在终端或串口中输出结果。
下面是Linux内核提供的8种打印等级。
file: 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" /* 调试等级 */
可以看出KERN_EMERG等级最高,KERN_DEBUG等级最低,从这里也可以看出他们的优先级是数值越小,其紧急和严重程度就越高,release版本通常是KERN_WARNING等级,debug版本通常是KERN_DEBUG, 越高优先级打印内容会更多
控制台级别在内核printk源码如下写道
file: kernel/printk/printk.c
#define MINIMUM_CONSOLE_LOGLEVEL 1 /*可以使用的最小日志级别*/
#define DEFAULT_CONSOLE_LOGLEVEL 1 /*比KERN_DEBUG 更重要的消息都被打印*/
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
int console_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL, /*控制台日志级别,优先级高于该值的消息将在控制台显示 等级范围1~15*/
DEFAULT_MESSAGE_LOGLEVEL,/*默认消息日志级别,printk没定义优先级时,打印这个优先级以上的消息,等级范围1~7*/
MINIMUM_CONSOLE_LOGLEVEL,/*最小控制台日志级别,控制台日志级别可被设置的最小值(最高优先级)*/
DEFAULT_CONSOLE_LOGLEVEL,/* 默认的控制台日志级别,等级范围1~15*/
};
系统下查看日志级别的时候,可以使用命令 cat /proc/sys/kernel/printk来查看这四个值 通过 /proc/sys/kernel/printk 文件可以调节 printk 的输出等级,该文件有 4 个数字值:
$ cat /proc/sys/kernel/printk
4 4 1 7
四个数值含义分别如下对应printk源码数组内容:
-
控制台日志级别:优先级高于该值的消息将被打印至控制台; -
默认的消息日志级别:将用该优先级来打印没有优先级的消息(即 printk 没有指定消息级别); -
最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级); -
默认的控制台日志级别:控制台日志级别的缺省值。
四种控制日志级别的设置
说完日志级别以及控制台级别内核相关源代码我们说下四种控制printk打印输出的方法
defconfig
在配置内核时,有一个宏用来系统默认的输出等级 CONFIG_MESSAGE_LOGLEVEL_DEFAULT,通常默认设置4.只有当输出等级高于4时才会输出到终端或串口。通常在产品开发阶段,会吧系统的默认等级设置到最低,以便在开发测试阶段暴露更多的问题和调试信息,在产品发布时在把输出等级设置为0或4。
file:arch/arm64/configs/OK3588-Linux_defconfig
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 //将默认输出等级设置为7,表示打开所有的输出信息
这个方法实测好像是不行,但是网上说是可以的,不知道为啥有大佬知道可以后台方便告诉我
bootargs参数
此外还可以通过在启动内核时传递命令行给内核的方法来修改系统默认的输出等级,这里我用QEMU启动时添加“loglevel=8”给内核的bootargs参数
qemu-system-aarch64 -machine virt,virtualization=true,gic-version=3 -nographic -m size=1024M -cpu cortex-a57 -smp 4 -kernel arch/arm64/boot/Image -initrd rootfs.cpio.gz -append "root=/dev/ram console=ttyAMA0 rdinit=/linuxrc loglevel=8"
查看系统下的printk输出等级
# cat /proc/sys/kernel/printk
8 4 1 7
例如传递“loglevel=7”给内核的启动参数
qemu-system-aarch64 -machine virt,virtualization=true,gic-version=3 -nographic -m size=1024M -cpu cortex-a57 -smp 4 -kernel arch/arm64/boot/Image -initrd rootfs.cpio.gz -append "root=/dev/ram console=ttyAMA0 rdinit=/linuxrc loglevel=7"
查看系统下的printk输出等级
# cat /proc/sys/kernel/printk
7 4 1 7
你会看到loglevel=x,修改的是控制台串口日志打印等级,但是实际在开发板系统上的bootargs添加loglevel参数,cat /proc/sys/kernel/printk节点参数是不行的,不知道为啥有知道的大佬可以留言。
procfs节点
在系统运行时,默认系统的printk输出等级
$ cat /proc/sys/kernel/printk
4 4 1 7
修改的是控制台串口日志打印等级为7
$ echo 7 > /proc/sys/kernel/printk
或者
$ echo “7 4 1 7” > /proc/sys/kernel/printk
在查看所有printk输出等级
$ cat /proc/sys/kernel/printk
7 4 1 7
如果屏蔽掉所有的内核 printk 打印,只需要把第一个数值调到最小值 1 或者 0,指令如下:
$ echo 1 4 1 7 > /proc/sys/kernel/printk
dmesg
在系统运行时,可以用dmesg方法控制输出打印等级方法,如果屏蔽掉所有的内核 printk 打印, 控制台输出等级为1如下操作
$ dmesg -n 1
相对于
$ cat /proc/sys/kernel/printk
1 4 1 7
如控制台输出等级为7如下操作
$ dmesg -n 7
相当于
$ cat /proc/sys/kernel/printk
7 4 1 7
也就是dmesg -n x,x来输出控制控制台输出等级
以上四种我最常用的是proc节点和dmesg的方法控制日志打印信息等级
实际使用
在实际调试中,printk加在代码里输出文件名(FILE),输出函数名(func)和代码行号(LINE)也是一个很好的技巧
printk("pan:%s %s, %d", __FILE__, __func__,__LINE__);
读者要注意printk()输出格式,如下表所示,否则在编译时会出现很多的警告。
总结
本篇我们学会了四种控制日志级别的设置以及常用的使用方法,如有疑问,可以评论区留言,支持的话一键三连!欢迎关注公众号[Linux随笔录],不定期分享Linux小知识