1 printk#
printk函数主要做两件事情:第一件就是将信息记录到log中,而第二件事就是调用控制台驱动来将信息输出。printk的相关函数定义在linux/printk.h。
1.1 日志级别#
printk需要设置日志级别,用来控制printk打印的这条信息是否在终端上显示的,当printk设置的日志级别高于控制台级别时,printk要打印的信息才会在控制台打印出来。
内核日志一共有8种级别:
1 |
1.2 控制台级别#
1 |
|
使用命令 cat /proc/sys/kernel/printk
来查看这四个值:
结果显示了 current, default, minimum 和 boot-time-default 日志级别。
其中的 4 4 1 7,分别对应与:console_loglevel、default_message_loglevel、minimum_console_loglevel、default_console_loglevel
default_message_loglevel:
缺省时的消息日志级别,因此当printk未指定优先级时,将以该默认级别输出,也就是DEFAULT_MESSAGE_LOGLEVEL =4, 对应KERN_WARNING。
也就是说printk("hello world\n");就表示printk(KERN_WARNING "hello world\n");
那如果我们将控制台级别设成<4,如:
echo 3 > /proc/sys/kernel/printk
那么printk("hello world\n");
就无法输出到控制台。
名称 | 字符串 | 别名函数 |
---|---|---|
KERN_EMERG | “0” | pr_emerg() |
KERN_ALERT | “1” | pr_alert() |
KERN_CRIT | “2” | pr_crit() |
KERN_ERR | “3” | pr_err() |
KERN_WARNING | “4” | pr_warn() |
KERN_NOTICE | “5” | pr_notice() |
KERN_INFO | “6” | pr_info() |
KERN_DEBUG | “7” | pr_debug() and pr_devel() 若定义了DEBUG |
KERN_DEFAULT | “” | KERN_WARNING |
KERN_CONT | “c” | pr_cont() |
1.3 修改控制台级别#
1 | echo "n" > /proc/sys/kernel/printk |
另一种方式,使用 dmesg
:
1 | dmesg -n 8 |
此时所有的printk日志级别都会被输出到控制台,如下图所示:
1 | printk ( KERN_EMERG "Hello, EMERG.\n" ) ; |
再来一种方法,修改bootargs:
Uboot中修改console=ttyS0,115200
改为loglevel=7 console=ttyS0,115200
,表示设置内核的console_loglevel 值=7
,开机cat /proc/sys/kernel/printk
,可以看到控制台级别被设置成了7:
1.4 printk带时间戳讯息#
make menuconfig开启如下:
1.5 printk底层实现#
源码位于kernel\printk\printk.c
。
1 | printk |
1.5.1 命令行参数console#
1 | /* IMX6ULL */ |
命令行信息可以来自设备树或者环境变量:
1 | / { |
修改环境变量:
1 | /* 进入IMX6ULL的UBOOT */ |
1.5.2 console驱动注册过程#
1 | struct console { |
1.5.2.1 处理命令行参数#
1 | __setup("console=", console_setup); |
处理u-boot通过dts传给内核的cmdline参数,比如bootparam参数。
对于这两个”console=xxx”就会调用console_setup函数两次,构造得到2个数组项:
1 | struct console_cmdline { |
在cmdline中,最后的”console=xxx”就是”selected_console”(被选中的console,对应/dev/console):
1.5.2.2 register_console#
1 | uart_add_one_port |
Linux下Uart子系统驱动 - fuzidage - 博客园 (cnblogs.com)
1.5.2.3 /dev/console#
1 | tty_open |
Linux下Uart子系统驱动 - fuzidage - 博客园 (cnblogs.com)
1.6 内核信息的早期打印#
当我们注册了uart_driver、并调用uart_add_one_port后,它里面才注册console,在这之后才能使用printk。
如果想更早地使用printk函数,比如在安装UART驱动之前就使用printk,这时就需要自己去注册console。
更早地、单独地注册console,有两种方法:
early_printk:自己实现write函数,不涉及设备树,简单明了
earlycon:通过设备树传入硬件信息,跟内核中驱动程序匹配
earlycon是新的、推荐的方法,在内核已经有驱动的前提下,通过设备树或cmdline指定寄存器地址即可。
1.6.1 early_printk#
arch\arm\kernel\early_printk.c,必须实现这几点:
- 配置内核,选择:CONFIG_EARLY_PRINTK
- 内核中实现:printch函数
- cmdline中添加:earlyprintk
1.6.2 earlycon#
2 prink.h介绍#
1 |
|
pr_emerg到pr_info
都是一些基本的kernel打印函数,用来设置内核日志打印级别,可以看到它和下面这种打印本质上并无差异。
而pr_debug
则有3种输出方式,当开启dynamic_debug后,则走dynamic_pr_debug流程(dynamic_debug见下一节)。当用户开启了DEBUG宏,则走printk流程,否则什么都不打印。
3 dmesg命令#
3.1 /proc/kmsg#
/proc/kmsg
是一个特殊的文件,它提供了内核消息缓冲区的访问,这个缓冲区包含了内核产生的所有消息,包括各种调试和错误信息,如内核的启动打印
dmesg命令就是cat /proc/kmsg
。
3.2 修改内核日志缓冲区大小#
3.3 dmesg#
dmesg命令是从kernel ring buffer中读取内核日志信息。因此可以用dmesg命令查看。
1 | -C: 直接清除ring buffer |
其实用dmesg -n也是可以设置控制台打印级别:
有时候在调试kernel驱动时内核panic了or死锁了,那么无法敲命令,如何查看日志呢?重启后日志就没了,那么可以敲如下命令:
1 | cat /proc/kmsg > /mnt/data/ker.log & 2>&1 |
用后台进程将日志导入到文件。
- dmesg -f:根据系统打印信息:
可用系统有
kern - kernel messages(内核信息)
user - random user-level messages(随机用户信息)
mail - mail system(邮件系统信息)
daemon - system daemons(系统守护进程信息)
auth - security/authorization messages(认证授权安全信息)
syslog - messages generated internally by syslogd(系统日志信息)
lpr - line printer subsystem(打印机信息)
news - network news subsystem(网络系统信息)
- dmesg -l:根据level来打印信息:
可用的level信息有
emerg - system is unusable(系统无法使用)
alert - action must be taken immediately
crit - critical conditions(临界条件)
err - error conditions(错误条件)
warn - warning conditions(警告条件)
notice - normal but significant condition
info - informational
debug - debug-level messages(debug)