一、背景说明

1、问题背景

近期公司一台线上服务器系统出现崩溃,已经配置了kdump,但未生成相关的crash文件。服务器配置:
浪潮SA5212M5,Gold5118*2 内存配置为128GB.

[root@test-99-230 log]# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="CentOS Linux"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=169M biosdevname=0 net.ifnames=0 rhgb quiet"
GRUB_DISABLE_RECOVERY="true"
GRUB_GFXPAYLOAD_LINUX=text
[root@test-99-230 log]#


[root@test-99-230 log]# grep -rn crash /var/log/messages
3410:Aug  5 05:43:58 test-99-230 kernel: Command line: BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=169M biosdevname=0 net.ifnames=0 rhgb quiet
3529:Aug  5 05:43:58 test-99-230 kernel: Reserving 169MB of memory at 576MB for crashkernel (System RAM: 130563MB)
3814:Aug  5 05:43:58 test-99-230 kernel: Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=169M biosdevname=0 net.ifnames=0 rhgb quiet
4269:Aug  5 05:43:58 test-99-230 kernel: crash memory driver: version 1.1
4552:Aug  5 05:43:59 test-99-230 kernel: megaraid_sas 0000:5e:00.0: firmware crash dump#011: no
5951:Aug  5 06:18:47 test-99-230 kernel: Command line: BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=169M biosdevname=0 net.ifnames=0 rhgb quiet
6070:Aug  5 06:18:47 test-99-230 kernel: Reserving 169MB of memory at 576MB for crashkernel (System RAM: 130563MB)
6355:Aug  5 06:18:47 test-99-230 kernel: Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=169M biosdevname=0 net.ifnames=0 rhgb quiet
6810:Aug  5 06:18:47 test-99-230 kernel: crash memory driver: version 1.1

crashkernel=169M中可以看出,配置的第二内核内存是169MB,通过不断模拟触发crash场景,最终定位本次未产生crash文件根因为

第二内核预留的内核较小,导致第二内核在运行期间加载驱动时内存不够使用发生oom(内存溢出)现象
因为OS本身已经使能kdump,所以在内核发生崩溃后会启动crashdump服务,即会加载第二内核。
但是第二内核在启动过程中因为前期为其crashkernel预留的内存不够,在加载驱动时发生oom(内存溢出),因此vmcore生成失败。

二、排查思路

1、先了解下crashkernel

在Linux操作系统中,kdump是一个用于捕获系统崩溃转储(crash dump)的机制。为了使kdump能够正常工作,系统需要预留一部分内存,这部分内存称为crashkernel。
crashkernel参数用于指定在系统启动时预留多少内存用于崩溃转储。这些预留的内存不会被操作系统的其他部分使用,确保在系统崩溃时,kdump有足够的资源来保存内存转储。

以下是crashkernel参数的几种常见设置方式及其示例:

  1. 自动预留内存 (crashkernel=auto)

    crashkernel=auto
    

    这种设置允许系统根据总内存自动配置预留的内存量。这是最简单的一种设置方式,适用于大多数用户。

  2. 固定预留内存 (crashkernel=<size>)

    crashkernel=128M
    

    这种方式明确指定预留内存的大小,例如128MB。这种设置方式适用于已知需要多少内存用于kdump的情况。

  3. 可变预留内存 (crashkernel=<range1>:<size1>,<range2>:<size2>)

    crashkernel=512M-2G:64M,2G-:128M
    

    这种设置方式根据系统的总内存来预留不同大小的内存。例如,当系统总内存小于512MB时,不预留内存;在512MB到2GB之间时,预留64MB;大于2GB时,预留128MB。

  4. 偏移预留内存 (crashkernel=<size>@<offset>)

    crashkernel=128M@16M
    

    这种方式从指定的偏移地址(例如16MB)开始预留指定大小的内存(例如128MB)。如果offset参数设置为0或完全省略,kdump会自动偏移预留内存。对于一般用户来说,这种方式通常不指定offset,因为很难确定预留内存的起始位置。

  5. 可变预留+偏移预留内存 (crashkernel=<range1>:<size1>,<range2>:<size2>@<offset>)

    crashkernel=512M-2G:64M,2G-:128M@16M
    

    这种方式结合了可变预留和偏移预留的特点。例如,从16MB开始预留内存,根据系统总内存的不同,预留64MB或128MB。

crashkernel设置方式示例
设置方式示例示例含义
自动预留crashkernel=auto允许基于系统的总内存自动地配置预留内存,
确切的值crashkernel=128M预留128M内存
可变预留crashkernel=512M-2G:64M,2G-:128M系统总内存小于512M时,不预留;在512M-2G时,预留64M;大于2G预留128M
偏移预留crashkernel=128M@16M从16 MB开始预留128 MB内存。如果offset参数设置为0或完全省略,kdump会自动偏移预留内存。不过,offset一般不指定,对于一般用户来讲,很难确定预留内存的起始位置。
可变预留+偏移预留crashkernel=512M-2G:64M,2G-:128M@16M从16MB开始预留内存,预留64M或者128M,取决于系统总内存的大小。
如何配置crashkernel

配置crashkernel参数需要修改引导加载程序(如GRUB)的配置文件。以下是配置步骤:

  1. 打开GRUB配置文件:

    sudo vi /etc/default/grub
    
  2. 找到GRUB_CMDLINE_LINUX这一行,并添加crashkernel参数。例如:

    GRUB_CMDLINE_LINUX="crashkernel=512M-2G:64M,2G-:128M"
    
  3. 更新GRUB配置:

    sudo grub2-mkconfig -o /boot/grub2/grub.cfg
    
  4. 重启系统使配置生效:

    sudo reboot
    
验证crashkernel配置

重启系统后,可以通过以下命令验证crashkernel配置是否生效:

dmesg | grep -i crashkernel

2、再了解下kdump

Kdump 是 Linux 系统中一种强大的内核崩溃转储工具,它的出现大大增强了系统管理员对系统崩溃的诊断能力。自 2005 年首次推出以来,Kdump 已成为许多 Linux 系统中不可或缺的组件,帮助用户在系统崩溃后获取重要的内存转储信息。本文将详细介绍 Kdump 的工作原理、实现机制、以及如何有效地使用它来排查系统崩溃问题。

2.1 Kdump 的基本概念
2.1.1. 生产内核(Production Kernel)

生产内核是系统正常运行时的内核,它负责处理所有的系统调用、驱动程序和用户空间的请求。在正常情况下,生产内核负责维护系统的稳定性和性能。

2.1.2. 捕获内核(Capture Kernel)

捕获内核是一个特殊的内核,在系统崩溃时启动,用于收集生产内核的内存数据。捕获内核在生产内核崩溃后被引导,以便在不干扰生产内核的情况下保存内存转储数据。

2.1.3. Ramdisk

Ramdisk 是一种将内存区域虚拟化为硬盘驱动器的机制。通过使用 ramdisk,可以大幅提高读写速度,因为数据直接存储在内存中,而不需要经过传统的磁盘 I/O 操作。在 Kdump 中,ramdisk 用于存储捕获内核和相关的数据。

2.1.4. ELF 文件

ELF(Executable and Linkable Format)文件是一种标准的文件格式,用于存储可执行文件、目标代码和共享库。在 Kdump 中,ELF 文件用于存储内存的转储数据,以便进行后续分析。

2.2 Kdump 的工作原理

Kdump 的工作原理依赖于 Kexec 机制。Kdump 的核心思想是预留一部分内存来加载捕获内核,并在系统崩溃时启动捕获内核来处理内存转储。以下是 Kdump 的详细工作流程:
在这里插入图片描述

2.2.1. Kexec 机制

Kexec 是一种快速启动机制,它允许在当前运行的内核环境下直接启动另一个内核,而无需经过传统的 BIOS 启动过程。Kexec 的实现包括两个主要组成部分:

  • 内核空间系统调用:kexec_load():这是一个系统调用,用于将捕获内核和其 ramdisk 加载到当前内核中。kexec_load() 接受捕获内核的地址,并将其加载到生产内核中。
  • 用户空间工具:kexec-tools:这是一个用户空间工具集,用于执行 Kexec 相关操作。kexec-tools 包含 kexec 程序,它负责调用 kexec_load() 并传递捕获内核的地址。
2.2.2. 内核空间:kexec_load()

在内核空间,kexec_load() 系统调用用于将捕获内核和 ramdisk 加载到内存中。该调用将捕获内核的内存映像和 ramdisk 存储位置传递给生产内核。生产内核在运行过程中保留一部分内存用于加载捕获内核。

当系统崩溃时,生产内核会调用 machine_kexec() 函数,这通常是一个硬件相关的函数,用于引导捕获内核。捕获内核会从生产内核的内存中读取崩溃数据,并生成 /proc/vmcore 文件,该文件包含生产内核的内存映像。

2.2.3. 用户空间:kexec-tools

在用户空间,kexec-tools 工具集用于准备和加载捕获内核。kexec-tools 包含以下组件:

  • kexec:这是一个用户空间程序,负责调用 kexec_load() 并将捕获内核和 ramdisk 加载到生产内核中。kexec 还负责配置捕获内核的启动参数和内存位置。
  • purgatory:这是一个小型的用户空间程序,用于处理在崩溃时捕获内核的启动。它负责将捕获内核和 ramdisk 加载到内存中,并传递必要的启动信息。
2.3 Kdump 的工作流程

Kdump 的工作流程包括以下几个步骤:
在这里插入图片描述

2.3.1. 预留内存

在系统启动时,crashkernel 参数用于指定用于捕获内核的内存量。例如,crashkernel=128M 表示预留 128MB 的内存给捕获内核。这个预留内存区域必须在系统启动时配置,以确保系统在崩溃时能够启动捕获内核。

2.3.2. 生产内核运行

生产内核正常运行,处理所有的系统调用和用户请求。生产内核会定期将捕获内核和 ramdisk 加载到预留内存中,并配置捕获内核的启动参数。

2.3.3. 系统崩溃

当系统出现崩溃、死锁或死机时,生产内核会触发 machine_kexec() 函数。这会启动捕获内核,并将生产内核的内存映像转储到 /proc/vmcore 文件中。

2.3.4. 捕获内核启动

捕获内核会从生产内核的内存中读取崩溃数据,并生成 /proc/vmcore 文件。捕获内核和 ramdisk 组成一个微环境,用于处理和存储内存转储数据。

2.3.5. 数据转储

捕获内核的 ramdisk 中的脚本会执行数据转储操作,将 /proc/vmcore 文件中的数据保存到磁盘或网络存储中。这些数据可以用于后续的分析和调试。

2.3.6. 数据分析

使用工具如 gdbcrash 等分析工具对生成的 vmcore 文件进行分析。这些工具可以帮助用户定位系统崩溃的原因,并提供详细的崩溃报告。在系统发生崩溃时,vmcore 文件是分析和诊断问题的关键。这个文件包含了崩溃时的内存映像,提供了有关内核状态的重要信息。使用 gdbcrash 等工具,我们可以深入分析 vmcore 文件,找出系统崩溃的原因.

1. GDB

GDB(GNU Debugger)是一种强大的调试工具,广泛用于调试应用程序和内核。它支持查看内存、设置断点、执行代码等操作,适用于深入分析和诊断内核崩溃问题。

2. Crash

crash 是专门用于分析内核崩溃转储的工具,它提供了多种命令,用于查看内核数据结构、内存内容、线程状态等信息。相比于 GDB,crash 更加专注于内核级别的调试和分析。

使用 gdb 分析 vmcore 文件
1. 准备工作

首先,确保你已经安装了 gdb 和适用于你内核版本的调试符号(通常是内核的调试符号包)。将 vmcore 文件和内核映像(vmlinux 文件)放在同一目录下。

gdb /path/to/vmlinux /path/to/vmcore
2. 分析内存内容

使用 GDB,可以查看内存内容、内核数据结构等。以下是一些常用的命令:

  • 查看内存

    (gdb) x/10x 0xc0000000
    

    这个命令可以查看从地址 0xc0000000 开始的 10 个内存单元的内容。

  • 查看内核符号

    (gdb) info symbol 0xc0000000
    

    这个命令可以查看指定地址的符号信息。

  • 设置断点并运行

    (gdb) break function_name
    (gdb) continue
    

    设置断点并继续执行,用于调试内核崩溃时的代码执行路径。

3. 分析崩溃原因
  • 查看内核调用栈

    (gdb) bt
    

    打印调用栈信息,帮助了解崩溃发生时的调用路径。

  • 检查特定的数据结构

    (gdb) p data_structure
    

    打印特定数据结构的内容,帮助检查是否存在异常。

使用 crash 分析 vmcore 文件
1. 准备工作

确保你已经安装了 crash 工具,并且已经下载了与内核版本匹配的调试符号。

crash /path/to/vmlinux /path/to/vmcore
2. 使用 crash 命令

crash 提供了多种命令来分析内核崩溃数据:

  • 查看系统信息

    crash> sysinfo
    

    显示系统信息,包括内核版本、CPU 信息和内存布局。

  • 查看内核线程列表

    crash> ps
    

    列出系统中所有的内核线程,帮助识别崩溃时的活动线程。

  • 查看内核堆栈

    crash> bt
    

    打印内核调用栈,类似于 GDB 中的 bt 命令。

  • 查看特定进程的信息

    crash> proc
    

    显示系统中的进程信息,包括每个进程的状态和内存使用情况。

  • 查看内存内容

    crash> x /10x 0xc0000000
    

    显示从地址 0xc0000000 开始的 10 个内存单元的内容。

3. 经典场景分析案例
案例 1:内核崩溃由于内存损坏

假设在分析 vmcore 文件时发现系统因为内存损坏导致崩溃。以下是分析步骤:

  1. 查看内存内容

    crash> x /20x 0xc0000000
    

    检查崩溃时的内存内容,寻找异常值。

  2. 检查调用栈

    crash> bt
    

    确认崩溃发生时的调用路径,查找可能导致内存损坏的代码。

  3. 查看特定数据结构

    crash> p memory_structure
    

    检查相关内存数据结构的内容,确认是否有损坏或异常情况。

案例 2:内核崩溃由于驱动程序问题

假设系统崩溃由于一个驱动程序引发的错误。以下是分析步骤:

  1. 查看系统信息

    crash> sysinfo
    

    确认系统和内核版本,确保与驱动程序版本匹配。

  2. 查看内核调用栈

    crash> bt
    

    分析调用栈信息,找到崩溃时涉及的驱动程序代码。

  3. 查看进程状态

    crash> ps
    

    查找崩溃时活跃的进程,确认是否涉及问题驱动程序。

案例 3:内核崩溃由于系统资源耗尽

假设系统崩溃由于资源耗尽。以下是分析步骤:

  1. 查看系统资源使用情况

    crash> sysinfo
    

    检查系统的内存和 CPU 使用情况,确认是否存在资源耗尽的情况。

  2. 查看内核线程

    crash> ps
    

    查找系统崩溃时活跃的线程,确认是否有线程占用过多资源。

  3. 检查内存分配

    crash> kmem -i
    

    查看内核内存分配情况,确认是否存在资源泄漏或异常分配。

2.4 Kdump 的应用场景

假设你在生产环境中运行一个关键应用,系统突然出现无响应的情况,没有明显的日志记录,且监控数据看起来正常。这时,Kdump 可以发挥重要作用:

  1. 配置 Kdump:首先,确保系统已配置 Kdump,并且 crashkernel 参数已正确设置。例如,crashkernel=128M 表示为捕获内核预留了 128MB 的内存。

  2. 触发崩溃:可以通过模拟崩溃(例如使用 sysrq 触发 panic)来测试 Kdump 是否能够正确捕获和转储内存。

  3. 分析崩溃数据:崩溃后,捕获内核会生成 /proc/vmcore 文件。使用 crash 工具分析 vmcore 文件,找出导致崩溃的原因。

  4. 故障排查:通过分析内存转储数据,可以找到崩溃的根本原因,比如某个驱动程序或内核模块的问题,并进行修复。

Kdump 的优势
  • 实时数据捕获:Kdump 能够在系统崩溃时实时捕获内存数据,提供详细的崩溃信息。
  • 高效转储:通过使用 ramdisk,可以大幅提高数据转储的速度。
  • 详细分析:生成的 vmcore 文件可以通过 gdbcrash 等工具进行详细的分析,帮助开发者和管理员找到系统崩溃的原因。

三、解决方案

1. 方案一调整crashkernel(需重启系统)

通过上面分析,结合日志:

32489:Aug  5 19:51:35 dlp2-99-230 kernel: megaraid_sas 0000:5e:00.0: firmware crash dump#011: no
33775:Aug  5 20:00:45 dlp2-99-230 kernel: Command line: BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=768M biosdevname=0 net.ifnames=0 rhgb quiet
33894:Aug  5 20:00:45 dlp2-99-230 kernel: Reserving 256MB of low memory at 1264MB for crashkernel (System low RAM: 1539MB)
33895:Aug  5 20:00:45 dlp2-99-230 kernel: Reserving 768MB of memory at 132336MB for crashkernel (System RAM: 130563MB)
34180:Aug  5 20:00:45 dlp2-99-230 kernel: Kernel command line: BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=768M biosdevname=0 net.ifnames=0 rhgb quiet
34635:Aug  5 20:00:45 dlp2-99-230 kernel: crash memory driver: version 1.1
34919:Aug  5 20:00:46 dlp2-99-230 kernel: megaraid_sas 0000:5e:00.0: firmware crash dump#011: no

此处说明:

  • Firmware Crash Dump: megaraid_sas 0000:5e:00.0: firmware crash dump#011: no
    这行日志表明 megaraid_sas 控制器的固件不支持生成 crash dump 文件。固件设置中可能未启用相关功能或该控制器本身不支持该特性。
    该相关问题再红帽知识库也曾复现(但当前还不能完全确认是 firmware crash dump#011: no导致crash文件未生成):
    在这里插入图片描述

在这里插入图片描述

  • Reserving 256MB of low memory at 1264MB for crashkernel (System low RAM: 1539MB)

    表示系统在低内存区域(从 1264MB 开始)预留了 256MB 内存。
    
  • Reserving 768MB of memory at 132336MB for crashkernel (System RAM: 130563MB)

    表示系统在高内存区域(从 132336MB 开始)预留了 768MB 内存。
    

这里再说明下为什么需要高、低内存区域都要预留:
kdump 机制中,低内存和高内存的预留是为了确保系统在崩溃后能够成功启动捕获内核并生成内存转储文件。以下是低内存和高内存均需要预留的原因:

1.1 低内存预留的重要性
  1. 设备驱动和DMA内存:

    • 许多设备驱动和DMA(直接内存访问)操作需要使用低内存(通常是低于 4GB 的内存区域)。这些设备在捕获内核启动时仍需要工作,以便在崩溃时能够正常操作。
  2. 中断处理和内存映射:

    • 系统中的许多中断处理程序和内存映射操作都依赖于低内存。为了确保捕获内核能够正常处理中断和内存映射,低内存预留是必需的。
1.2高内存预留的重要性
  1. 内存转储空间:

    • 高内存(高于 4GB 的内存区域)预留用于存储内核崩溃转储文件。这些转储文件可能非常大,因此需要大量的高内存来保存所有的崩溃数据。
  2. 内核数据结构:

    • 捕获内核需要访问和使用大量的内核数据结构,这些数据结构通常分布在高内存区域。预留高内存确保这些数据结构在捕获内核启动时可用。

假设我们有一台具有 128GB 内存的服务器,设置 crashkernel=768M 来预留内存以备内核崩溃时使用。

  1. 低内存预留:

    • Reserving 256MB of low memory at 1264MB for crashkernel (System low RAM: 1539MB)
      • 系统在低内存区域预留了 256MB,用于捕获内核启动时需要的设备驱动和DMA操作。
  2. 高内存预留:

    • Reserving 768MB of memory at 132336MB for crashkernel (System RAM: 130563MB)
      • 系统在高内存区域预留了 768MB,用于存储内核崩溃转储文件和必要的内核数据结构。
1.3为什么两者都需要预留?
  • 低内存需求:

    • 捕获内核的引导和基础服务通常需要低内存支持,确保最基本的设备和中断处理功能正常。
  • 高内存需求:

    • 大量的崩溃数据需要在高内存中存储,避免占用低内存区域,导致设备和基础服务无法正常运行。

预留低内存和高内存的做法是为了确保在系统崩溃后,捕获内核能够成功启动并生成完整的内存转储文件。低内存用于设备驱动和基础服务,高内存用于存储崩溃数据和必要的内核数据结构。这种双重预留机制确保了捕获内核在各种硬件环境和崩溃情况下的可靠性。

有了上述分析, 进行调整操作:

[root@dlp2-99-230 log]# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="CentOS Linux"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=768M biosdevname=0 net.ifnames=0 rhgb quiet"  # 768M 或者 512M 
GRUB_DISABLE_RECOVERY="true"
GRUB_GFXPAYLOAD_LINUX=text
您在 /var/spool/mail/root 中有邮件
[root@dlp2-99-230 log]# grub2-mkconfig -o /boot/grub2/grub.cfg
[root@dlp2-99-230 log]# reboot
[root@dlp2-99-230 log]# cat /proc/cmdline  # 验证
BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=768M biosdevname=0 net.ifnames=0 rhgb quiet
[root@dlp2-99-230 log]#
1.4 模拟crash

什么是 sysrq?
sysrq 是 Linux 内核中的一个功能,通过它可以直接向内核发送特定的命令,进行如重启、内存转储、文件系统同步等操作。这个功能在调试和诊断系统问题时非常有用。

[root@dlp2-99-230 log]# systemctl status kdump # 确保kdump服务正常
● kdump.service - Crash recovery kernel arming
   Loaded: loaded (/usr/lib/systemd/system/kdump.service; enabled; vendor preset: enabled)
   Active: active (exited) since 一 2024-08-05 12:01:11 CST; 1h 31min ago
  Process: 2638 ExecStart=/usr/bin/kdumpctl start (code=exited, status=0/SUCCESS)
 Main PID: 2638 (code=exited, status=0/SUCCESS)
    Tasks: 0
   Memory: 0B
   CGroup: /system.slice/kdump.service

805 12:01:06 dlp2-99-230 systemd[1]: Starting Crash recovery kernel arming...
805 12:01:11 dlp2-99-230 kdumpctl[2638]: kexec: loaded kdump kernel
805 12:01:11 dlp2-99-230 kdumpctl[2638]: Starting kdump: [OK]
805 12:01:11 dlp2-99-230 systemd[1]: Started Crash recovery kernel arming.
您在 /var/spool/mail/root 中有邮件
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# cat /etc/kdump.conf  | grep -v '#'

path /var/crash
core_collector makedumpfile -l --message-level 1 -d 31
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]#

[root@dlp2-99-230 log]#
[root@dlp2-99-230 log]# echo 1 | sudo tee /proc/sys/kernel/sysrq  # 该命令将 1 写入 /proc/sys/kernel/sysrq 文件中,启用所有 sysrq 功能。
[root@dlp2-99-230 log]# echo c | sudo tee /proc/sysrq-trigger # 一旦启用了 sysrq 功能,可以通过以下命令触发内核崩溃: 将 c 字符写入 /proc/sysrq-trigger 文件中,告诉内核立即触发崩溃 (Crash)。这将导致系统崩溃并重启。

查看生成的文件

[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# ls
vmcore  vmcore-dmesg.txt
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# pwd
/var/crash/127.0.0.1-2024-08-05-11:56:35
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# ls
vmcore  vmcore-dmesg.txt
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# ll
总用量 1682440
-rw------- 1 root root 1722502368 85 11:56 vmcore
-rw-r--r-- 1 root root     311479 85 11:56 vmcore-dmesg.txt
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]#

# 这里说明下:有可能不生成vmcore相关文件 是因为kdump时加载的 Modules 出现问题,可以罗列出module,把最可能有问题的三方模块进行屏蔽,使用blacklist命令,具体操作可以参考红帽的官方文档说明 
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# tail -n 30  vmcore-dmesg.txt   # 相关模块
[  296.149234] Modules linked in: cfg80211 rfkill xt_multiport iptable_raw ip_set_hash_ip ip_set_hash_net veth ipip tunnel4 ip_tunnel xt_set ip_set_hash_ipportnet ip_set_hash_ipportip ip_set_bitmap_port ip_set_hash_ipport ip_set dummy nvidia_uvm(OE) ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6 ip6_tables iptable_mangle xt_comment xt_mark ipt_MASQUERADE nf_nat_masquerade_ipv4 nf_conntrack_netlink nfnetlink iptable_nat nf_nat_ipv4 xt_addrtype iptable_filter xt_conntrack nf_nat br_netfilter bridge LeoFS(OE) nfsv3 nfs_acl rpcsec_gss_krb5 auth_rpcgss nfsv4 yrfs_693(OE) yrfs_610(OE) dns_resolver LeoNET(POE) yrfs_63x(OE) nfs yrfs_6641(OE) lockd grace fscache tcp_diag inet_diag overlay(T) yrfs(OE) uio_pci_generic uio vfio_pci vfio_iommu_type1 vfio cuse fuse 8021q garp mrp stp llc bonding rdma_ucm(OE)
[  296.149555]  rdma_cm(OE) iw_cm(OE) ib_ipoib(OE) ib_cm(OE) nf_conntrack_ipv4 nf_defrag_ipv4 ip_vs_sh ip_vs_wrr ip_vs_rr ip_vs nf_conntrack ib_umad(OE) sunrpc dm_mirror dm_region_hash dm_log dm_mod iTCO_wdt iTCO_vendor_support skx_edac coretemp intel_rapl iosf_mbi kvm_intel kvm irqbypass crc32_pclmul ghash_clmulni_intel aesni_intel lrw gf128mul glue_helper ablk_helper cryptd pcspkr sg i2c_i801 lpc_ich joydev mei_me mei ipmi_si ipmi_devintf ipmi_msghandler acpi_power_meter knem(OE) ip_tables xfs libcrc32c nvidia_drm(POE) nvidia_modeset(POE) mlx5_ib(OE) ib_uverbs(OE) ib_core(OE) nvidia(POE) sd_mod crc_t10dif crct10dif_generic ast i2c_algo_bit drm_kms_helper ttm syscopyarea sysfillrect sysimgblt fb_sys_fops mlx5_core(OE) ahci crct10dif_pclmul crct10dif_common crc32c_intel libahci drm i40e megaraid_sas
[  296.149872]  mlxfw(OE) psample libata auxiliary(OE) devlink mlx_compat(OE) ptp pps_core drm_panel_orientation_quirks wmi nfit libnvdimm xpmem(OE)
[  296.149929] CPU: 20 PID: 11510 Comm: bash Kdump: loaded Tainted: P           OE  ------------ T 3.10.0-1160.el7.x86_64 #1
[  296.149962] Hardware name: Inspur SA5212M5/YZMB-00882-102, BIOS 4.0.3 03/29/2018
[  296.149985] task: ffff90eb19a46300 ti: ffff90eb456d4000 task.ti: ffff90eb456d4000
[  296.150008] RIP: 0010:[<ffffffffad274856>]  [<ffffffffad274856>] sysrq_handle_crash+0x16/0x20
[  296.150039] RSP: 0018:ffff90eb456d7e58  EFLAGS: 00010246
[  296.150056] RAX: ffffffffad274840 RBX: ffffffffadae74a0 RCX: 0000000000000000
[  296.150078] RDX: 0000000000000000 RSI: ffff90fc8e4138d8 RDI: 0000000000000063
[  296.150099] RBP: ffff90eb456d7e58 R08: ffffffffade0487c R09: 00000000ffffffff
[  296.150120] R10: 0000000000000df2 R11: 0000000000000df1 R12: 0000000000000063
[  296.150142] R13: 0000000000000000 R14: 0000000000000004 R15: 0000000000000000
[  296.150164] FS:  00007f62dc667740(0000) GS:ffff90fc8e400000(0000) knlGS:0000000000000000
[  296.150188] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  296.150207] CR2: 0000000000000000 CR3: 0000001eeefe0000 CR4: 00000000007607e0
[  296.150228] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  296.150251] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  296.150272] PKRU: 55555554
[  296.150282] Call Trace:
[  296.150296]  [<ffffffffad27507d>] __handle_sysrq+0x10d/0x170
[  296.150315]  [<ffffffffad2754e8>] write_sysrq_trigger+0x28/0x40
[  296.150337]  [<ffffffffad0c6d40>] proc_reg_write+0x40/0x80
[  296.150357]  [<ffffffffad04db50>] vfs_write+0xc0/0x1f0
[  296.150375]  [<ffffffffad04e92f>] SyS_write+0x7f/0xf0
[  296.150395]  [<ffffffffad593f92>] system_call_fastpath+0x25/0x2a
[  296.150414] Code: eb 9b 45 01 f4 45 39 65 34 75 e5 4c 89 ef e8 e2 f7 ff ff eb db 0f 1f 44 00 00 55 48 89 e5 c7 05 81 36 7d 00 01 00 00 00 0f ae f8 <c6> 04 25 00 00 00 00 01 5d c3 0f 1f 44 00 00 55 31 c0 c7 05 fe
[  296.151439] RIP  [<ffffffffad274856>] sysrq_handle_crash+0x16/0x20
[  296.152316]  RSP <ffff90eb456d7e58>
[  296.153205] CR2: 0000000000000000
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]#

2、方案二 (验证次数较少) 修改kdumpctl

该方案的优势,是无需重启操作系统,即可再下一次崩溃时,生成对应的crash文件.
kdumpctl 工具用于管理 kdump 服务,允许管理员启动、停止、检查 kdump 的状态,并重新加载配置。、

2.1 替换cmdline

修改 kdump 的 cmdline 参数可以通过编辑相关配置文件来实现。把remove_cmdline_param()和append_cmdline()方法,直接修改成直接输出方式:即$cmdline的值就是BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=768M biosdevname=0 net.ifnames=0 rhgb quiet

[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=768M biosdevname=0 net.ifnames=0 rhgb quiet
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]#

# remove_cmdline_param <kernel cmdline> <param1> [<param2>] ... [<paramN>]
# Remove a list of kernel parameters from a given kernel cmdline and print the result.
# For each "arg" in the removing params list, "arg" and "arg=xxx" will be removed if exists.
remove_cmdline_param()
{
        local cmdline=$1
        shift

        for arg in $@; do
                cmdline=`echo $cmdline | \
                         sed -e "s/\b$arg=[^ ]*//g" \
                             -e "s/^$arg\b//g" \
                             -e "s/[[:space:]]$arg\b//g" \
                             -e "s/\s\+/ /g"`
        done
        echo $cmdline
}


#
# This function appends argument "$2=$3" to string ($1) if not already present.
#
append_cmdline()
{
        local cmdline=$1
        local newstr=${cmdline/$2/""}

        # unchanged str implies argument wasn't there
        if [ "$cmdline" == "$newstr" ]; then
                cmdline="${cmdline} ${2}=${3}"
        fi

        echo $cmdline
}
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=768M biosdevname=0 net.ifnames=0 rhgb quiet

[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# current_cmdline=$(cat /proc/cmdline)
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# echo "Current cmdline: $current_cmdline"
Current cmdline: BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro crashkernel=768M biosdevname=0 net.ifnames=0 rhgb quiet

[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# new_cmdline=$(remove_cmdline_param "$current_cmdline" "crashkernel")
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# echo "Cmdline after removing crashkernel: $new_cmdline"
Cmdline after removing crashkernel: BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro biosdevname=0 net.ifnames=0 rhgb quiet

[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# updated_cmdline=$(append_cmdline "$new_cmdline" "crashkernel" "768M")
[root@dlp2-99-230 127.0.0.1-2024-08-05-11:56:35]# echo "Updated cmdline: $updated_cmdline"
Updated cmdline: BOOT_IMAGE=/vmlinuz-3.10.0-1160.el7.x86_64 root=UUID=87afdba4-8e9d-4349-a068-23ac53d18164 ro biosdevname=0 net.ifnames=0 rhgb quiet crashkernel=768M

2.2 模拟crash
参考1.4模拟crash

该方案更加便捷,但是缺点也非常明显,不利于统一管理,也有可能会带来其他kdumpctl周边问题。

四、总结

针对公司近期遇到的Centos7.9操作系统kdump crash文件vmcore未生成问题,可能也有多种原因,针对该现象最好的办法就是分析日志,根据日志的异常,和kdump的实现原理去分析推测可能卡住的地方。具体的crashkernel预留可参考调整 crashkernel 参数https://blog.csdn.net/qq_28513801/article/details/140925575 如有任何疑问或需要进一步的帮助,请随时留言讨论。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部