LV06-01-内核模块-02-printk
本文主要是内核模块中常用的打印函数printk的相关笔记。若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows版本 | windows11 |
Ubuntu版本 | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
终端软件 | MobaXterm(Professional Edition v23.0 Build 5042 (license)) |
Linux开发板 | 正点原子 i.MX6ULL Linux 阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
Win32DiskImager | Win32DiskImager v1.0 |
buildroot | 2023.05.1版本 |
点击查看本文参考资料
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.nxp.com.cn/ | NXP官方网站 | |
https://www.nxpic.org.cn/ | NXP 官方社区 | |
https://u-boot.readthedocs.io/en/latest/ | u-boot官网 | |
https://www.kernel.org/ | linux内核官网 |
点击查看相关文件下载
分类 | 网址 | 说明 |
NXP | https://github.com/nxp-imx | NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库 |
https://elixir.bootlin.com/linux/latest/source | 在线阅读linux kernel源码 | |
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
I.MX6ULL | i.MX 6ULL Applications Processors for Industrial Products | I.MX6ULL 芯片手册(datasheet,可以在线查看) |
i.MX 6ULL Applications ProcessorReference Manual | I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网) |
一、概述
大部分常用的C库函数在Linux内核中都已经得到了实现。在所有没有实现的函数中,最著名的就数printf()函数了。内核代码虽然无法调用 printf()函数,但它可以调用printk()函数。printk()函数负责把格式化好的字符串拷贝到内核日志缓冲上,这样syslog程序就可 以通过读取该缓冲区来获取内核信息。
printk()函数是直接使用了向终端写函数tty_write()。而printf()函数是调用write()系统调用函数向标准输出设备写。所以 在用户态(如进程0)不能够直接使用printk()函数,而在内核态由于他已是特权级,所以无需系统调用来改变特权级,因而能够直接使用 printk()函数。printf是使用了标准的C库函数的时候才能使用的,而内核中无法使用标准的C库函数,所以就连最常见的printf都不能使用。
二、两个级别
1. 日志级别
1.1 有哪些?
printk相比printf来说还多了个:日志级别的设置,用来控制printk打印的这条信息是否在终端上显示的,当日志级别的数值小于控制台级别时,printk要打印的信息才会在控制台打印出来,否则不会显示在控制台。在我们内核中一共有8种级别(数字越小级别越高),他们定义在 include/linux/kern_levels.h 中,分别为:
1 |
所有的printk()
消息都会被打印到内核日志缓冲区,这是一个通过/dev/kmsg输出到用户空间的环 形缓冲区。读取它的通常方法是使用 dmesg
。日志级别指定了一条消息的重要性。内核根据日志级别和当前 console_loglevel (一个内核变量)决定是否立即显示消息(将其打印到当前控制台)。如果消息的优先级比 console_loglevel 高(日志级 别值较低),消息将被打印到控制台。如果省略了日志级别,则以 KERN_DEFAULT
级别打印消息。格式字符串虽然与C99基本兼容,但并不遵循完全相同的规范。它有一些扩展和一些限制(没 有 %n
或浮点转换指定符)。
1.2 怎么控制?
我们可以直接在printk中指定本条打印信息的级别,一般格式如下:
1 | printk(KERN_INFO "Message: %s\n", arg); |
直接在格式字符串前指定打印等级即可。
2. 控制台级别
2.1 有哪些?
上边提到了控制台级别,控制台级别定义在哪?它们定义在 include/linux/printk.h 文件中:
1 | /* printk's without a loglevel use this.. */ |
我们看一下console_printk这个数组(定义在ernel/printk/printk.c中):
1 | int console_printk[4] = { |
- console_printk[0]:
CONSOLE_LOGLEVEL_DEFAULT
,控制台日志级别,优先级高于该值的消息将在控制台显示(也就是终端)。 - console_printk[1]:
MESSAGE_LOGLEVEL_DEFAULT
,默认消息日志级别,printk没定义优先级时,打印这个优先级以上的消息。 - console_printk[2]:
CONSOLE_LOGLEVEL_MIN
,最小控制台日志级别,控制台日志级别可被设置的最小值(最高优先级 - console_printk[3]:
CONSOLE_LOGLEVEL_DEFAULT
,默认的控制台日志级别。
我们可以在linux系统中通过以下命令查看:
1 | cat /proc/sys/kernel/printk |
如上图,
- console_printk[0]:为
CONSOLE_LOGLEVEL_DEFAULT
:默认为7,所有优先级高于7的log等级(0~6),都会打印在终端上。 - console_printk[1]:为
MESSAGE_LOGLEVEL_DEFAULT
:默认为4,printk打印消息时的默认等级。 - console_printk[2]:为
CONSOLE_LOGLEVEL_MIN
:控制台日志级别可被设置的最小值(最高优先级),这里默认为1。 - console_printk[3]:为
CONSOLE_LOGLEVEL_DEFAULT
:默认的控制台日志级别,默认为7。
2.2 怎么控制?
我们直接在终端输入以下指令即可:
1 | echo 8 4 1 7 > /proc/sys/kernel/printk |
中间的数字分别就代表各个等级,可以直接这样修改。例如:
三、使用实例
代码可以看这里:01_module_load/printk_eg · sumumm/imx6ull-linuxdriver-eg - 码云 - 开源中国 (gitee.com)。操作的时候主要是看加载驱动的时候的打印信息,主要是以下信息:
1 | printk(KERN_EMERG"KERN_EMERG:%s\r\n", KERN_EMERG); |
然后通过下边的命令修改各个默认值来看内核日志打印情况:
1 | echo 8 4 1 7 > /proc/sys/kernel/printk |