diff --git a/linux/00 小问题记录.md b/linux/00 小问题记录.md new file mode 100644 index 0000000..db11e0b --- /dev/null +++ b/linux/00 小问题记录.md @@ -0,0 +1,304 @@ +## ECS 实例 dmesg 时间与真实时间存在 10 分钟偏差 + +- **记录日期**:2026-09-24 +- **现象**:`dmesg -T` 显示的时间戳与真实时间相差约 10 分钟(偏慢)。 +- **根因**:系统默认时钟源(clocksource)为 `tsc`,该 TSC 频率偏慢 **33.7ppm**,累计 **220 天** 误差约 **641 秒**(≈ 10.7 分钟),导致系统时钟计数不准、dmesg 时间戳随之偏移。 +- **解决**:GRUB 启动参数加入 `clocksource=kvm-clock` → 重新生成 grub 配置 → 重启实例,使 MONOTONIC 从 `kvm-clock` 重新起算。 + +### 1. 问题描述 + +ECS 实例 `dmesg -T` 打印的时间与真实时间(`date`)存在约 10 分钟偏差,且偏差随运行时长持续累积。内核日志自身的时序(先后顺序)是正确的,但**时间戳的绝对刻度不准**。 + +### 2. 原因分析 + +- 内核中存在多个可选时钟源(TSC、kvm-clock、hpet、acpi_pm 等),启动时会按优先级选定一个作为当前时钟源。 +- 本实例默认选中了 `tsc`(Time Stamp Counter,CPU 内部计数器)。该 CPU 的 TSC 计数频率与标称频率存在偏差,实测**偏慢 33.7ppm**。 +- ppm = parts per million,1ppm 即百万分之一: + + ``` + 累计误差 = 33.7e-6 × 220 天 × 86400 秒/天 + = 33.7e-6 × 19,008,000 + ≈ 640.5 秒 ≈ 641 秒 ≈ 10.7 分钟 + ``` + +- 该 TSC 在虚拟化环境下不是可靠的授时基准(可能未与宿主机对齐、或缺少稳定/不变标志),误差会线性累积。 +- `dmesg -T` 的时间戳由内核 `MONOTONIC` 时钟换算而来,时钟源偏慢直接反映为 dmesg 时间显示不准。 +- `kvm-clock` 是 KVM 半虚拟化时钟源,直接读取宿主机提供的时钟信息,在同为虚拟机的 ECS 实例上精度显著更高,应优先使用。 + +### 3. 解决方案 + +#### 步骤 1:修改 GRUB 启动参数 + +编辑 `/etc/default/grub`,在 `GRUB_CMDLINE_LINUX` 中追加 `clocksource=kvm-clock`,例如: + +```sh +GRUB_CMDLINE_LINUX="... clocksource=kvm-clock" +``` + +也可用命令追加(避免手改出错): + +```sh +sed -i 's/\(GRUB_CMDLINE_LINUX="[^"]*\)"/\1 clocksource=kvm-clock"/' /etc/default/grub +grep GRUB_CMDLINE_LINUX /etc/default/grub +``` + +#### 步骤 2:重新生成 GRUB 配置文件 + +> Alinux 的 grub 配置文件路径为 `/boot/efi/EFI/alinux/grub.cfg`(UEFI 引导)。 + +```sh +grub2-mkconfig -o /boot/efi/EFI/alinux/grub.cfg +``` + +#### 步骤 3:重启实例 + +```sh +reboot +``` + +重启的必要性:只有重启后才会以新的启动参数引导,`MONOTONIC` 从 `kvm-clock` **重新开始计数**,`dmesg -T` 的时间显示随之恢复正常。 + +#### 步骤 4:验证 + +```sh +# 当前时钟源,应显示 kvm-clock +cat /sys/devices/system/clocksource/clocksource0/current_clocksource + +# 可选时钟源列表,修改前可先确认 kvm-clock 在列 +cat /sys/devices/system/clocksource/clocksource0/available_clocksource + +# 确认启动参数已生效 +cat /proc/cmdline + +# 观察 dmesg 时间戳与 date 是否一致 +dmesg -T | tail +date +``` + +### 4. 备注 + +- 修改后 `dmesg` 中已有的历史日志时间戳会因 MONOTONIC 重新起算而重新计算,属正常现象。 +- 若实例主要用于对时间精度敏感的场景(如日志审计、分布式追踪),建议同时确认 NTP/chrony 正常同步,二者互补:NTP 校正的是墙上时钟(`CLOCK_REALTIME`),clocksource 决定的是时基精度。 +- 若 `available_clocksource` 中无 `kvm-clock`,说明宿主机/虚拟化环境不支持该时钟源,需改用其他精度更高的时钟源(如 `hpet`、`acpi_pm`)并排查宿主机侧问题。 + +--- + +## kdump 实战:让 hung_task 与 softlockup 触发 vmcore + +### 0. 总结(TL;DR) + +目标是**在内核出现 hung_task(进程长期 D 状态不切出)或 softlockup(CPU 上任务长时间不让出)时,自动 panic 并触发 kdump,把现场落到 `/var/crash` 下的 vmcore**,供后续 `crash` 工具做根因分析。 + +最短路径: + +```bash +# 1) 开启并确认 kdump 服务 +systemctl enable --now kdump +systemctl status kdump + +# 2) 配置两个 panic 开关 +echo 'kernel.hung_task_panic=1' >> /etc/sysctl.conf +echo 'kernel.softlockup_panic=1' >> /etc/sysctl.conf +sysctl -p + +# 3) 复现后取证 +ls -l /var/crash/ +crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /var/crash/<时间戳>/vmcore +``` + +> 前提:内核已预留 crashkernel 内存且 kdump 服务处于 active。若 `crashkernel` 没预留,需要改 GRUB 后重启。 + +--- + +# 操作一:开启 kdump 服务 + +## 1.1 确认内核已预留 crashkernel 内存 + +kdump 需要一个**独立预留的内存区域**(capture kernel)来在内核崩溃后接管系统。检查方式: + +```bash +grep -o 'crashkernel=[^ ]*' /proc/cmdline +cat /sys/kernel/kexec_crash_size # 非 0 表示已预留 +``` + +- 较新发行版通常默认 `crashkernel=auto`(RHEL/CentOS 7+)或 `crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M`。 +- 若输出为空或 `kexec_crash_size` 为 0,需要在 GRUB 里补上,例如: + +```bash +# /etc/default/grub +GRUB_CMDLINE_LINUX="... crashkernel=512M" + +# 重新生成 grub 配置 +grub2-mkconfig -o /boot/grub2/grub.cfg # BIOS +grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg # UEFI(路径按发行版调整) +# 然后重启 +``` + +## 1.2 安装并启动 kdump 服务 + +```bash +# 安装(按发行版) +yum install -y kexec-tools # RHEL/CentOS +apt install -y kdump-tools # Debian/Ubuntu + +# 开启并立即启动 +systemctl enable --now kdump + +# 校验 +systemctl status kdump +kdumpctl status 2>/dev/null || true +``` + +## 1.3 确认 kdump 就绪 + +```bash +cat /sys/kernel/kexec_crash_loaded # 期望 1 +ls -l /var/crash/ # 崩溃后 vmcore 落在这里 +``` + +## 1.4 关键配置项(/etc/kdump.conf) + +| 配置项 | 说明 | +|---|---| +| `path /var/crash` | vmcore 落盘目录 | +| `core_collector makedumpfile -l --message-level 1 -d 31` | 用 makedumpfile 压缩、并过滤掉全零页等无用页,显著减小 vmcore 体积 | +| `default reboot` | dump 完成后重启 | +| `ext4 /dev/vg/root` 等 | 根分区未挂载时的兜底落盘目标 | + +> 提示:`-d 31` 会过滤掉零页、缓存页等,**排查时不要用 `-d 0`(体积巨大),也不建议过滤过多**;若怀疑内存内容相关,保留默认即可。 + +--- + +# 操作二:配置 hung_task 与 softlockup 触发 panic + +内核默认检测到 hung_task / softlockup 时**只打印告警日志,不会 panic**,所以现场不会产生 vmcore。需要打开 panic 开关。 + +```bash +cat >> /etc/sysctl.conf <<'EOF' +kernel.hung_task_panic=1 +kernel.softlockup_panic=1 +EOF + +# 或使用 sysctl.d(推荐,语义更清晰) +# cat > /etc/sysctl.d/99-kdump-panic.conf <<'EOF' +# kernel.hung_task_panic=1 +# kernel.softlockup_panic=1 +# EOF +``` + +## 2.1 两个开关的含义 + +| 参数 | 含义 | 默认值 | +|---|---|---| +| `kernel.hung_task_panic` | 检测到 D 状态(不可中断睡眠)任务超时后,触发 panic | 0(只告警) | +| `kernel.softlockup_panic` | 检测到 softlockup(某 CPU 上任务长时间不调度)后,触发 panic | 0(只告警) | + +## 2.2 配套可调参数(按需) + +```bash +kernel.hung_task_timeout_secs = 120 # hung_task 判定超时,默认 120s +kernel.hung_task_warnings = 10 # 告警打印条数,-1 表示不限 +kernel.watchdog_thresh = 10 # softlockup 检测阈值基数,默认 10s +kernel.nmi_watchdog = 1 # Hardlockup 依赖 NMI 看门狗 +kernel.hardlockup_panic = 1 # 硬锁(中断被长时间关闭)也 panic,按需 +kernel.panic_on_oops = 1 # Oops 即 panic,按需 +``` + +## 2.3 注意事项 + +- **softlockup 检测依赖 watchdog 线程本身能运行**:若 CPU 长时间关中断,属于 hardlockup,需靠 `nmi_watchdog` + `hardlockup_panic` 捕获。 +- 部分内核 `softlockup_panic` 受编译选项约束;若写入失败,可检查 `CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC`,或改用内核启动参数 `softlockup_panic=1`。 +- `hung_task_panic=1` 对**合法长 D 状态**(如大 IO、网络存储挂起)也会 panic,生产环境务必评估影响,建议先在测试环境验证。 + +--- + +# 操作三:使配置生效 + +```bash +# 方式一:立即加载 sysctl.conf +sysctl -p + +# 方式二:只加载某个 sysctl.d 文件 +sysctl -p /etc/sysctl.d/99-kdump-panic.conf + +# 方式三:重启(持久生效,也是兜底手段) +reboot +``` + +## 3.1 生效校验 + +```bash +sysctl kernel.hung_task_panic kernel.softlockup_panic +# kernel.hung_task_panic = 1 +# kernel.softlockup_panic = 1 + +# kdump 侧确认 +systemctl is-active kdump +cat /sys/kernel/kexec_crash_loaded # 1 +``` + +> 建议同时确认 `kdump` 开机自启:`systemctl is-enabled kdump`。 + +--- + +# 操作四:复现后取证与分析 + +## 4.1 vmcore 落盘位置 + +问题复现后,系统会 panic → kexec 启动 capture kernel → 由 kdump 把内存镜像写入: + +``` +/var/crash/<时间戳>/vmcore +/var/crash/<时间戳>/vmcore-dmesg.txt +``` + +```bash +ls -lh /var/crash/ +cat /var/crash/<时间戳>/vmcore-dmesg.txt | tail -50 # 先看崩溃瞬间的 dmesg +``` + +## 4.2 用 crash 工具做根因分析 + +```bash +# 安装 debuginfo(以 CentOS 为例) +debuginfo-install kernel-$(uname -r) + +# 加载 vmcore +crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux \ + /var/crash/<时间戳>/vmcore +``` + +常用 crash 命令: + +| 命令 | 用途 | +|---|---| +| `bt` / `bt -a` | 查看崩溃时各 CPU 的调用栈(hung_task/softlockup 常看这里) | +| `log` | 崩溃前的内核日志缓冲 | +| `ps` / `foreach bt` | 进程状态与全量栈,定位 D 状态任务 | +| `vm` | 内存与页状态概览 | +| `sys` | 系统基本信息(版本、panic 原因等) | +| `dis -l ` | 反汇编定位到源码行 | + +## 4.3 可选:手动触发一次转储做演练 + +```bash +# 需确认 kernel.sysrq=1 且 kdump 已就绪,演练会重启系统 +echo c > /proc/sysrq-trigger +``` + +--- + +# 操作五:常见问题排查(Q&A) + +- **`/var/crash` 下没有 vmcore** + - `kdump` 服务未运行或未开机自启:`systemctl is-active kdump` / `is-enabled`。 + - crashkernel 未预留:看 `/proc/cmdline` 与 `/sys/kernel/kexec_crash_size`。 + - panic 未触发:确认 `hung_task_panic` / `softlockup_panic` 已为 1,且告警类型确实是这两种。 +- **复现了但只看到告警日志、没有 panic** + - 说明只命中了 hung_task/softlockup 告警路径,检查 sysctl 是否被其他配置覆盖(`sysctl -a | grep -E 'hung_task|softlockup'`)。 +- **`softlockup_panic=1` 写入报不存在** + - 内核编译未开放该 sysctl,改用启动参数 `softlockup_panic=1`,或升级/更换内核。 +- **vmcore 特别大,把磁盘写满** + - 确认 `core_collector` 使用 `makedumpfile -d 31` 过滤;必要时落盘到独立分区。 +- **生产环境慎用** + - `hung_task_panic=1` 可能把「可自愈的长 D 状态」也变成宕机,建议先用 `hung_task_timeout_secs` 调大、并在测试环境验证。