Linux之kernel(20)kdump支持

Linux之kernel(21)kdump介绍

Author:OnceDay Date:2024年1月14日

漫漫长路,有人对你微笑过嘛...

参考文档:

1. 概述
1.1 背景介绍

在业务使用过程中,经常遇到服务器概率性崩溃(准确来说是内核崩溃),重启后一切ok,但是这种隐藏的bug在商业领域是不可接受的,宕机出现即是大问题。因此内核提供了一种保留崩溃现场的机制,保存内核崩溃的现场信息在硬盘等下电不易失的存储介质,方便程序员可以分析崩溃原因。

"内核崩溃转储"是指在内核执行中断时将易失性内存(RAM)的一部分内容复制到磁盘上。以下事件可能导致内核中断停止:

  • 内核恐慌(Kernel panic)。
  • 不可屏蔽中断(NMI, Non-maskable interrupts)。
  • 机器检查异常(MCE, Machine check exceptions)。
  • 硬件故障。
  • 手动干预。

对于其中一些事件(内核恐慌、NMI),内核将自动作出反应,并通过kexec触发崩溃转储机制。在其他情况下,需要手动干预才能捕获内存。每当发生上述事件之一时,找出根本原因以防止其再次发生是很重要的。可以通过检查复制的内存内容来确定原因。

当内核发生panic时,内核依赖于kexec机制,在系统启动时分配的预保留内存部分中快速重新启动内核的新实例。这允许现有的内存区域保持不变,以便安全地将其内容复制到存储中。

注意,在系统无法热启动的情况下,Kdump是不适用的,如硬件的异常导致CPU宕机,也就是只能通过重新关闭开启电源才能启动的情况

1.2 kdump和kexec介绍

kdumpkexec 是 Linux 系统中用于内核崩溃调试和快速重启的两个关键机制。

kexec 是一个允许你在不进行完全硬件重置的情况下引导新内核的机制。kexec 的工作原理可以简化为以下几个步骤:

  1. 加载新内核kexec 工具将新内核及其初始 RAM 磁盘(initrd)加载到当前运行系统的内存中。这涉及到预留内存区域来存放新内核的代码和数据。

  2. 准备启动新内核kexec 设置必要的硬件状态和CPU寄存器,为启动新内核做准备。

  3. 切换内核 :当准备工作完成时,kexec 使用一个特殊的系统调用来关闭当前运行的内核,并跳转到新内核的入口点,从而开始执行新内核。

kexec 跳过了 BIOS 或 UEFI 固件的引导过程,因此可以显著加快重启的速度。然而,由于不进行完全的硬件重置,某些硬件状态可能会被保留,这在某些情况下可能会引起问题。

kdump 是一个基于 kexec 的内核崩溃转储机制。当系统发生内核崩溃(如内核恐慌或严重的硬件错误)时,kdump 允许系统安全地捕获崩溃时的内存镜像,这个镜像可以用于事后分析和调试。kdump 的工作原理通常包括以下步骤:

  1. 预留内存 :系统启动时,kdump 需要预留一部分内存给所谓的 "崩溃内核"(second kernel 或 capture kernel),以确保在主内核崩溃时,这部分内存不被使用且处于干净状态。

  2. 配置崩溃内核kdump 配置使用 kexec 在发生崩溃时自动加载崩溃内核,这通常通过配置文件(如 /etc/kdump.conf)完成。

  3. 内核崩溃时的处理 :当主内核崩溃时,kdump 机制被触发。系统利用 kexec 将控制权传递给预留的崩溃内核。

  4. 捕获转储:崩溃内核启动后,它运行在预留内存中,与主内核的内存空间隔离。它的任务是收集崩溃的主内核的内存映像(vmcore),这通常包括所有的 RAM 内容。

  5. 保存转储文件:崩溃内核将内存转储保存到一个文件系统(通常是硬盘上的特定位置),这样调试者就可以在系统重启后分析这个转储文件。

kdump 的使用对于开发人员和系统管理员来说是非常有价值的,因为它提供了在系统崩溃时的内存状态的完整快照,包括调用栈、寄存器状态、内核变量等,这些信息对于确定崩溃原因至关重要。

1.3 捕获机制介绍

本节参考文档: linux内核调试之 kdump安装配置 - 知乎 (zhihu.com)

kexecLinux内核的一个补丁,可以从当前正在运行的内核直接引导到一个新内核。起初是方便系统可以极其快速地重新启动,后来在crash dump领域得到新的应用。

Kexec包括 2 个组成部分:

  • 内核空间的系统调用 kexec_load,负责在生产内核(production kernelfirst kernel)启动时将捕获内核(capture kernelsencond kernel)加载到指定地址。
  • 用户空间的工具kexec-tools,将捕获内核的地址传递给生产内核,从而在系统崩溃的时候能够找到捕获内核的地址并运行。

当系统崩溃时,kdump使用kexec启动到第二个内核。第二个内核通常叫做捕获内核,以很小内存启动以捕获转储镜像。第一个内核保留了内存的一部分给第二内核启动用。由于kdump利用kexec启动捕获内核,绕过了 BIOS,所以第一个内核的内存得以保留。这是内核崩溃转储的本质。

kdump 需要两个不同目的的内核,生产内核和捕获内核。有两种方式:

  • 构建一个单独的自定义转储捕获内核以捕获内核转储;
  • 或者将系统内核本身作为转储捕获内核,这就不需要构建一个单独的转储捕获内核。(只能用于可支持可重定位内核的体系结构上,目前有 i386,x86_64,ppc64 和 ia64)

在内核崩溃之前所有关于核心映像的必要信息都用ELF 格式编码并存储在保留的内存区域中 。ELF 头所在的物理地址被作为命令行参数(fcorehdr=)传递给新启动的转储内核。

在第二个内核中,"前一个系统的内存"可以通过两种方式访问:

  • 通过/dev/oldmem设备接口,一个捕捉设备可以使用raw原生方式读这个设备文件并写出到文件(根据ELF头信息定位)。
  • 通过/proc/vmcore,这个方式是将转储输出为一个ELF格式的文件,并且可以使用一些文件拷贝命令(比如 cp,scp 等)将信息读出来。

下面是SUSe系统上的捕获执行流程,在不同系统上基本类似,可供参考:

1.4 kdump内核配置

一般内核中配置宏如下,可在.config文件中修改,或者通过make menuconfig修改:

yacas 复制代码
CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
CONFIG_PROC_VMCORE=y
CONFIG_PROC_KCORE=y
CONFIG_SYSFS=y
CONFIG_DEBUG_INFO=y

在实例运行设备中,再通过下面的命令确认内核相关功能是否开启:

yacas 复制代码
ubuntu->~:$ ls /sys/kernel/ | grep kexec
kexec_crash_loaded
kexec_crash_size
kexec_loaded
ubuntu->~:$ ls /proc/ | grep kcore
kcore
root@ubuntu:~# cat /proc/sys/kernel/kexec_load_disabled
0

这里列出来的4个节点必须要有,否则即使后续安装了kdumpkexec工具包,也会执行失败,因为需要内核中先支持相关的功能

其中字段含义如下:

  • kexec_crash_loaded ,查看捕获内核的加载状态0-未加载1-已加载
  • kexec_crash_size,查看捕获内核的大小。
  • kexec_load_disabled,表示kexec_load系统调用是否被禁止,当发生了一次kexec_load后,此值会自动设置为1。
1.5 kdump预留内存

一般有如下的方式:

  • 直接通过size指定预留的大小,offset指定预留内存地址的起始位置。

    yacas 复制代码
    crashkernel=size[KMG][@offset[KMG]]
  • 根据系统的内存大小自动选择预留内存的大小。

    yacas 复制代码
    crashkernel=512M-2G:64M,2G-6G:256M,6G-8G:512M,8G-:768M

    按照实际设备内存的容量进行预留,满足实际所需。

  • 如果系统内存大于4G,则支持在4G以上预留内存,如下两种方式:

    yacas 复制代码
    crashkernel=size[KMG],high
    crashkernel=size[KMG],low

    当指定high参数时,系统会在0~4G和4G以上预留两段内存。默认情况下,x86_64会在4G以下预留256M内存。

    low参数主要是配合high来使用的。如果觉得4G以下默认预留的256M太多了,可以手动指定预留内存。

对于完整的Linux内核系统,一般可以在/etc/default/grub.d/kdump-tools.cfg文件中直接修改配置:

yacas 复制代码
ubuntu->~:$ cat /etc/default/grub.d/kdump-tools.cfg 
GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT crashkernel=384M-:512M"

对于嵌入式设备,则可以直接修改cmdline启动参数:

  • 在dts中中添加:修改chosen。
  • 在BoardConfig中添加。
  • 在uboot中添加:在源码中添加或者通过setenv配置bootargs变量。
  • 在android的Makefile中添加。

可以通过以下命令来确定为kdump预保留内存的大小和地址范围:

yacas 复制代码
ubuntu->~:$ cat /sys/kernel/kexec_crash_size
536870912
ubuntu->~:$ cat /proc/iomem | grep Crash
  00000000-00000000 : Crash kernel
ubuntu->~:$ sudo dmesg | grep -i crash
[    0.006958] Reserving 512MB of memory at 2544MB for crashkernel (System RAM: 8191MB)

/proc/iomem表示的是系统的物理内存布局, System RAM entry表示当前系统可用的预留内存。

评估捕获内核需要的内存大小,可从以下方面出发:

  1. 系统内kernelinitrdromfsdevices driver的大小。
  2. 捕获内核启动cpu的个数。启动cpu越多,需要的内存越大。一般情况下,捕获内核一般启动一个CPU核即可。
1.6 捕获内核信息

可参考文档: 【调试】kdump原理及其使用方法 - 知乎 (zhihu.com)

捕获内核是使用kexec来执行,可以从Linux kernel官网下载专门的源码包: Index of /pub/linux/utils/kernel/kexec/

然后解压、编译、安装即可,需要注意,一般是交叉编译,需要设置合适的编译参数,例如:

yacas 复制代码
./configure ARCH=arm64 --build=x86_64-linux-gnu --host=aarch64-linux-gnu --target=aarch64-linux-gnu --without-xen

该命令工具有几个重要的参数,如下:

  • -d,执行kexec指令时会打印调试信息。
  • -l,将内核加载到预留内存中。
  • -p,加载新内核以便在紧急(panic)情况下使用。
  • --append/command-line,capture内核的command line的内容。
  • --t,内核的类型,比如vmlinux,Image,uImage,zImage。
  • --intrd/ramdisk,指定initrd初始根文件系统镜像。
  • --reuseinitrd,复用第一个内核的initrd。
  • --dtb,指定设备树。

和正常启动的内核一样,kexec也需要配置cmdline,这个参数和第一个内核cmdline参数保持一致即可(可以去掉crashkernel)参数。如下:

yacas 复制代码
ubuntu->~:$ sudo cat /var/crash/kexec_cmd 
/sbin/kexec -p --command-line="BOOT_IMAGE=/boot/vmlinuz-5.15.0-56-generic root=UUID=7bccaefa-b039-4ff6-bd32-22dde0066c0b ro net.ifnames=0 biosdevname=0 console=ttyS0,115200 console=tty0 panic=5 intel_idle.max_cstate=1 intel_pstate=disable processor.max_cstate=1 reset_devices systemd.unit=kdump-tools-dump.service nr_cpus=1 irqpoll nousb ata_piix.prefer_ms_hyperv=0" --initrd=/var/lib/kdump/initrd.img /var/lib/kdump/vmlinuz
ubuntu->~:$ cat /proc/cmdline 
BOOT_IMAGE=/boot/vmlinuz-5.15.0-56-generic root=UUID=7bccaefa-b039-4ff6-bd32-22dde0066c0b ro net.ifnames=0 biosdevname=0 console=ttyS0,115200 console=tty0 panic=5 intel_idle.max_cstate=1 intel_pstate=disable processor.max_cstate=1 crashkernel=2G-16G:512M,16G-:768M
2. ubuntu使用

参考文档: Kernel crash dump | Ubuntu

2.1 安装linux-crashdump

在ubuntu下,可以使用sudo apt install linux-crashdump一键安装所需的软件包,如下:

yacas 复制代码
onceday->~:# sudo apt install linux-crashdump
# kdump-tools kexec-tools 两个包

安装过程中,kexeckdump有两个配置选项,都需要使能,然后kexec会接管内核panic行为,并配合kdump生成内核转储文件。kdump安装要求重启才能生效,主要是添加cmdline参数crashkernel

kexec配置项可以通过下面/etc/default/kexec文件直接修改(包括使能):

yacas 复制代码
ubuntu->~:$ cat /etc/default/kexec
# Defaults for kexec initscript
# sourced by /etc/init.d/kexec and /etc/init.d/kexec-load

# Load a kexec kernel (true/false)
LOAD_KEXEC=false

# Kernel and initrd image
KERNEL_IMAGE="/vmlinuz"
INITRD="/initrd.img"

# If empty, use current /proc/cmdline
APPEND=""

# Load the default kernel from grub config (true/false)
USE_GRUB_CONFIG=false

kdump配置项可以通过/etc/default/kdump-tools文件直接修改:

yacas 复制代码
ubuntu->~:$ cat /etc/default/kdump-tools 
# kdump-tools configuration
# ---------------------------------------------------------------------------
# USE_KDUMP - controls kdump will be configured
#     0 - kdump kernel will not be loaded
#     1 - kdump kernel will be loaded and kdump is configured
#
USE_KDUMP=1


# ---------------------------------------------------------------------------
# Kdump Kernel:
# KDUMP_KERNEL - A full pathname to a kdump kernel.
# KDUMP_INITRD - A full pathname to the kdump initrd (if used).
#     If these are not set, kdump-config will try to use the current kernel
#     and initrd if it is relocatable.  Otherwise, you will need to specify
#     these manually.
KDUMP_KERNEL=/var/lib/kdump/vmlinuz
KDUMP_INITRD=/var/lib/kdump/initrd.img


# ---------------------------------------------------------------------------
# vmcore Handling:
# KDUMP_COREDIR - local path to save the vmcore to.
# KDUMP_FAIL_CMD - This variable can be used to cause a reboot or
#     start a shell if saving the vmcore fails.  If not set, "reboot -f"
#     is the default.
#     Example - start a shell if the vmcore copy fails:
#         KDUMP_FAIL_CMD="echo 'makedumpfile FAILED.'; /bin/bash; reboot -f"
# KDUMP_DUMP_DMESG - This variable controls if the dmesg buffer is dumped.
#     If unset or set to 1, the dmesg buffer is dumped. If set to 0, the dmesg
#     buffer is not dumped.
# KDUMP_NUM_DUMPS - This variable controls how many dump files are kept on
#     the machine to prevent running out of disk space. If set to 0 or unset,
#     the variable is ignored and no dump files are automatically purged.
# KDUMP_COMPRESSION - Compress the dumpfile. No compression is used by default.
#     Supported compressions: bzip2, gzip, lz4, xz
KDUMP_COREDIR="/var/crash"
#KDUMP_FAIL_CMD="reboot -f"
#KDUMP_DUMP_DMESG=
#KDUMP_NUM_DUMPS=
#KDUMP_COMPRESSION=


# ---------------------------------------------------------------------------
# Makedumpfile options:
# MAKEDUMP_ARGS - extra arguments passed to makedumpfile (8).  The default,
#     if unset, is to pass '-c -d 31' telling makedumpfile to use compression
#     and reduce the corefile to in-use kernel pages only.
MAKEDUMP_ARGS="-c -d 31"


# ---------------------------------------------------------------------------
# Kexec/Kdump args
# KDUMP_KEXEC_ARGS - Additional arguments to the kexec command used to load
#     the kdump kernel
#     Example - Use this option on x86 systems with PAE and more than
#     4 gig of memory:
#         KDUMP_KEXEC_ARGS="--elf64-core-headers"
# KDUMP_CMDLINE - The default is to use the contents of /proc/cmdline.
#     Set this variable to override /proc/cmdline.
# KDUMP_CMDLINE_APPEND - Additional arguments to append to the command line
#     for the kdump kernel.  If unset, it defaults to
#     "reset_devices systemd.unit=kdump-tools-dump.service nr_cpus=1 irqpoll nousb ata_piix.prefer_ms_hyperv=0"
#KDUMP_KEXEC_ARGS=""
#KDUMP_CMDLINE=""
#KDUMP_CMDLINE_APPEND="reset_devices systemd.unit=kdump-tools-dump.service nr_cpus=1 irqpoll nousb ata_piix.prefer_ms_hyperv=0"

# ---------------------------------------------------------------------------
# Architecture specific Overrides:

# ---------------------------------------------------------------------------
# Remote dump facilities:
# HOSTTAG - Select if hostname of IP address will be used as a prefix to the
#           timestamped directory when sending files to the remote server.
#           'ip' is the default.
#HOSTTAG="hostname|[ip]"

# NFS -     Hostname and mount point of the NFS server configured to receive
#           the crash dump. The syntax must be {HOSTNAME}:{MOUNTPOINT}
#           (e.g. remote:/var/crash)
# NFS_TIMEO - Timeout before NFS retries a request. See man nfs(5) for details.
# NFS_RETRANS - Number of times NFS client retries a request. See man nfs(5) for details.
#NFS="<nfs mount>"
#NFS_TIMEO="600"
#NFS_RETRANS="3"

# FTP - Hostname and path of the FTP server configured to receive the crash dump.
#       The syntax is {HOSTNAME}[:{PATH}] with PATH defaulting to /.
# FTP_USER - FTP username. A anonomous upload will be used if not set.
# FTP_PASSWORD - password for the FTP user
# FTP_PORT=21 - FTP port. Port 21 will be used by default.
#FTP="<server>:<path>"
#FTP_USER=""
#FTP_PASSWORD=""
#FTP_PORT=21

# SSH - username and hostname of the remote server that will receive the dump
#       and dmesg files.
# SSH_KEY - Full path of the ssh private key to be used to login to the remote
#           server. use kdump-config propagate to send the public key to the
#           remote server
#SSH="<user at server>"
#SSH_KEY="<path>"

安装完可以执行kdump-config查看目前配置:

(1) 内核版本不支持kdump功能,比如一些内核配置缺失

yacas 复制代码
onceday->~:# kdump-config show
 * kdump is not supported by this kernel
 * no crashkernel= parameter in the kernel cmdline
DUMP_MODE:              kdump
USE_KDUMP:              1
KDUMP_COREDIR:          /var/crash
crashkernel addr:
   /var/lib/kdump/vmlinuz
kdump initrd:
   /var/lib/kdump/initrd.img
current state:    Not ready to kdump

kexec command:
  no kexec command recorded

对于这种内核配置不支持的情况,需要重新编译内核,然后替换,再进行后续的测试操作

(2) 内核版本支持kdump功能,可以进行内核转储。

yacas 复制代码
ubuntu->~:$ kdump-config show
DUMP_MODE:              kdump
USE_KDUMP:              1
KDUMP_COREDIR:          /var/crash
crashkernel addr: 0x
   /var/lib/kdump/vmlinuz: symbolic link to /boot/vmlinuz-5.15.0-56-generic
kdump initrd: 
   /var/lib/kdump/initrd.img: symbolic link to /var/lib/kdump/initrd.img-5.15.0-56-generic
current state:    ready to kdump

kexec command:
  no kexec command recorded

如果在安装linux-crashdump包之后没有重新启动,则需要重新启动以激活启动命令中的crashkernel参数。重新启动后,kdump-tools将被启用并激活。如果在重启后启用了kdump- tools,则只需要发出kdumpconfig load命令来激活kdump机制。kdump-config show命令用于查看kdump的当前状态。

2.2 crash文件存储

除了本地转储之外,现在还可以使用远程转储功能,使用SSH或NFS协议将内核崩溃转储发送到远程服务器。本地内核崩溃转储本地转储是自动配置的,并且将保持使用,除非选择了远程协议。

(1) 使用SSH协议的远程内核崩溃转储。

要启用SSH远程转储功能,需要修改/etc/default/kdump-tools文件,具体操作如下:

yacas 复制代码
# ---------------------------------------------------------------------------
# Remote dump facilities:
# SSH - username and hostname of the remote server that will receive the dump
#       and dmesg files.
# SSH_KEY - Full path of the ssh private key to be used to login to the remote
#           server. use kdump-config propagate to send the public key to the
#           remote server
# HOSTTAG - Select if hostname of IP address will be used as a prefix to the
#           timestamped directory when sending files to the remote server.
#           'ip' is the default.
SSH="ubuntu@kdump-netcrash"

唯一需要定义的强制变量是SSH。必须包含远程服务器的用户名和主机名,格式为{username}@{remote server}

SSH_KEY可以用来提供一个现有的要使用的私钥。否则,kdump-config propagate命令将创建一个新的秘钥对。HOSTTAG变量可以用来使用系统的主机名作为要创建的远程目录的前缀,而不是IP地址。

kdump-config show命令用于确认kdump是否正确配置为使用SSH协议:

yacas 复制代码
DUMP_MODE:        kdump
USE_KDUMP:        1
KDUMP_SYSCTL:     kernel.panic_on_oops=1
KDUMP_COREDIR:    /var/crash
crashkernel addr: 0x2c000000
   /var/lib/kdump/vmlinuz: symbolic link to /boot/vmlinuz-4.4.0-10-generic
kdump initrd: 
   /var/lib/kdump/initrd.img: symbolic link to /var/lib/kdump/initrd.img-4.4.0-10-generic
SSH:              ubuntu@kdump-netcrash
SSH_KEY:          /root/.ssh/kdump_id_rsa
HOSTTAG:          ip
current state:    ready to kdump

(2) 使用NFS协议的远程内核崩溃转储

要启用NFS协议远程转储功能,需要修改/etc/default/kdump-tools文件,修改方法如下:

yacas 复制代码
# NFS -     Hostname and mount point of the NFS server configured to receive
#           the crash dump. The syntax must be {HOSTNAME}:{MOUNTPOINT} 
#           (e.g. remote:/var/crash)
#
NFS="kdump-netcrash:/var/crash"

与SSH协议一样,可以使用HOSTTAG变量将IP地址替换为主机名,作为远程目录的前缀。kdump-config show命令用于确kdump是否正确配置为使用NFS协议。

2.3 crash dump确认

要确认是否启用了内核转储机制,需要验证几件事。首先,确认存在crashkernel引导参数:

yacas 复制代码
ubuntu->~:$ cat /proc/cmdline

BOOT_IMAGE=/boot/vmlinuz-5.15.0-56-generic root=UUID=7bccaefa-b039-4ff6-bd32-22dde0066c0b ro net.ifnames=0 biosdevname=0 console=ttyS0,115200 console=tty0 panic=5 intel_idle.max_cstate=1 intel_pstate=disable processor.max_cstate=1 crashkernel=2G-16G:512M,16G-:768M

crashkernel参数的语法如下:

yacas 复制代码
crashkernel=<range1>:<size1>[,<range2>:<size2>,...][@offset]
    range=start-[end] 'start' is inclusive and 'end' is exclusive.

因此,对于在/proc/cmdline中找到的crashkernel参数,我们将有:

yacas 复制代码
crashkernel=2G-16G:512M,16G-:768M

其含义为:

  • 如果设备内存小于2G,将不会保留任何内存。
  • 如果设备内存在2G~16G之间,保留512M内存。
  • 如果设备内存大于16G,保留768M内存。

第二,验证内核已经为kdump内核保留了请求的内存区域,运行:

yacas 复制代码
dmesg | grep -i crash

下面是一个功能正常开启的日志输出:

yacas 复制代码
[    0.006827] Reserving 512MB of memory at 2384MB for crashkernel (System RAM: 8191MB)

最后,kdump-config show命令显示kdump-tools配置的当前状态。

2.4 测试crash dump机制

测试崩溃转储机制将导致系统重新启动。在某些情况下,如果系统负载过重,可能会导致数据丢失。如果要测试该机制,请确保系统处于空闲状态或负载非常轻

通过查看/proc/sys/kernel/sysrq内核参数的值来验证SysRQ机制是否启用:

yacas 复制代码
ubuntu->~:$ cat /proc/sys/kernel/sysrq
1

如果返回值为0,则表示禁用"先转储后重启"特性。大于1表示开启了sysrq特性的一个子集。请参见/etc/sysctl.d/10-magic-sysrq.conf查看这些选项的详细描述及其默认值。使用以下命令启用转储然后重新启动测试:

yacas 复制代码
sudo sysctl -w kernel.sysrq=1

完成此操作后,您必须成为root用户,因为仅使用sudo是不够的。作为根用户,必须发出命令:

yacas 复制代码
echo c > /proc/sysrq-trigger

如果正在使用网络连接,将会与系统失去联系。这就是为什么最好在连接到系统控制台时进行测试。这样做的好处是可以看到内核转储进程

典型的测试输出应该如下所示:

yacas 复制代码
sudo -s
[sudo] password for ubuntu: 
# echo c > /proc/sysrq-trigger
[   31.659002] SysRq : Trigger a crash
[   31.659749] BUG: unable to handle kernel NULL pointer dereference at           (null)
[   31.662668] IP: [<ffffffff8139f166>] sysrq_handle_crash+0x16/0x20
[   31.662668] PGD 3bfb9067 PUD 368a7067 PMD 0 
[   31.662668] Oops: 0002 [#1] SMP 
[   31.662668] CPU 1 
....

输出的其余部分被截断,但应该看到系统重新启动,并且在日志的某个地方,您将看到以下行:

yacas 复制代码
Begin: Saving vmcore from kernel crash ...

一旦完成,系统将重新启动到正常的操作模式。然后会在/var/crash目录下找到内核崩溃转储文件和相关的子目录,例如:

yacas 复制代码
ubuntu->~:$ ll /var/crash/
drwxr-xr-x  2 root root  4096 Jan 14 23:17 202401142317/
-rw-r--r--  1 root root     0 Jan 14 23:18 kdump_lock
-rw-r--r--  1 root root   425 Jan 14 23:18 kexec_cmd
-rw-r-----  1 root root 33366 Jan 14 23:18 linux-image-5.15.0-56-generic-202401142317.crash

如果转储由于内存不足 (OOM)错误而无法工作,则尝试通过编辑/etc/default/grub.d/kdump-tools.cfg来增加保留的内存量。

yacas 复制代码
GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT crashkernel=384M-:512M"

然后可以运行sudo update-grub,然后重新启动,然后再次测试。

只有系统panic,才会触发kdump。在实际使用情况经常遇到系统挂死或者OOM,重启后无kdump日志的情况,这是因为系统hung和oom不会触发panic,因此系统无法进入第二内核存储日志信息

这种情况可以配置异常后系统panic,从而达到触发kdump目的:

  • 比如配置hungtaskpanicecho 1 > /proc/sys/kernel/hung_task_panic
2.5 解码VmCoreDmesg信息

从你提供的信息来看,VmCoreDmesg字段似乎包含了经过base64编码的压缩数据。为了解码和解压这些数据,你需要先进行base64解码,然后解压缩。在Ubuntu系统上,你可以使用base64zcatgzip工具来完成此任务。

以下是你可以在终端中执行的步骤:

  1. 保存Base64数据到文件 : 首先,你需要将那串Base64编码的数据保存到一个文件中,假设我们将其保存为vmcore_dmesg.base64

  2. Base64解码 : 然后,你可以使用base64命令对文件进行解码。

    yacas 复制代码
    base64 --decode vmcore_dmesg.base64 > vmcore_dmesg.gz

    如果在尝试解码base64字符串时收到"无效输入"的错误,通常意味着输入包含了不属于base64编码方案的字符,或者字符串以其他方式损坏或不完整。重要的是确保base64字符串完整无误,没有包含任何不是编码数据一部分的额外字符或换行符。

    确保没有不小心包含的空格、换行符或其他空白字符在你尝试解码的base64数据中。

    如果base64数据已经分割成多行了,你可能需要移除这些换行符。你可以使用tr命令来移除所有的换行:

    yacas 复制代码
    cat vmcore_dmesg.base64 | tr -d '\n' | base64 --decode > vmcore_dmesg.gz

    确保base64数据是完整的。一个base64编码的字符串的长度应该是4的倍数。如果不是,可能在结尾缺少填充字符(=)。

    所有空格和换行符都需要去掉,然后长度为4的整数倍,且需要完整的gzip压缩格式,否则都会认为存在错误。

    yacas 复制代码
    ubuntu->crash:$ cat vmcore_dmesg.base64 | tr -d '\n' |base64 --decode > vmcoredmesg.gz
    ubuntu->crash:$ file vmcoredmesg.gz 
    vmcoredmesg.gz: gzip compressed data, was "VmCoreDmesg", max compression, original size modulo 2^32 108136
  3. 解压缩 : 接下来,使用gzipzcat来解压缩解码后的数据。

    yacas 复制代码
    ubuntu->crash:$ gunzip vmcoredmesg.gz 
    ubuntu->crash:$ cat vmcoredmesg 
    [    0.000000] Linux version 5.15.0-56-generic (buildd@lcy02-amd64-004) (gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 (Ubuntu 5.15.0-56.62-generic 5.15.64)
    ......

请注意,如果数据是通过特定的压缩方法(不是gzip)压缩的,你可能需要使用相应的工具来解压。如果gzip -dzcat无法正确解压数据,你可能需要尝试其他解压工具,比如bzip2, xz, 或unzip,取决于数据实际上是如何被压缩的。

在上述命令中,如果你遇到权限问题,你可能需要在命令前添加sudo来获取必要的权限。此外,请确保处理的数据是可信的,因为解码和解压缩来自不明确来源的数据存在安全风险。

3. Crash分析dump文件

Crash是一个用于分析内核转储文件的工具,和kdump配套使用。kdump会在内存中保留一块区域,这个区域用于存放捕获内核,生产内核运行过程中崩溃时,kdump通过kexec机制自动启动捕获内核,对生产内核的完整信息(CPU寄存器、栈帧数据等)进行保存,生产内核重启后,使用Crash工具来分析这个转储的文件。

3.1 Crash和GDB关系

crashGDB(GNU Debugger)都是用于调试程序和系统的工具,但它们的设计目标、功能和使用场景有所不同。

GDB 是一个功能强大的调试工具,广泛用于调试C和C++编写的用户空间应用程序。它允许程序员在程序执行中暂停、检查和修改内存内容、寄存器状态、执行步骤跟踪和反汇编等。GDB 提供了一个交互式环境,可以让开发者在编写代码时或程序崩溃后查找错误。

GDB 也可以用于内核调试,尤其是与 KGDB(内核内置的调试工具)结合使用时。但是,对于直接分析内核转储文件(如 kdump 生成的 vmcore 文件),GDB 并不是最直接的工具,因为它主要是针对用户空间调试设计的。

crash 是一个专门设计用来分析 Linux 内核崩溃转储文件(vmcore)和实时系统的工具。它基于 GDB 的技术,但提供了针对内核数据结构的特定命令和功能。crash 可以用来执行许多内核相关的分析任务,如查看崩溃时的内存、CPU寄存器状态、进程列表、内核日志、调用栈、加载的模块等。

crash 在内部使用了 GDB 的一些技术。事实上,在 crash 的早期版本中,它实际上是作为一个 GDB 的扩展来运行的。它继承了 GDB 的很多功能,并扩展了这些功能以适应内核转储分析的特殊需求。crash 的用户界面和 GDB 类似,因此熟悉 GDB 的用户可以更容易地学会使用 crash

总结如下:

  • GDB 是一个通用调试工具,主要用于用户空间程序的调试。
  • crash 是一个专门的工具,用于分析 Linux 内核转储和实时系统,它基于 GDB 的技术,提供了许多针对内核分析的特定命令。
  • crash 的用户界面与 GDB 类似,但增加了对内核数据结构的直接支持和额外的功能。
3.2 获取内核符号

ubuntu自带的系统安装包里是没有vmlinux文件的,自行编译需要拿到ubuntu的内核配置,自行解压vmlinuz比较麻烦,因为它不是标准的压缩文件,需要提取里面压缩部分。

最简单的方法是从开发仓库(ddebs repository)中下载:

yacas 复制代码
echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com $(lsb_release -cs)-security main restricted universe multiverse
deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \
sudo tee -a /etc/apt/sources.list.d/ddebs.list
 
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 428D7C01

然后下载并安装内核调试符号:

yacas 复制代码
sudo apt-get update
sudo apt-get install linux-image-$(uname -r)-dbgsym

最终可以在下面目录中找到对应文件:

yacas 复制代码
ubuntu->~:$ file /usr/lib/debug/boot/vmlinux-5.15.0-72-generic 
/usr/lib/debug/boot/vmlinux-5.15.0-72-generic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=693d0776f1d4f10dcc27195473221a0639a01a1c, with debug_info, not stripped

然后使用crash加载内核和dump文件:

yacas 复制代码
ubuntu->~:$ crash /usr/lib/debug/boot/vmlinux-5.15.0-72-generic /var/crash/202401142317/dump.202401142317 

crash 8.0.0
Copyright (C) 2002-2021  Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010  IBM Corporation
Copyright (C) 1999-2006  Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012  Fujitsu Limited
Copyright (C) 2006, 2007  VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011, 2020-2021  NEC Corporation
Copyright (C) 1999, 2002, 2007  Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002  Mission Critical Linux, Inc.
Copyright (C) 2015, 2021  VMware, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions.  Enter "help copying" to see the conditions.
This program has absolutely no warranty.  Enter "help warranty" for details.
 
GNU gdb (GDB) 10.2                             
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...

WARNING: kernel version inconsistency between vmlinux and dumpfile

crash: page excluded: kernel virtual address: ffffffff9e482bc0  type: "current_task (per_cpu)"
......

可以看到,因为使用的内核版本不一致,导致无法正常读取dump文件,并且直接报错退出。

附录:
附录1. 内核调试器(KGDB)

KGDB(Kernel GNU Debugger)是内核级的调试器,它允许开发者像调试普通的C程序一样调试Linux内核。KGDB使用GDB作为其前端,以提供熟悉的用户界面和强大的调试工具集。KGDB的主要作用和特点包括:

  1. 实时调试:

    KGDB允许开发者在内核执行时实时调试内核代码。这意味着你可以在内核运行时暂停它,在内核代码中单步执行,设置断点,检查变量等。

  2. 错误定位:

    KGDB非常适合用于定位那些难以复现或需要实时分析的内核错误。使用KGDB,开发者可以更容易地查看崩溃时的内核状态,包括调用栈、寄存器内容和内存状态。

  3. 内核分析:

    调试器可以检查内核内部的数据结构,理解内核行为,验证代码更改对内核状态的影响。

  4. 远程调试:

    KGDB通过串行连接或以太网连接支持远程调试,这意味着开发者可以在一台机器上运行内核,而在另一台机器上进行调试。

为了使用KGDB,内核需要用相关选项编译以支持KGDB。此外,通常需要两台计算机或虚拟机------一台作为正在运行和被调试的内核的主机,另一台作为运行GDB的调试主机。一旦设置好并启动,调试主机可以通过GDB连接到被调试的主机,并像调试用户空间程序一样进行内核调试。

KGDB对内核开发者来说是一个宝贵的工具,因为它提供了深入内核并直接与其交云的能力,这在其他调试手段都失败的时候尤其有用。然而,由于它停止了整个系统的运行,KGDB主要用于开发环境,而不适用于生产环境。在生产环境中,通常会使用如kdump、crash和其他日志工具来收集和分析系统崩溃的信息。

附录2. 安装内核调试符号包

在Ubuntu上获取内核镜像的符号版本,通常是指获取内核的调试符号,这对于调试内核或者分析核心转储文件是必要的。符号版本的内核包含了额外的调试信息,可以在出现问题时为开发者提供更多的上下文信息。

获取内核符号文件的步骤如下:

  1. 安装内核调试符号包: Ubuntu提供了与内核关联的调试符号包。这些包通常命名为 linux-image-$(uname -r)-dbgsym,其中 $(uname -r) 是你当前运行内核的版本号。为了获取这些包,你需要启用具有调试符号的存储库。这可以通过添加Ubuntu的Debugging Symbol Archive进行:

    sh 复制代码
    echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list
    echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list
    echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list
    echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list

    然后导入密钥并更新软件包列表:

    yacas 复制代码
    sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C8CAB6595FDFF622
    sudo apt-get update
  2. 安装内核调试符号包: 使用apt获取相应的调试符号包:

    yacas 复制代码
    sudo apt-get install linux-image-$(uname -r)-dbgsym

    如果你的系统提示找不到相关包,可能是因为你的内核版本特定,或者是你的系统架构不支持(比如说,使用了一个非官方的内核)。在这种情况下,你可能需要手动下载或者编译带有调试信息的内核。

  3. 使用调试符号: 一旦调试符号包被安装,工具如 gdb, crash 等就可以使用这些符号来提供更多的调试信息。

请注意,调试符号包通常很大,因为它们包含了大量的额外信息。只有在需要进行内核调试或分析的时候才需要安装它们。对于生产环境,一般不建议安装调试符号,因为它会占用大量的磁盘空间,并且增加了系统的攻击面。

如果尝试使用apt更新时遇到了GPG错误。这是因为APT无法验证由http://ddebs.ubuntu.com提供的软件包的签名,因为它没有相应的公钥。为了解决这个问题,你需要将缺失的GPG公钥添加到你的系统里

可以使用以下命令将其添加到系统中:

yacas 复制代码
wget -qO - http://ddebs.ubuntu.com/dbgsym-release-key.asc | sudo apt-key add -

这条命令做了以下几件事情:

  1. 使用wget从ddebs.ubuntu.com下载公钥文件,并且通过-qO -选项将下载的内容直接输出到标准输出。
  2. 管道操作符|将wget命令的输出传递给sudo apt-key add -命令,后者将公钥添加到APT的可信公钥列表中。

执行这个命令后,APT应该能够验证ddebs.ubuntu.com提供的软件包签名了。完成这步操作后,可以重新运行sudo apt update来更新你的软件包列表。

请注意,从Ubuntu 20.04版本开始,使用apt-key命令直接添加密钥已经被弃用,取而代之的是将密钥文件放在/etc/apt/trusted.gpg.d/目录下。如果系统是Ubuntu 20.04或更高版本,应当使用如下方法:

yacas 复制代码
wget -qO - http://ddebs.ubuntu.com/dbgsym-release-key.asc | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/dbgsym-archive-keyring.gpg

这个命令下载公钥,并且通过gpg --dearmor命令处理ASCII-armor格式的公钥文件,生成适用于apt.gpg格式的密钥文件,并将其存储在/etc/apt/trusted.gpg.d/目录。

因为它没有相应的公钥。为了解决这个问题,你需要将缺失的GPG公钥添加到你的系统里**。

可以使用以下命令将其添加到系统中:

yacas 复制代码
wget -qO - http://ddebs.ubuntu.com/dbgsym-release-key.asc | sudo apt-key add -
相关推荐
ChoSeitaku12 分钟前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
DdddJMs__13518 分钟前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
娃娃丢没有坏心思1 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++
keep__go1 小时前
Linux 批量配置互信
linux·运维·服务器·数据库·shell
矛取矛求1 小时前
Linux中给普通账户一次性提权
linux·运维·服务器
Fanstay9851 小时前
在Linux中使用Nginx和Docker进行项目部署
linux·nginx·docker
大熊程序猿1 小时前
ubuntu 安装kafka-eagle
linux·ubuntu·kafka
ahadee2 小时前
蓝桥杯每日真题 - 第11天
c语言·vscode·算法·蓝桥杯
No0d1es3 小时前
2024年9月青少年软件编程(C语言/C++)等级考试试卷(九级)
c语言·数据结构·c++·算法·青少年编程·电子学会