麒麟 V10 LVM 磁盘繁忙率异常复现:从 807% 到内核修复
文章目录21 节
一位同事在麒麟 V10 上遇到过一个很反直觉的现象:磁盘实际流量只有几十 KiB/s,sar 的 %util 却会周期性跳到几千甚至几万;直接分区使用的盘看起来正常,做成 LVM 的盘更容易暴露异常。关闭应用和服务后,现象仍会复现。
我在 Termark 资产“临时测试麒麟”上搭建了 direct 分区盘与 LVM 盘两条路径,对齐同事的麒麟构建、内核和 sysstat 版本,复现出 sar 807.70%、atop 895%,再用 /proc/diskstats 的最小 4 KiB 写入实验定位到 io_ticks 异常。最后只升级麒麟维护内核,在磁盘、LVM、XFS、sysstat、atop 和 fio 均不变的条件下完成了 A/B 验证。
这次实验的结论不是“LVM 无法被 atop 监控”,也不是“sar 算法有问题”。真正异常的是旧内核块层提供的设备繁忙时间计数;经本次实测有效的解决方案,是升级到包含相应修复的后续麒麟维护内核。
一、先纠正截图中的工具:这是 sar,不是 atop
同事提供的异常画面具有 DEV、tps、rkB/s、wkB/s、aqu-sz、await 和 %util 等列,因此这是 sysstat 套件中的 sar -d 输出,不是 atop 界面。

截图里相邻样本的间隔约为 2 秒,sdb 只有约 0.75 KiB/s 读取、2 tps,但 %util 突然达到 29368.50%。如果按 sysstat 的换算关系反推:
29368.50% × 2000 ms ÷ 100 ≈ 587370 ms也就是说,内核计数在一个 2 秒样本里一次性增加了约 587 秒,接近 10 分钟。这与同事描述的“每 10 分钟出现一次”高度吻合:磁盘并没有真的同时繁忙 293 倍,而是一次 I/O 或 flush 触发了旧时间差的集中补记。
需要保留一个证据边界:这张图来自同事现场,只能证明现场存在极端 %util 跳变;本文后续的 807.70%、895%、io_ticks +86999 ms 和内核升级结果,来自我在独立麒麟实验机上的复现。
二、先把版本对齐,否则复现结果没有可比性
同事补充的版本截图显示:

| 项目 | 同事环境 | 我的复现阶段 | 修复验证阶段 |
|---|---|---|---|
| 操作系统 | Kylin Linux Advanced Server V10 SP3 Build23 | 相同 | 相同 |
| 构建日期 | 2023-03-24 | 相同 | 相同 |
| 内核 | 4.19.90-52.46.v2207.ky10.x86_64 | 完全相同 | 4.19.90-52.65.v2207.ky10.x86_64 |
| sysstat | 12.2.1-1.p00.ky10.x86_64 | 完全相同 | 保持不变 |
| atop | 未提供 | 2.7.1 | 保持同一二进制 |
| fio | 未提供 | 3.7 | 保持不变 |
下面这组命令只读取系统、内核和工具版本,适合在目标麒麟主机与复现机上分别执行。/etc/.kyinfo 可能包含构建信息,公开日志前应检查是否需要脱敏。
风险等级:INFO(只读)
作用: 对齐发行版构建、当前运行内核、sysstat、fio、iotop、LVM 和 atop 版本。 执行对象: 麒麟 Linux 主机的 shell;读取版本不要求停止业务。 完成验证: 复现阶段至少应确认发行版构建、内核和 sysstat 与问题主机一致;否则只能作为相似环境测试,不能称为同版本复现。
cat /etc/.kyinfo
uname -r
rpm -q sysstat fio iotop lvm2 device-mapper
/usr/local/sbin/atop -V 2>/dev/null || atop -V我的复现环境最终得到:
Kylin Linux Advanced Server V10 SP3 Build23,2023-03-24
4.19.90-52.46.v2207.ky10.x86_64
sysstat-12.2.1-1.p00.ky10.x86_64
fio-3.7-6.p01.ky10.x86_64
iotop-0.6-20.p01.ky10.noarch
atop 2.7.1这也回答了“版本不一样会不会影响实验”的问题:会。%util 的原始数据来自运行内核,内核版本是这次实验最关键的变量;sysstat 版本会影响展示和设备命名,但无法修复内核已经记错的 io_ticks。
三、实验拓扑与安全边界
系统盘 /dev/sda 已经使用 LVM,实验没有修改它。我只操作两块新挂载的 10 GiB QEMU 测试盘:
/dev/sdb
└─ /dev/sdb1, XFS, label=ATOP_DIRECT
└─ /mnt/atop-direct
/dev/sdc
└─ /dev/sdc1, GPT Linux LVM
└─ PV /dev/sdc1
└─ VG vg_atop_lab
└─ LV lv_lvm_test, dm-2, XFS, label=ATOP_LVM
└─ /mnt/atop-lvm1. 操作前只读确认磁盘身份
下面的检查用于确认候选盘的容量、序列号、文件系统签名、挂载、LVM 归属、只读标志和 holders。命令本身不修改磁盘,但不能只按 /dev/sdX 名称或容量猜设备;生产环境还应核对 WWN、阵列 LUN ID 或虚拟化平台映射。
风险等级:INFO(只读)
作用: 在任何分区和格式化之前,证明
/dev/sdb、/dev/sdc是本次专用空盘,并排除系统盘、已挂载盘和现有 LVM 成员。 执行对象: 本次实验机;其他环境必须替换设备名,并使用持久化设备身份重新核对。 完成验证: 两块目标盘无挂载、无现有签名、不是 PV,holders 为空且只读标志为0;任何一项不符合都应停止。
lsblk -o NAME,KNAME,MAJ:MIN,TYPE,SIZE,FSTYPE,LABEL,MOUNTPOINTS,RO,SERIAL,WWN
findmnt
wipefs -n /dev/sdb
wipefs -n /dev/sdc
pvs -o pv_name,pv_uuid,pv_size,pv_free,vg_name
udevadm info --query=property --name=/dev/sdb | grep -E '^(ID_SERIAL|ID_WWN)='
udevadm info --query=property --name=/dev/sdc | grep -E '^(ID_SERIAL|ID_WWN)='
ls -la /sys/class/block/sdb/holders /sys/class/block/sdc/holders
cat /sys/class/block/sdb/ro /sys/class/block/sdc/ro我核对到的实验盘序列号分别是 QEMU_HARDDISK_QM00005 与 QEMU_HARDDISK_QM00007。这两个值只用于记录本次虚拟机磁盘身份,不应作为其他环境的匹配条件。
2. 创建 direct 分区盘
下面是本次实验实际执行的 direct 路径创建命令。它会重写 /dev/sdb 的分区表并格式化 /dev/sdb1,对错误设备执行会造成数据丢失。
风险等级:DANGER(重分区并格式化整块目标盘)
作用: 将已确认无数据的专用测试盘
/dev/sdb创建为 GPT 单分区 XFS,作为不经过 LVM 的对照组。 执行对象: 本次实验唯一确认过的/dev/sdb,序列号为QEMU_HARDDISK_QM00005;其他环境严禁照抄设备名。 执行前检查: 必须先完成上一节的lsblk、findmnt、wipefs -n、序列号、PV 和 holders 核对,并有独立备份或确认该盘无数据。 不可恢复点:sfdisk写入分区表和mkfs.xfs -f创建文件系统会覆盖原有磁盘结构;没有备份时不能通过简单撤销恢复。 恢复与验证: 若命令中途失败,先停止后续写入并从备份恢复;完成后用lsblk -f、blkid和findmnt核对来源、标签与挂载点。
printf 'label: gpt\n,8G,L\n' | sfdisk /dev/sdb
partprobe /dev/sdb
udevadm settle
mkfs.xfs -f -L ATOP_DIRECT /dev/sdb1
mkdir -p /mnt/atop-direct
mount /dev/sdb1 /mnt/atop-direct
lsblk -f /dev/sdb
blkid /dev/sdb1
findmnt /mnt/atop-direct3. 创建 LVM 测试盘
本机 util-linux 2.35.2 的 `sfdisk` 不接受 `8e00` 这种 GPT 类型缩写,因此我使用了 Linux LVM 的完整类型 GUID。下面命令会重写 `/dev/sdc`、创建 PV/VG/LV,并格式化逻辑卷。风险等级:DANGER(重分区、创建 LVM 并格式化目标盘)
作用: 将已确认无数据的
/dev/sdc建成sdc1 → PV → vg_atop_lab → lv_lvm_test → XFS,作为 LVM 实验组。 执行对象: 本次实验唯一确认过的/dev/sdc,序列号为QEMU_HARDDISK_QM00007;VG/LV 名称也必须确认不会与现有环境冲突。 执行前检查: 除磁盘身份检查外,还要用vgs、lvs确认vg_atop_lab和lv_lvm_test尚不存在,并确保/dev/sdc1不属于任何现有卷组。 不可恢复点:sfdisk、pvcreate和mkfs.xfs -f会覆盖目标上的原有分区、LVM 元数据和文件系统;没有备份时无法可靠撤销。 恢复与验证: 中途失败时不要对其他盘尝试同名命令;完成后用pvs、vgs、lvs、dmsetup、lsblk和findmnt验证完整映射。
printf 'label: gpt\n,8G,E6D6D379-F507-44C2-A23C-238F2A3DF928\n' \
| sfdisk /dev/sdc
partprobe /dev/sdc
udevadm settle
pvcreate -y /dev/sdc1
vgcreate vg_atop_lab /dev/sdc1
lvcreate -y -n lv_lvm_test -l 100%FREE vg_atop_lab
mkfs.xfs -f -L ATOP_LVM /dev/vg_atop_lab/lv_lvm_test
mkdir -p /mnt/atop-lvm
mount /dev/vg_atop_lab/lv_lvm_test /mnt/atop-lvm创建完成后,我用下面的只读命令核对 device-mapper 映射。预期可以看到 dm-2 最终落到 /dev/sdc1。
风险等级:INFO(只读)
作用: 证明 fio 写入的逻辑卷、device-mapper 设备、分区与底层整盘之间的对应关系。 执行对象: 已完成创建的测试机。 完成验证:
dmsetup table中应出现linear 8:33 2048,即本次dm-2 → sdc1 → sdc;实际主次设备号可能随环境变化。
lsblk -o NAME,KNAME,MAJ:MIN,TYPE,SIZE,FSTYPE,LABEL,MOUNTPOINTS
pvs -o pv_name,pv_uuid,pv_size,pv_free,vg_name
vgs -o vg_name,vg_uuid,vg_size,vg_free,pv_count,lv_count
lvs -o lv_name,vg_name,lv_uuid,lv_size,segtype,devices,kernel_major,kernel_minor
dmsetup ls --tree
dmsetup table vg_atop_lab-lv_lvm_test
findmnt /mnt/atop-direct
findmnt /mnt/atop-lvm本次实测映射为:
vg_atop_lab-lv_lvm_test (253:2)
└─ (8:33)
0 16769024 linear 8:33 2048实验没有向 /etc/fstab 写入测试挂载,避免未确认的实验盘在下次启动时自动挂载。
四、安装工具,并正确理解 atop 的 DSK 与 LVM
麒麟仓库中没有提供本次需要的 atop 包,我安装了 fio、iotop 和编译依赖,再从 atop 官方站点下载 2.7.1 源码构建。安装软件和写入 /usr/local 会持久修改测试机,但不要求重启。
风险等级:CAUTION(安装软件并写入系统目录)
作用: 准备 fio、iotop 和 atop 2.7.1,使复现机能同时采集进程流量、设备繁忙率与压测数据。 执行对象: 隔离测试机;需要 root 权限和可用的软件源、HTTPS 访问。 需要核对: 上游版本和 SHA-256 可能随下载来源变化;本文记录的是本次实测文件,不应跳过来源与哈希验证。 影响与恢复: 会安装 RPM,并在
/usr/local/src/atop-lab、/usr/local/sbin写文件;不再需要时可删除自行构建文件,并通过包管理器评估卸载依赖。 完成验证:atop -V应显示 2.7.1;源码包和二进制哈希应与本次记录一致,或与读者自行确认的可信来源一致。
dnf install -y fio iotop ncurses-devel
mkdir -p /usr/local/src/atop-lab
cd /usr/local/src/atop-lab
curl -fL -o atop-2.7.1.tar.gz \
https://www.atoptool.nl/download/atop-2.7.1.tar.gz
sha256sum atop-2.7.1.tar.gz
tar -xzf atop-2.7.1.tar.gz
cd atop-2.7.1
make -j1
install -m 0755 atop /usr/local/sbin/atop-2.7.1
ln -sfn /usr/local/sbin/atop-2.7.1 /usr/local/sbin/atop
/usr/local/sbin/atop -V
sha256sum /usr/local/sbin/atop-2.7.1本次哈希记录为:
atop-2.7.1.tar.gz
ca48d2f17e071deead5e6e9cc9e388bf6a3270d695e61976b3794d4d927b5c4e
/usr/local/sbin/atop-2.7.1
39e97987f2a9eb67bd23c35acd8bc832bba6a4a29d3d47038aae5666b887045datop 支持 LVM,但它把不同层级分成不同记录类型:
| atop 类型 | 本次对象 | 含义 |
|---|---|---|
DSK | sdb、sdc | 物理整盘或底层块设备 |
LVM | dm-2 / lv_lvm_test | device-mapper 逻辑卷 |
下面的命令只采集设备统计,不修改磁盘。若只使用 -P DSK,逻辑卷不会出现;要同时观察底层整盘与 LVM,应显式选择 DSK,LVM。
风险等级:INFO(只读监控)
作用: 每秒同时输出物理盘与 LVM 逻辑卷的繁忙率,避免因过滤参数造成“atop 不显示 LVM”的误判。 执行对象: 目标 Linux 主机;
1 30表示每 1 秒采样一次,共 30 次。 完成验证: 输出中应同时出现DSK | sdc与LVM | lv_lvm_test一类记录。
atop -P DSK,LVM 1 30因此,“LVM 盘在 atop 中没有繁忙率”可能包含两种完全不同的问题:一是只筛选了 DSK,导致 LVM 行没被输出;二是本文复现的旧内核 io_ticks 异常,导致底层盘 busy 出现离谱跳变。两者不能混为一谈。
五、使用相同 fio 参数做 direct 与 LVM 对照
我让两轮 fio 只改变目标文件路径,其他参数完全一致:4 KiB 随机写、libaio、队列深度 32、direct I/O、持续 20 秒、2 GiB 测试文件。
采集端的命令只读取系统统计。建议分别在多个终端或由同一采集脚本并行启动,确保 sar、iostat、atop、pidstat、iotop 与 /proc/diskstats 覆盖同一时间窗口。
风险等级:INFO(只读监控,但会产生一定采样开销)
作用: 从设备层、逻辑卷层和进程层同时观察同一次 fio 负载。 执行对象: 测试机;
-p ALL用于让 iostat 显示整盘、分区和 device-mapper,-P DSK,LVM用于让 atop 同时显示底层盘与 LVM。 注意事项: 1 秒高频采样会增加少量 CPU 与日志开销;生产故障取证时应评估持续时长和输出目录空间。 完成验证: 各工具的采样时间应覆盖 fio 起止时间,且设备名能映射到同一测试路径。
atop -P DSK,LVM 1 30
sar -d -p 1 30
iostat -dxm -p ALL 1 30
pidstat -d 1 30
iotop -b -o -P -k -d 1 -n 30fio 会在目标文件系统持续写入测试文件,不能在生产业务目录、容量不足的文件系统或未经确认的挂载点上执行。下面是本次 direct 对照组的实测命令。
风险等级:CAUTION(持续写入 2 GiB 测试文件并制造 I/O 压力)
作用: 在 direct 分区盘上生成可控的 4 KiB 随机写负载,作为 LVM 路径的基线。 执行对象: 已确认挂载到
/mnt/atop-direct的专用测试文件系统;需要预留至少 2 GiB 空间。 影响与恢复: 测试会占用磁盘带宽并创建或覆盖fio-test.bin;停止 fio 可终止压力,验证后可删除该测试文件回收空间。 完成验证: fio 正常运行约 20 秒并输出带宽、IOPS;同时确认监控工具记录的是sdb/sdb1。
fio \
--name=direct_randwrite \
--filename=/mnt/atop-direct/fio-test.bin \
--size=2G \
--rw=randwrite \
--bs=4k \
--ioengine=libaio \
--iodepth=32 \
--direct=1 \
--runtime=20 \
--time_based=1 \
--group_reporting \
--output-format=json第二轮保持所有参数不变,只把目标切换到 LVM 文件系统。
风险等级:CAUTION(持续写入 2 GiB 测试文件并制造 I/O 压力)
作用: 在 LVM 逻辑卷上重复同一负载,与 direct 组比较底层盘、分区和逻辑卷的统计差异。 执行对象: 已确认挂载到
/mnt/atop-lvm的专用测试文件系统。 影响与恢复: 与 direct 轮相同;测试会创建或覆盖 LVM 挂载点内的fio-test.bin,不应指向真实业务文件。 完成验证: fio 正常结束,监控中同时出现底层sdc与lv_lvm_test/dm-2,并保留原始输出用于交叉核对。
fio \
--name=lvm_randwrite \
--filename=/mnt/atop-lvm/fio-test.bin \
--size=2G \
--rw=randwrite \
--bs=4k \
--ioengine=libaio \
--iodepth=32 \
--direct=1 \
--runtime=20 \
--time_based=1 \
--group_reporting \
--output-format=json在旧内核 52.46 上,我得到的完整负载对照是:
| 测试 | fio 带宽 | fio IOPS | sar 整盘峰值 | sar 整盘平均 | atop 整盘峰值 | atop LVM 峰值 |
|---|---|---|---|---|---|---|
direct /mnt/atop-direct | 24.43 MiB/s | 6253.64 | sdb 78.60% | 45.42% | sdb 85% | 不活动 |
LVM /mnt/atop-lvm | 24.23 MiB/s | 6203.29 | sdc 807.70% | 100.27% | sdc 895% | 98% |
LVM 轮第一个活动样本的实测输出如下:
sar:
17:20:28 sdc ... %util 807.70
17:20:28 vg_atop_lab-lv_lvm_test ... %util 20.80
atop:
LVM | -lv_lvm_test | busy 24% | write 5655 | avio 38.6 us
DSK | sdc | busy 895% | write 2826 | avio 2.86 ms两轮 fio 吞吐量和 IOPS 几乎相同,LVM 逻辑卷自身也只有约 20% 到 24%,但底层 sdc 突然报告 8 到 9 倍繁忙。仅靠真实业务流量无法解释这个差异。
六、最小触发:只写 4 KiB,直接观察 io_ticks 跳变
完整 fio 复现证明了现象,但还不能排除工具计算、应用行为或采样边界。为了缩小变量,我让 LVM 路径空闲 35 秒,然后只向现有测试文件写入 4 KiB,并在写入前后记录 /proc/diskstats。
下面的辅助函数只读取指定设备的原始计数。$13 是设备执行 I/O 的累计毫秒数,即本文关注的 io_ticks;$14 是加权 I/O 毫秒数。不同内核导出的字段数可能不同,解析前应先核对当前内核文档和原始行。
风险等级:INFO(只读)
作用: 以原始内核计数为准,绕开 sar、iostat 和 atop 的格式化与换算层。 执行对象: 本次测试机;设备列表需按实际拓扑修改。 完成验证: 能稳定输出
sdb、sdb1、sdc、sdc1、dm-2的请求数、inflight、io_ticks 和 weighted 值。
awk '
$3=="sdb" || $3=="sdb1" || $3=="sdc" || $3=="sdc1" || $3=="dm-2" {
printf "%-5s reads=%s writes=%s inflight=%s io_ticks=%s weighted=%s\n", \
$3, $4, $8, $12, $13, $14
}
' /proc/diskstats下面是最小触发序列。它会在 /var/tmp 保存三次快照,并覆盖 LVM 测试文件偏移 4096 字节处的 4 KiB 数据;oflag=direct,dsync 用于绕过页缓存并要求同步写入。该命令只能针对可丢弃的实验文件。
风险等级:CAUTION(覆盖测试文件中的 4 KiB 数据)
作用: 在长时间空闲后用一次极小的同步 direct 写触发块层完成路径,观察旧内核是否把异常时间差补入
io_ticks。 执行对象: 已确认可覆盖的/mnt/atop-lvm/fio-test.bin;不能替换为数据库文件、虚拟磁盘或其他业务数据。 执行前检查: 用findmnt /mnt/atop-lvm确认挂载来源,用ls -lh确认测试文件存在且可丢弃。 影响与恢复: 只覆盖测试文件中的 4 KiB;删除并重新创建 fio 测试文件即可恢复实验状态。 完成验证: 比较after-idle与after-write,请求数只应增加少量;若io_ticks增加数万或数十万毫秒,而 weighted 只增加几毫秒,即复现计数异常。
findmnt /mnt/atop-lvm
ls -lh /mnt/atop-lvm/fio-test.bin
awk '$3=="sdb" || $3=="sdb1" || $3=="sdc" || $3=="sdc1" || $3=="dm-2" {print}' \
/proc/diskstats > /var/tmp/diskstats-before-idle.txt
sleep 35
awk '$3=="sdb" || $3=="sdb1" || $3=="sdc" || $3=="sdc1" || $3=="dm-2" {print}' \
/proc/diskstats > /var/tmp/diskstats-after-idle.txt
dd if=/dev/zero \
of=/mnt/atop-lvm/fio-test.bin \
bs=4096 count=1 seek=1 conv=notrunc \
oflag=direct,dsync status=none
awk '$3=="sdb" || $3=="sdb1" || $3=="sdc" || $3=="sdc1" || $3=="dm-2" {print}' \
/proc/diskstats > /var/tmp/diskstats-after-write.txt旧内核 52.46 的差分结果是:
| 设备 | 空闲 35 秒 Δio_ticks | 触发写请求增量 | 触发 Δweighted | 触发 Δio_ticks |
|---|---|---|---|---|
sdc | 0 ms | 2 | 1 ms | 86999 ms |
sdc1 | 0 ms | 1 | 1 ms | 86998 ms |
dm-2 | 0 ms | 4 | 3 ms | 3 ms |
direct sdb | 0 ms | 2 | 1 ms | 2 ms |
direct sdb1 | 0 ms | 1 | 1 ms | 328147 ms |
这组数字是整篇实验最关键的证据:LVM 轮只增加了 2 个底层写请求,weighted I/O time 只增加 1 ms,但 sdc io_ticks 一次增加了 86999 ms。因此异常在内核导出的原始设备统计中已经存在,sar、iostat、atop 只是读取并换算,不是它们凭空制造了 807% 或 895%。
它还推翻了“直接分区的盘完全正常”这一假设。direct 的整盘 sdb 只增加 2 ms,但分区 sdb1 曾跳增 328147 ms。atop 默认的 DSK 行和本机 sar -d -p 通常不会把普通分区作为独立可见对象,而 LVM 路径的底层整盘 sdc 恰好发生跳变,所以界面上才形成“direct 正常、只有 LVM 异常”的错觉。
七、为什么 iotop 流量很小,%util 却可以达到几千
这些工具观察的是不同指标:
| 工具或数据源 | 主要观察对象 |
|---|---|
| iotop、pidstat | 进程提交或完成的 I/O 字节、任务 I/O 行为 |
| fio | 测试任务自身的 IOPS、带宽、时延 |
/proc/diskstats | 内核按块设备累计的请求数与时间计数 |
| sar、iostat、atop | 读取设备计数并按采样间隔换算 %util 或 busy |
设备繁忙率可以简化理解为:
%util = Δio_ticks(ms) ÷ 采样间隔(ms) × 100如果旧内核在一次 flush 或 passthrough 请求完成时,把长时间空闲形成的时间戳差一次性加入 io_ticks,应用实际只写了几 KiB,进程工具看到的流量仍然很小;但设备级 %util 会按错误的数万或数十万毫秒计算,因此轻易超过 100%。
Linux 上游块层历史中有多项与该机制相关的修复:
5b18b5a73760:block: delete part_round_stats and switch to less precise counting2b8bd423614c:block/diskstats: more accurate approximation of io_ticks for slow disks86d7331299fd:block: update io_ticks when io hangb81c14ca14b6:blk-mq: do not update io_ticks with passthrough requests99dc422335d8:block: support to account io_ticks precisely
这些提交用于解释上游机制,不代表我已经逐项证明麒麟 52.65 精确回移了哪一个提交。本文的最终结论来自麒麟内核 A/B 实测:52.46 会出现原始 io_ticks 大幅跳增,52.65 在同条件下不再跳增。
八、解决方案:升级维护内核,并保持其他变量不变复测
本次验证通过的目标内核是:
4.19.90-52.65.v2207.ky10.x86_64这不是要求所有麒麟 V10 主机都无条件安装同一版本。生产实施前必须确认发行版分支、仓库来源、HBA、多路径、备份软件、杀毒、内核模块和第三方驱动兼容性,并保留当前可启动内核和带外控制台。
1. 升级前只读检查
下面命令检查当前内核、可用版本、/boot 空间、默认启动项和存储拓扑。dnf --showduplicates 只查询仓库元数据,不安装软件。
风险等级:INFO(只读)
作用: 在安装新内核之前,确认主机分支、目标版本可用性、启动空间、当前默认内核和 LVM 根文件系统关系。 执行对象: 待升级的麒麟 V10 SP3 主机。 完成验证: 仓库能提供目标维护内核,
/boot空间足够,旧内核仍可识别,存储拓扑和第三方模块清单已留档。
cat /etc/.kyinfo
uname -r
rpm -q sysstat
rpm -qa 'kernel*' | sort
dnf --showduplicates list kernel
df -h /boot /
grubby --default-kernel
grubby --info=ALL
lsblk -o NAME,KNAME,MAJ:MIN,TYPE,SIZE,FSTYPE,MOUNTPOINTS
pvs
vgs
lvs -a -o lv_name,vg_name,segtype,devices2. 安装新内核,但先不删除旧内核
安装新内核会写入 /boot 和 RPM 数据库,但当前正在运行的内核不会立即切换。target 必须替换为本机仓库中已确认兼容的完整版本。
风险等级:CAUTION(安装新的系统内核包)
作用: 把目标维护内核安装到系统,同时保留当前旧内核作为回退入口。 执行对象: 本次实验的目标为
4.19.90-52.65.v2207.ky10.x86_64;其他麒麟分支不能直接套用。 影响与恢复: 会占用/boot和根文件系统空间;安装失败时不要删除旧内核,可根据包管理器日志修复仓库或依赖后重试。 完成验证:rpm -q能查到对应的 kernel、core、modules 和 modules-extra,且内核与 initramfs 文件存在。
target=4.19.90-52.65.v2207.ky10.x86_64
dnf install -y "kernel-$target"
rpm -q \
"kernel-$target" \
"kernel-core-$target" \
"kernel-modules-$target" \
"kernel-modules-extra-$target"
test -s "/boot/vmlinuz-$target"
test -s "/boot/initramfs-$target.img"下面的检查只读取新 initramfs 中的模块列表,用来确认 LVM 根文件系统所需的 device-mapper 等基础模块没有明显缺失。
风险等级:INFO(只读)
作用: 在改变默认启动项前,验证目标内核文件与 initramfs 可读,并检查常见 LVM/块设备模块。 执行对象: 已安装目标内核的主机。 完成验证:
vmlinuz、initramfs非空,lsinitrd能正常读取;实际所需模块应结合根盘、HBA、多路径和虚拟化驱动确认。
target=4.19.90-52.65.v2207.ky10.x86_64
ls -lh "/boot/vmlinuz-$target" "/boot/initramfs-$target.img"
lsinitrd -m "/boot/initramfs-$target.img" \
| grep -E '^(dm|lvm|qemu|rootfs-block)$'3. 设置默认内核并重启
改变默认启动内核并重启会中断主机服务;若目标内核或第三方驱动不兼容,还可能导致系统无法正常启动。生产环境必须有维护窗口、最新备份、业务停机确认和可操作的带外/虚拟控制台。
风险等级:DANGER(改变启动内核并造成整机中断)
作用: 把已验证文件完整的
52.65设置为默认启动项,并通过重启切换运行内核。 执行对象: 本次实验机;执行前必须确认旧内核仍保留在 GRUB 菜单中。 执行前检查: 核对grubby --info=ALL、备份、控制台、第三方模块兼容性和维护窗口;未完成这些条件时不要重启生产主机。 不可恢复点:systemctl reboot会立即中断当前业务与 SSH 会话;错误内核可能无法启动。 回滚路径: 通过 GRUB/虚拟控制台选择原内核启动,再用grubby --set-default /boot/vmlinuz-<OLD_KERNEL>恢复默认项;新内核稳定前不要删除旧内核。 完成验证: 重启后uname -r必须显示目标版本,文件系统、LVM、网络、业务和内核日志均通过验收。
target=4.19.90-52.65.v2207.ky10.x86_64
grubby --set-default "/boot/vmlinuz-$target"
grubby --default-kernel
grubby --info=ALL
systemctl reboot4. 重启后的系统与存储验收
下面的命令在系统重新上线后执行,主要检查运行内核、失败服务、块设备、LVM、挂载和新的 I/O/XFS/device-mapper 错误。读取 dmesg 通常需要 root 权限。
风险等级:INFO(只读验收)
作用: 证明主机确实运行在目标内核上,并确认升级没有破坏存储映射或产生新的块层错误。 执行对象: 重启后的麒麟主机。 完成验证:
uname -r为目标版本,测试文件系统与业务文件系统均正常挂载,LVM 结构完整,内核日志没有新 I/O、XFS 或 device-mapper 错误。
uname -r
systemctl --failed
lsblk
pvs
vgs
lvs -a -o lv_name,vg_name,segtype,devices
findmnt
dmesg -T | grep -Ei \
'I/O error|blk_update_request|Buffer I/O|XFS.*(error|corrupt)|device-mapper.*error' \
|| true本次实验机的 systemctl is-system-running 仍为 degraded,原因是实验前已经存在的 lm_sensors.service 失败;内核升级前后都存在,因此没有把它归因于本次存储实验。
九、52.65 内核的同条件 A/B 结果
升级后,我没有重建磁盘、LVM、XFS 或测试文件,也没有升级 sysstat、atop、fio 和 iotop。唯一核心变量是运行内核从 52.46 变为 52.65。
先重复同样的“空闲 35 秒后写 4 KiB”测试:
| 设备 | 空闲 35 秒 Δio_ticks | 写请求增量 | flush 增量 | Δweighted | Δio_ticks |
|---|---|---|---|---|---|
sdc | 0 ms | 3 | 2 | 2 ms | 4 ms |
sdc1 | 0 ms | 2 | 0 | 2 ms | 4 ms |
dm-2 | 0 ms | 3 | 0 | 4 ms | 4 ms |
旧内核的 +86999 ms 已经消失,所有层级都回到毫秒级。
再重复完整 LVM fio:
fio: 31.27 MiB/s, 8004.60 IOPS
sar sdc %util peak: 94.30%
sar sdc %util average: 56.86%
sar LVM %util average: 65.96%
sar sdc/LVM samples over 110%: 0atop 的 LVM 行偶尔出现 100% 到 105% 的 1 秒近似值,这与不同设备层级、采样边界和取整有关;关键是底层盘不再出现 895% 或几千百分比的跳变。实际性能判断仍应结合 IOPS、吞吐量、await、aqu-sz、业务延迟和底层硬件指标,而不是只看单一 %util。
十、生产解决方案与维护窗口前的临时口径
已验证的解决方案
对与本实验同分支、同类问题的麒麟 V10 SP3 主机,优先升级到厂商支持的后续维护内核。本次验证通过的是 4.19.90-52.65.v2207.ky10.x86_64。是否使用这一具体版本,应以目标主机的软件仓库、麒麟支持策略和第三方驱动兼容性为准。
下面这些做法不能修复根因:
- 关闭应用或服务:异常在空闲后的单次 4 KiB 写入中仍能触发。
- 只看 iotop:它能说明进程流量很小,但不能修正设备时间计数。
- 更换 sar、iostat 或 atop:三者最终都依赖内核设备统计。
- 单独升级 sysstat:可能改善展示和设备匹配,但不会修复
/proc/diskstats原始io_ticks。 - 把
%util > 100%截断成 100%:这会掩盖内核记账异常,而不是解决问题。
维护窗口前的临时监控口径
在尚未升级内核时,我建议把极端 %util 样本标记为“待核对的设备计数异常”,同时保存这些证据:
iostat -dxm -p ALL,同时覆盖整盘、分区与dm-*。atop -P DSK,LVM,同时覆盖物理盘与逻辑卷。- 异常样本前后的
/proc/diskstats原始值。 - 同期 IOPS、字节数、weighted I/O time、
await、aqu-sz、应用延迟和底层存储指标。 - 发生时间与上一次 I/O 的间隔,判断是否存在周期性空闲后 flush 触发。
判断依据不是“只要超过 100% 就忽略”,而是检查极高 %util 是否同时具备:I/O 字节很低、请求数只增加少量、weighted time 很低、io_ticks 却突然增加数万毫秒。满足这一组合时,才有充分理由把它与真实磁盘过载区分开。
十一、实验后的回滚与保留状态
本次实验结束时:
当前及默认内核:4.19.90-52.65.v2207.ky10.x86_64
保留回退内核:4.19.90-52.46.v2207.ky10.x86_64
保留原始内核:4.19.90-52.22.v2207.ky10.x86_64
sysstat:12.2.1-1.p00.ky10.x86_64
测试挂载:/mnt/atop-direct、/mnt/atop-lvm我没有清理测试数据,也没有写 /etc/fstab,以便继续复查原始证据。下面只给出未来清理的边界,本文实验中没有执行这些删除命令。
如果确认不再需要复现证据,先做只读核对。设备名、序列号、挂载来源和 VG/LV 必须全部与本次实验一致。
风险等级:INFO(只读清理前检查)
作用: 在删除实验 LVM 与卸载文件系统之前,再次确认所有目标只属于本次实验。 执行对象: 实验机;任何输出不符合预期都应停止。 完成验证: 两个挂载来源、两块磁盘序列号、VG/LV 名称和 device-mapper 映射均与本文实验拓扑一致。
findmnt /mnt/atop-direct
findmnt /mnt/atop-lvm
udevadm info --query=property --name=/dev/sdb | grep '^ID_SERIAL='
udevadm info --query=property --name=/dev/sdc | grep '^ID_SERIAL='
pvs
vgs
lvs -a -o lv_name,vg_name,devices
dmsetup ls --tree下面的清理模板会卸载 LVM 测试文件系统并删除 LV、VG 和 PV 元数据,也会卸载 direct 测试盘。它不会自动清除 /dev/sdb、/dev/sdc 的分区表,避免把更大范围的擦除隐藏在一段命令里。
风险等级:DANGER(删除实验 LVM 元数据并使测试挂载离线)
作用: 在证据已备份且明确不再需要复现环境后,删除
lv_lvm_test、vg_atop_lab、/dev/sdc1上的 PV,并卸载 direct 测试盘。 执行对象: 仅限本文实验创建的/mnt/atop-lvm、vg_atop_lab/lv_lvm_test、/dev/sdc1和/mnt/atop-direct。 执行前检查: 完成上面的挂载、序列号和 LVM 核对,确认没有进程使用挂载点,并已备份所有需要保留的原始证据。 不可恢复点:lvremove、vgremove、pvremove会删除 LVM 元数据;没有备份时测试卷数据无法通过简单撤销恢复。 恢复与验证: 需要保留数据时不要执行;误删后的恢复依赖独立备份或专业数据恢复。完成后用findmnt、pvs、vgs、lvs确认对象已消失。
umount /mnt/atop-lvm
lvremove -y /dev/vg_atop_lab/lv_lvm_test
vgremove -y vg_atop_lab
pvremove -y /dev/sdc1
umount /mnt/atop-direct
findmnt /mnt/atop-lvm || true
findmnt /mnt/atop-direct || true
pvs
vgs
lvs十二、最终结论
这次复现把几个容易混淆的问题拆开了:
- 同事截图是
sar -d,不是 atop;现场的29368.50%与约 10 分钟周期可以由一次性补入约 587 秒io_ticks解释。 - atop 支持 LVM。
DSK与LVM是不同记录类型,只使用atop -P DSK看不到逻辑卷,应使用atop -P DSK,LVM。 - “direct 正常、LVM 异常”部分来自默认展示层级造成的错觉;旧内核中 direct 分区
sdb1也出现过异常,只是默认界面通常不显示这一层。 - 旧内核
52.46上,一次 4 KiB 写入让sdc io_ticks增加86999 ms,而 weighted time 只有1 ms,证明异常来自内核块设备时间记账。 - 保持 sysstat、atop、fio、磁盘、LVM 和 XFS 不变,只升级到
52.65后,同一最小写入的sdc io_ticks只增加4 ms,完整负载也不再出现数百或数千百分比。
所以,生产解决方向应是评估并升级麒麟维护内核,同时保留旧内核、控制台和完整回滚路径。关闭应用、更换监控工具或截断 %util 都只能改变观察方式,不能修复错误的内核原始计数。
殷工每次都提供非常有趣的实验课题,亦师亦友跟着学习受益匪浅。