
简介「同一个存储挂到两台服务器一台挂载后另一台无法挂载或提示存储正忙」这类典型故障常出现在需要多机共享同一块存储的 Linux 运维场景中。这份 docx 文档围绕 Device-Mapper 机制展开先说明设备文件映射与多机并发访问产生冲突的原因再落到 dmsetup 命令的实际用法用 dmsetup remove_all 清理残留映射后重新挂载并延伸至快照、clone、镜像等映射管理能力适合有一定 Linux 基础、正在处理存储共享与映射异常的运维和开发人员参考。压缩包内仅 1 个 docx 文件约 11KB篇幅精炼可直接作为排错时的命令速查与思路对照。目前已有 1650 人学习下载可用于快速定位存储正忙类问题的处理方向。1. 存储挂载里最折腾人的场景一台服务器挂了另一台提示“存储正忙”存储挂载中最折腾人的场景是主备切换主机宕机或被强制下电业务要迁到备机结果备机执行一条mount命令后得到的是mount: /data: cant read superblock on /dev/mapper/mpatha或者干脆device or resource busy。第一反应往往是存储侧没映射好去管理界面翻半天LUN 到两台主机的映射明明都在接着怀疑文件系统坏了直接运行fsck把本来能救的数据搞得更糟。这类故障的难点不在命令而在“盘到底被谁占着”——SCSI 层、device-mapper 层、文件系统层各有一套独立状态任何一层有残留上层就会报“正忙”。这篇笔记按现象、根因、处置、预防的顺序走一遍适合正在做存储切换、被dmsetup和 multipath 卡住的运维和 DBA 照着操作。2. 为什么另一台挂不上残留映射、路径聚合与 LVM 占用2.1 从 /dev/sdX 到挂载点设备栈是四层叠加的要理解“正忙”先建立设备栈的概念。一块通过 FC 或 iSCSI 映射过来的 LUN在 Linux 里并不是直接以/dev/sda的身份被文件系统使用的中间至少要经过两到三层转换。最底下是 SCSI 层。每条链路看到一个块设备双路径就是两块比如/dev/sdb和/dev/sdc。这两个设备文件指向同一块物理 LUN但内核把它们当两个独立的块设备。往上multipathd按 LUN 的 WWID 把这些路径聚合成一个 device-mapper 设备也就是/dev/mapper/mpatha对应内核里的dm-0。再往上如果用了 LVMdm-0会被做成 PV在它上面建 VG 和 LV于是又生成一个 dm 设备/dev/mapper/vg_data-lv_data对应dm-1。文件系统最终格式化在dm-1上挂到/mnt/data。关键在于每一层的“占用”是分开记录的。SCSI 层记录设备是否被打开device-mapper 层记录每个映射的 open count 和状态文件系统层记录挂载点和日志状态。你在最上层看到device or resource busy真正卡住的可能是最底下那个你以为早就消失了的dm-0。这也是为什么只盯着/dev/sdX排查永远找不到答案。还有一层常被忽略/sys/block/dm-0/holders/这个目录。它记录的是“谁在这个 dm 设备之上又建了 dm 设备”。如果dm-0下面还压着dm-1那么任何直接对dm-0执行的删除动作都会被内核拒绝因为子设备还持有父设备的引用。排查时把这个目录的内容打印出来往往能立刻看清层级关系。提示设备栈里的层数越多出问题时越应该“从上往下”拆而不是从下往上删。顺序反了内核一定拦你。2.2 “存储正忙”的三种成因先分清是哪一种第一种是本机上一次的 dm 映射没清干净。这种情况在做过双活尝试、或者这台机器曾经是主机的环境里特别常见。上一轮umount没成功、某个进程还开着挂载点下的文件、或者 LVM 的 LV 还处于 active/dev/mapper下就会出现一堆 dm 设备。这时执行dmsetup info mpatha会看到Open count不是 0State是ACTIVE。内核的判定很直接既然有引用就不允许重新占用。第二种是同一块 LUN 在机器上呈现了两套设备。典型现象是multipath -ll里那个 mpath 设备的路径状态全是failed faulty running而lsblk又能看到一个新出现的/dev/sdd两者 WWID 相同。原因通常是一侧主机异常断链后路径没有自动恢复multipath 没能把新扫出来的路径并回原来的 map于是你手上既有“旧的死映射”又有“新的生盘符”。这时候用哪一个去挂都会出问题用旧的路径不通用新的旧的 map 还占着这个 WWID。第三种是上层 LVM 元数据残留。执行pvs会看到某个 PV 的 attr 列显示unknown device说明 VG 里记录的 PV 对应的 dm 设备已经不存在了。vgchange -an vg_data会报Cannot deactivate while in use或者干脆卡住不动。这种情况下dmsetup remove_all往往能一次解决因为残留的正是那些“名字还在、底层已经断了”的映射。三种成因的处置动作有重叠但顺序不能乱。通用的判断方法是先看挂载再看 LVM再看 dm最后看 multipath。从下往上查会绕远路。2.3 动手之前四条命令看清现场在敲任何删除命令之前先把现场记录下来。这一步不是形式主义一旦remove_all执行完发现系统盘也是 dm 设备你会无比庆幸自己存了一份清单。# 1) 列出全部 dm 映射树形展示父子关系 dmsetup ls --tree # 2) 关键字段一次性打出来设备名、主次设备号、状态、打开次数 dmsetup info -c -o name,major,minor,attr,open,segments # 3) multipath 视角每个 map 的 WWID、策略、每条路径的状态 multipath -ll # 4) 谁在挂载、谁在打开文件 findmnt -rn -S /dev/mapper/vg_data-lv_data fuser -mv /mnt/data 2/dev/nulldmsetup ls --tree的输出能直接告诉你层级如果vg_data-lv_data缩进在mpatha下面说明 LVM 还压在 multipath 之上必须先处理上面那层。dmsetup info -c的open列是核心指标非 0 就意味着有人开着这个设备删除一定失败attr列里如果带s表示这个映射处于 suspended 状态多见于路径全断后的残留。multipath -ll我一般重点看两处每个 map 括号里的 WWID 是否唯一以及路径行的状态字段。出现failed faulty说明这条链路已经不通如果所有路径都是这个状态这个 map 就是个需要清掉的对象。findmnt比mount好用在于它支持按源设备反查-S参数直接传设备路径即可-rn让输出变成一行方便脚本取。fuser -mv会把占用挂载点的进程 PID、命令名、访问方式全列出来。如果输出里有..c表示进程当前目录在里面..f表示打开的文件在里面往往就是这些东西挡住了umount。检查对象命令关键字段出现什么说明有残留dm 映射总数dmsetup ls设备名列表出现已下线 LUN 的名字引用计数dmsetup info mpathaOpen count大于 0映射状态dmsetup info -cattr列含ssuspended父子关系ls /sys/block/dm-0/holders/子设备目录非空多路径状态multipath -llfailed faulty全部路径失效挂载占用findmnt -S挂载点仍有输出把这张表逐行过一遍基本就能确定该删哪一层、能不能删。3. 用 dmsetup 清掉残留映射remove_all 的边界与正确顺序3.1 remove_all 删的是什么不删什么dmsetup remove_all的作用是把内核里所有 device-mapper 设备对象一次性拆掉。它覆盖的范围比想象中广multipath 聚合出来的mpatha、LVM 生成的vg-lv、加密卷的crypt、精简池的thin只要出现在dmsetup ls里全在清理范围内。它不碰的东西同样重要物理盘/dev/sdX不会被删分区表不会被动磁盘上的文件系统数据不会被擦/etc/multipath.conf和/etc/multipath/wwids这些配置文件原样保留。所以这个命令的实质是“把内核里的映射层拆掉等上层重新扫描时按配置重建”而不是“清空磁盘”。理解这一点才不会在按下回车前手抖。但风险也正在“范围广”这三个字上。生产环境里很多机器的系统盘本身就建在 LVM 上/、/var、/home全是 dm 设备。无脑执行remove_all这些映射会一起被拆正在写的日志直接 IO 报错极端情况下机器当场失联。这是我在实际环境里踩过的最严重的一次教训清单里明明只关心存储盘命令却不区分业务盘和系统盘。所以正确的做法是先看一眼清单再决定是精准删还是全量删# 先看有没有以 LVM- 开头、且名字里带系统卷特征的映射 dmsetup ls | grep -E ^(vg_root|vg_sys|root|swap) # 确认当前系统盘是不是也建在 dm 上 findmnt -n -o SOURCE / lsblk -o NAME,TYPE,MOUNTPOINT | grep -A2 dm-如果findmnt -n -o SOURCE /返回的是/dev/mapper/xxx这类路径remove_all就绝对不能直接执行必须改成按名字逐个dmsetup remove。只有在确认系统盘是直连分区、或者当前是救援模式、业务盘是唯一 dm 设备的情况下才考虑全量清理。注意dmsetup remove_all不等于“删除映射并保留数据可恢复”。它在拆除映射的瞬间会让所有挂在其上的文件系统 IO 中断异常掉电式的元数据风险是真实存在的。3.2 一次完整的恢复顺序照着执行真正稳妥的恢复顺序是自顶向下先卸文件系统再停 LVM再刷 multipath最后才轮到remove_all做兜底。# 0) 留档把当前状态写进文件出问题可回溯 dmsetup ls --tree /tmp/dm_before.txt 21 multipath -ll /tmp/mpath_before.txt 21 findmnt -rn /tmp/mount_before.txt 21 # 1) 卸载业务文件系统卸不掉先记下来别硬来 umount /mnt/data || echo umount failed, 继续看谁占着 # 2) 停掉对应的 LVM把 LV 的引用放掉 vgchange -an vg_data # 3) 刷掉 multipath 聚合出来的 map比直接 remove_all 精准 multipath -F # 4) 兜底清掉所有剩余的 dm 映射 dmsetup remove_all # 5) 确认现场干净 dmsetup lsvgchange -an的-a n是 deactivate 的意思作用是把 VG 里所有 LV 的 dm 设备关掉、引用计数归零。这一步成功第 4 步的成功率会高很多。如果它报Logical volume vg_data/lv_data in use说明还有进程或挂载点持有回到第 1 步用fuser -mv继续查。multipath -F是大写 Fflush 所有 multipath map小写-f只刷指定的那一个。之所以建议先刷 multipath 再 remove_all是因为 multipath 会顺带把路径状态、wwid 绑定这些运行时信息一起重置比单纯删 dm 设备更彻底。dmsetup remove_all本身没有-f参数如果想强制用的是dmsetup remove --force name或dmsetup remove --deferred name只能针对单个设备不能对 all 生效。3.3 remove_all 报 busy 时的兜底手法最常见的情况是第 4 步返回一串Device or resource busy一个都没删掉。这说明某一层还持有引用需要按下面的顺序排查。# 看挂载点下还有谁开着文件 lsof D /mnt/data 2/dev/null # 看进程和挂载点的关系含当前工作目录 fuser -mv /mnt/data # 看这个 dm 设备下面还压着谁 ls -l /sys/block/dm-0/holders/ # 延迟删除引用归零后由内核自动完成 dmsetup remove --deferred mpatha # udev 事件积压导致设备节点删不掉时的处理 udevadm settle dmsetup udevcomplete_all # 实在删不掉先置为 suspended切断 IO 再重试 dmsetup suspend mpatha dmsetup remove mpathalsof D会递归遍历目录比fuser慢但更全fuser -mv快还能看到cwd类型的占用。两者结果不一致时以lsof为准因为有些进程用 mmap 打开文件fuser不一定显示。--deferred是个很实用的参数语义是“现在标记删除等所有引用都释放了再真正删”。它不会立刻生效但能避免反复重试。执行完dmsetup ls可能还能看到这个名字等进程退出后自己就消失了。dmsetup suspend是最后一招。它把设备挂起后续所有 IO 直接失败返回强行让上层释放句柄然后再删。副作用是如果有业务还在跑会看到一堆 IO error。生产上要不要用取决于你能不能接受这个代价。4. 清完映射之后重新识别、重新挂载与一致性检查4.1 让内核重新扫盘把路径重新聚合remove_all之后dmsetup ls应该是空的但lsblk里可能还看不到那块盘或者看到了/dev/sdd却没有对应的 mpath 设备。这是因为 SCSI 总线和 multipath 都还没重新发现。# 触发所有 SCSI host 重新扫描- - - 表示扫全部通道/目标/LUN for h in /sys/class/scsi_host/host*/scan; do echo - - - $h done # 让 multipath 按配置重建 map multipath -r # 等 udev 把设备节点和符号链接建好 udevadm settle # 看结果应该只有一个 map路径状态 ready multipath -ll lsblk -o NAME,SIZE,TYPE,MOUNTPOINTecho - - - scan里的三个短横线分别对应 channel、target、LUN全写-表示通配。这条命令不会删除任何东西只是让内核重新发起一次 SCSI 枚举属于安全操作。有些发行版上/sys/class/scsi_host/下会有一堆 host全扫一编需要几秒扫完dmesg里能看到新的 sd 设备注册日志。multipath -r是 reload按/etc/multipath.conf重新加载所有 map 的配置。如果之前是配置文件改过没生效这一步会一并带上。如果重新聚合后发现分区表没被识别lsblk里 mpatha 下面没有分区补一条partprobe /dev/mapper/mpatha。要是连 mpatha 都没出现检查systemctl status multipathd多路径服务没起来的话怎么扫都是白扫。4.2 重新挂载并验证真实读写挂载之前如果这台机器上一次是非正常断开掉电、强制重启、链路突然中断建议先用只读方式检查文件系统。fsck -n不会修改任何东西只报告问题确认没问题再走真正的修复或挂载。# 只读检查先看输出再决定动不动手 fsck -fn /dev/mapper/vg_data-lv_data # 正常挂载 mount -o rw /dev/mapper/vg_data-lv_data /mnt/data # 确认挂载点和源设备对应关系 findmnt /mnt/data # 绕过 page cache 做一次真实写入10MB 足够暴露链路问题 dd if/dev/zero of/mnt/data/.dm_write_test bs1M count10 oflagdirect # 读回来校验然后清理 dd if/mnt/data/.dm_write_test of/dev/null bs1M count10 iflagdirect sync rm -f /mnt/data/.dm_write_testoflagdirect让写入跳过页缓存直接落盘只有这样才能真正验证链路通不通。不带这个参数10MB 数据可能全在内存里就返回成功了等问题暴露出来已经是几分钟后的事。写完之后一定要读回校验读取用iflagdirect两边都绕过缓存才有意义。fsck -fn的-n是“对所有问题回答 no”纯粹只读。这一步的价值在于如果文件系统日志确实有未回放的记录你能提前看到而不是等挂载时报一个cant read superblock再去猜。4.3 事前预防让下一次切换不用 remove_allremove_all是善后手段不是日常操作。要减少这类故障关键是让切换过程本身干净。第一同一块 LUN 上的非集群文件系统任何时刻只能有一台机器挂载。ext4、xfs 都没有跨节点锁两台机器同时挂上去元数据缓存各写各的磁盘上的结构很快就不一致了。要共享就走 GFS2、OCFS2 这类集群文件系统或者干脆用分布式存储别在块设备层硬来。第二旧主机下线前走标准动作先umount再vgchange -an然后multipath -FiSCSI 的话再iscsiadm -m node -u登出。这一串做全了dm 映射自然就没了根本轮不到remove_all出场。把这些写进关机脚本或者 systemd 的ExecStop比事后救火省心得多。第三设备引用一律用 WWID 或/dev/disk/by-id/不要用/dev/sdX。清完映射重扫之后盘符顺序改变是常态脚本里写死/dev/sdb的早晚有一次会在错误的盘上执行mkfs。第四主备切换要有 fencing 机制。旧主机只是“失联”不等于“已停止”它可能还在往 LUN 上写。没有 fence 就切备机两台机器同时写同一块盘数据损坏是必然的而且这种损坏在fsck之后往往表现为大量文件归零。5. 避坑dmsetup 操作前后最容易翻车的五个点5.1 现象remove_all 执行完/dev/sda 变成了 /dev/sdb现象很直观清完映射重新扫盘lsblk里的盘符顺序和之前对不上原本脚本里写死的/dev/sdb1现在指向了另一块盘。原因是 SCSI 枚举顺序本来就不保证稳定。remove_all拆掉映射后内核重新注册设备谁先响应谁拿靠前的编号和上次的顺序毫无关系。解决方式是全部改用稳定标识。multipath 设备用/dev/mapper/mpatha或/dev/disk/by-id/dm-uuid-mpath-wwid物理盘用/dev/disk/by-path/下的长名字它包含 HBA 端口、target、LUN 信息重启多少次都不变。脚本里出现/dev/sd的地方一律替换掉。5.2 现象dmsetup remove_all 报 Device or resource busy一个都没删掉现象是命令返回一串 busydmsetup ls再查一次映射原封不动。原因通常是三种之一还有挂载点没卸进程持有文件句柄LV 还处于 active上层 dm 设备压着下层udev 事件积压设备节点删不掉导致内核认为还有引用。解决顺序是从上往下拆。先fuser -mv找进程把服务停掉再umount然后vgchange -an停 LVM再multipath -F最后才dmsetup remove_all。中间任何一步报错就停在那一步解决不要跳过继续往下执行。实在删不掉用dmsetup remove --deferred name挂起删除等引用自然释放。5.3 现象映射清干净了重新挂载还是报 cant read superblock现象是dmsetup ls已经空了multipath -ll也只有一个健康 map但mount依然报cant read superblock on /dev/mapper/mpatha。原因是问题根本不在映射层而在文件系统本身。上一次非正常断开时日志没有完整回放或者更糟两台机器同时挂载过元数据已经被写坏。解决办法是先fsck -fn只读检查看输出里的错误类型。如果只是日志需要回放正常fsck修一下通常能恢复如果报大量 inode 或块位图不一致说明损坏面积大别硬修直接从备份恢复更稳。这一步的教训是remove_all只解决“占用”问题解决不了“数据已经坏了”的问题两者要分清。5.4 现象两台机器都挂上了跑了一阵数据莫名其妙丢了现象是切换期间为了图快两台机器先后mount了同一块 LUN短时间内读写都正常几小时后应用开始报文件不存在、目录大小异常。原因是 ext4 和 xfs 都不是集群文件系统各自的日志和缓存互不知情。A 机器缓存了目录项B 机器删了文件A 回写时又把目录项写回去磁盘上就是一份自相矛盾的元数据。解决方式只有一个同一时刻只允许一台机器挂载。要做双活就上集群文件系统配合分布式锁管理器或者把共享需求上移到 NFS、对象存储层。在块设备层做“两台都挂”的尝试早晚要还这笔债。5.5 现象重启之后 dm 映射又冒出来了怀疑没删干净现象是remove_all之后确认清空机器重启dmsetup ls里那些名字又回来了。原因不是残留是multipathd开机自动按配置重建了 map。这是设计行为不是故障。判断“残留”和“正常重建”的区别在于重建出来的 map 路径状态是active ready指向现存链路真正的残留是路径全failed、WWID 已经不存在的那些。解决办法是别拿“重启后有没有映射”当判断标准。要看的是multipath -ll里每个 map 的路径状态和 WWID 是否和存储侧一致。要阻止某块 LUN 被自动聚合在/etc/multipath.conf的blacklist段里按 WWID 排除比事后删映射可靠。6. 快速定位三分钟判断“存储正忙”卡在哪一层遇到device or resource busy这类报错我不会一上来就翻日志而是先判断卡在哪一层。块存储的占用是分层记录的从文件系统往下到 SCSI 一共四层每层都有对应的秒级检查手段。先看文件系统层。findmnt -S /dev/mapper/vg_data-lv_data有输出说明还挂着umount就完事没输出但fuser -mv /mnt/data有 PID说明进程占着目录停服务即可。这两条加起来不超过十秒能解决大概一半的“正忙”。再看 LVM 层。pvs -a看 PV 的 attr 列出现unknown device说明 VG 里记着一个已经不存在的设备lvs -a -o devices看 LV 是否还 active。这一层有问题vgchange -an通常会卡住或报错直接指向设备栈上层没拆干净。然后是 device-mapper 层。dmsetup info -c -o name,attr,open的open列非 0 就是有引用attr带s是 suspended 残留。配合ls /sys/block/dm-N/holders/看父子关系能立刻判断是“删不掉”还是“不该删”。最后是 multipath 层。multipath -ll里路径全failed faulty说明链路断了map 是个空壳可以安全清掉WWID 出现重复说明同一块 LUN 被聚合成了两套设备必须先multipath -F再重扫。层级首选命令关键信号处置动作文件系统findmnt -S dev有挂载点输出umount或停占用进程进程fuser -mv mnt列出 PID停服务后重试卸载LVMpvs -a、lvs -a -o devicesattr 为unknown devicevgchange -andevice-mapperdmsetup info -copen非 0、attr含sdmsetup remove --deferred层级关系ls /sys/block/dm-N/holders/子设备非空先删上层multipathmultipath -llfailed faulty、WWID 重复multipath -F后重扫SCSIlsblk、dmesg | tail盘符缺失、注册失败写scan触发重扫这套顺序的价值在于“不跳层”。跳过文件系统直接删 dm一定 busy跳过 LVM 直接remove_all可能把系统盘一起拆了跳过 multipath 只删 dm下次扫盘还是两套设备打架。从那以后我每次做存储切换都强制按umount→vgchange -an→multipath -F→ 重扫 → 只读fsck→ 挂载 这条链走一遍宁可多花三分钟也不赌一次。上面那张表我打印出来贴在机柜门上出问题时从第一行往下扫基本三分钟内能定位到具体哪一层在拦路。希望帮到你。本文还有配套的精品资源点击获取