Dmesg和Linux内核日志

来自百问网嵌入式Linux wiki
Zhouyuebiao讨论 | 贡献2020年1月6日 (一) 15:16的版本
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)


本文的目的是提供有关Linux®内核日志的信息,包括配置,并详细说明dmesg命令的用法。

简介

Linux内核能够打印日志和跟踪消息,它们默认情况下存储在环形缓冲区中。
使用串行端口在 uart/console 上应用过滤器,也可以显示相同的消息。这是在内核命令行中使用 "console" 参数定义的。有关详细信息,请参考[1]
dmesg是内核控制台上的shell命令,该命令还会显示环形缓冲区的内容,带或不带过滤器(默认)。

printk函数

从内核代码中获取一些调试信息的最简单方法是通过打印出与内核等效的printf的各种信息-printk函数及其派生类。
请参阅elinux.org[2] 以获取参考。该信息将发送到控制台,并存储在环形缓冲区中。
printk("My Debugger is Printk\n");
您也可以查看Linux内核软件包中提供的printk-format.txt[3]文档,以获取有关语法和格式的详细信息。

linux kernel 环形缓冲区

Linux内核管理环形缓冲区以存储日志和跟踪消息的方式。
缓冲区的大小无法在运行时修改,其默认大小值为 2^CONFIG_LOG_BUF_SHIF 字节。
要更改它,有3种可能的方式:
  • 修改defconfig文件中的CONFIG_LOG_BUF_SHIFT值或使用配置参数文件:
In example for 64K : CONFIG_LOG_BUF_SHIFT=16
  • 使用Linux内核menuconfig更新配置
Location:
  -> General setup
    -> Kernel log buffer size (16 => 64KB, 17 => 128KB)
  • 在内核cmdline 行中修改内核参数 [4](通过设备树中的bootargs值,或直接在extlinux uboot配置文件中修改)
bootargs = "root=/dev/mmcblk0p5 rootwait rw console=ttySTM0,115200 log_buf_len=65536";
可以使用dmesg命令显示此环形缓冲区(请参阅下文 dmesg 章节)

log级别

如需了解详细信息,请参见elinux.org[5]
内核使用日志级别来确定消息的重要性,并通过将消息打印到当前控制台来确定是否立即将消息提供给用户。
为此,内核将消息的日志级别与 console_loglevel(一个内核变量)进行比较,如果优先级高于console_loglevel(即,其优先级较低),则消息将被打印到当前控制台。例如,如果console_loglevel = 5,将显示日志级别为0到4的所有消息。
请注意,所有日志级别低于或等于KERN_INFO级别的消息都存储在环形缓冲区中。

log级别值说明

日志级别表

名称 含义 别名功能 开发者别名功能
KERN_EMERG "0” 紧急消息,系统即将崩溃或不稳定 pr_emerg dev_emerg
KERN_ALERT "1” 发生了不好的情况,必须立即采取措施 pr_alert dev_alert
KERN_CRIT "2” 发生严重状况,例如严重的硬件/软件故障 pr_crit dev_crit
KERN_ERR "3” 错误状态,驱动程序经常使用该状态来指示硬件问题 pr_err dev_err
KERN_WARNING "4” 警告,本身并不严重,但可能表示问题 pr_warning dev_warn
KERN_NOTICE "5” 没什么严重的,但是值得注意的是。常用于报告安全事件 pr_notice dev_notice
KERN_INFO "6” 信息性消息,例如驱动程序初始化时的启动信息 pr_info dev_info
KERN_DEBUG "7” 调试信息 pr_debug,如果已定义DEBUG,则 pr_devel dev_dbg
重要提示:请注意,高优先级消息为日志级别0

设置控制台的日志级别过滤器值

默认值

要确定目标上当前的console_loglevel,可以使用以下命令进行验证:
Board $> cat /proc/sys/kernel/printk
7       4           1       7
current default_msg minimum default_console
第一个整数显示当前的 console_loglevel;第二个是默认日志级别,请参阅在内核源中使用loglevel进行日志和跟踪。
这是在编译时定义的:
- 通过CONFIG_CONSOLE_LOGLEVEL_DEFAULT = 7的当前控制台日志级别(在文件lib/Kconfig.debug中定义)
- 通过CONFIG_MESSAGE_LOGLEVEL_DEFAULT = 4的默认消息日志级别(在文件lib/Kconfig.debug中定义)
- 最小控制台经由记录等级的#define CONSOLE_LOGLEVEL_MIN 1(在文件中定义的包括 include/linux/printk.h)
- 默认控制台日志级别等于CONFIG_CONSOLE_LOGLEVEL_DEFAULT

使用内核命令行

如果要使用与 CONFIG_CONSOLE_LOGLEVEL_DEFAULT 指定不同的值,可以通过内核命令行参数设置控制台日志级别。
例如:
root=/dev/mmcblk0p5 rootwait rw console=ttySTM0,115200 loglevel=4
在这种情况下,只会在控制台上显示优先级高于KERN_WARNING的消息(意味着 < 4,KERN_EMERG 到 KERN_ERR)。
添加此命令行参数的2种方法是在引导分区的extlinux.conf文件中设置的:
  • 如果使用SD卡,则可以在主机PC上编辑此文件。
1.插入SD卡到PC主机上。
2.检查挂载的boot分区(一般在/media/$USER/bootfs 目录下)
3.检查您的开发板配置(假如使用ev1开发板使用mmc0(SD卡)启动)
PC $> cd /media/$USER/bootfs/mmc0_stm32mp157c-ev1_extlinux/
PC $> gedit extlinux.conf
4.将loglevel=8 添加至行尾。
5.保存文件,并插入SD卡到板子上。


运行时使用sysfs修改等级

要更改当前的console_loglevel,只需写入以下文件:
Board $> echo <loglevel> > /proc/sys/kernel/printk
或使用dmesg命令。
例如:临时增加日志级别以显示消息,直到日志级别8
Board $> echo 8 > /proc/sys/kernel/printk 
在这种情况下,每个内核消息都会显示在您的控制台上,因为所有优先级都将高于8(较低的日志级别值)。

请注意,重新启动后,此配置将被重置。


使用menuconfig修改显示级别

由于值是在编译步骤首先定义的,因此也可以使用Linux内核Menuconfig工具(Menuconfig或如何配置内核)来设置(CONFIG_CONSOLE_LOGLEVEL_DEFAULT和CONFIG_MESSAGE_LOGLEVEL_DEFAULT):
Symbol: CONSOLE_LOGLEVEL_DEFAULT [=7]
Location:
  Kernel hacking --->
    printk and dmesg options --->
      (7) Default console loglevel (1-15)

Symbol: MESSAGE_LOGLEVEL_DEFAULT [=4]
Location:
  Kernel hacking --->
    printk and dmesg options --->
      (4) Default message log level (1-7)

在内核源代码中使用loglevel进行日志和跟踪

使用printk

可以使用以下语法在printk函数调用中添加日志级别信息。
printk(KERN_ERR "something went wrong, return code: %d\n",ret);
默认日志级别值由CONFIG_MESSAGE_LOGLEVEL_DEFAULT给出(通常为"4"=KERN_WARNING)

使用专用函数

在上面的日志级别表中,有一些别名函数pr_和dev_。
定义这些函数是为了替换语法内部的 printk + loglevel 信息。
pr_err("something went wrong, return code: %d\n",ret);
dev_函数采用了另一个参数,以提供有关消息来自的当前设备或驱动程序的更多信息。
  • pr_info示例
pr_info("%s%s%s at %s (irq = %d, base_baud = %d) is a %s\n",
        port->dev ? dev_name(port->dev) : "",
        port->dev ? ": " : "",
        port->name,
        address, port->irq, port->uartclk / 16, uart_type(port));
我们将会看到如下打印信息
[ 0.919488] 40010000.serial: ttySTM0 at MMIO 0x40010000 (irq = 41, base_baud = 6046875) is a stm32-usart
  • dev_info 示例
dev_info(&pdev->dev, "interrupt mode used for rx (no dma)\n");
将显示如下信息,自动包括设备参数:
[    1.046700] stm32-usart 40010000.serial: interrupt mode used for rx (no dma)

Earlyprintk

earlyprintk是Linux内核调试函数,可用于跟踪在初始化常规控制台之前发生的内核问题。
  • Linux内核配置
为了启用Earlyprintk功能,Linux内核配置必须使用Linux内核Menuconfig工具(Menuconfig或如何配置内核)选中 CONFIG_DEBUG_LL,CONFIG_STM32MP1_DEBUG_UART和CONFIG_EARLY_PRINTK 这三个选项:
Symbol: DEBUG_LL
Location:
  Kernel hacking --->
    [*] Kernel low-level debugging functions

Symbol: STM32MP1_DEBUG_UART
Location:
  Kernel hacking --->
    [*] Kernel low-level debugging functions
      (*) Use STM32MP1 UART for low-level debug

Symbol: EARLY_PRINTK
Location:
  Kernel hacking --->
    [*] Early printk
  • 串口配置
启用Linux内核配置CONFIG_STM32MP1_DEBUG_UART时,它将配置要使用的UART寄存器的地址。
默认情况下,在STM32MP1板上,UART4用于Linux内核的控制台,并且在所有引导阶段都用于扩展。
如果新板上的UART端口不同,则必须应用以下更改:
  • 更新CONFIG_DEBUG_UART_PHYS的值,以选择调试控制台的UART端口
Symbol: DEBUG_UART_PHYS [=0x40010000]
Location:
  Kernel hacking --->
    [*] Kernel low-level debugging port (Use STM32MP1 UART4 for low-level debug)
    (0x40010000)  Physical base address of debug UART
  • 更新CONFIG_DEBUG_UART_VIRT的值,定义要使用的虚拟地址。
Symbol: DEBUG_UART_VIRT [=0xFE010000]
Location:
  Kernel hacking --->
    [*] Kernel low-level debugging port (Use STM32MP1 UART4 for low-level debug)
    (0xFE010000)  Virtual base address of debug UART
定义虚拟地址时应遵循以下规则:
-必须保留20个低权重位(在启用LPAE的情况下为21),以对齐1MB的区域大小(在LPAE中启用2MB)。
-必须将其映射到vmalloc区域的高位地址,以免被从低位地址开始声明的内核覆盖:即,这里我们选择0x FE0 xxxxx
CONFIG_DEBUG_UART_PHYS: 0x40010000 /* UART4 */
CONFIG_DEBUG_UART_VIRT: 0xFE010000
  • 请在下表中找到STMP32MP1的USART/UART:


请注意,用于控制台的UART端口必须与引导链的所有组件对齐:FSBL(TF-A),SSBL-U-Boot)和Linux内核

尤其是因为Linux内核没有为UART端口配置所有设置寄存器,因为这是由SSBL完成的(U- Boot- 如何调试),

另请参见TF-A-如何为FSBL更改进行调试)

  • 追踪
Earlyprintk跟踪会自动推送到前面定义的串行控制台,并且还会添加到内核环形日志缓冲区。

dmesg命令

了解更多内容,请参见手册页[6]
可以使用dmesg命令显示内核环形缓冲区。它将在控制台上显示环形缓冲区的所有内容。
  • 按照日志级别过滤消息:
Board $> dmesg -n <loglevel>
在这种情况下,将仅打印值小于(不小于等于)console_loglevel的消息。
在这里,< loglevel >可以是数字值,也可以是字符串:
Supported log levels (priorities):
  emerg (0)
  alert (1)
   crit (2)
    err (3)
   warn (4)
 notice (5)
   info (6)
  debug (7)
如下示例:临时更改日志级别为8以显示调试级别的消息
Board $> dmesg -n 8         # Temporary change loglevel to display messages up to debug level
or
Board $> dmesg -n debug 
在这种情况下,每个内核消息都会显示在您的控制台上,因为所有优先级都将高于8(较低的日志级别值)。
  • 清除dmesg缓冲区
Board $> dmesg -c        # Display the full content of dmesg ring buffer, and then clear it
Board $> dmesg -C        # Clear the dmesg ring buffer

/var/log/messages文件系统条目

显示Linux内核日志内容的另一种方法是查看文件var/log/messages的内容。
它包含启动时的常规系统活动消息。它还提供有关消息来源和日志级别的有用信息。

动态调试消息

这些消息使用的日志级别为7(KERN_DEBUG)。
使用方式,请参阅如何使用内核动态调试文章。