共享存储 device is busy:Device-Mapper残留映射排查 简介这份 docx 文档聚焦 Linux 多服务器环境中的共享存储挂载故障面向运维工程师、系统管理员以及正在学习存储管理的读者围绕同一存储设备挂到两台服务器后出现一台无法挂载或提示“存储正忙”的典型问题展开。内容从设备文件、设备映射与挂载机制入手分析冲突成因并给出借助 dmsetup 清理映射后重新挂载的排错思路同时延伸到快照、克隆与镜像等 Device-Mapper 管理用法帮助读者建立从现象定位到命令验证的完整排查路径。压缩包共 1 个 docx 文件约 11KB篇幅精炼适合作为日常运维排障时的速查笔记也可用于补充共享存储冲突、dmsetup 命令实践与存储管理知识框架便于按问题现象快速检索对应思路。已有 1650 人学习或下载适合需要快速了解共享存储冲突处理与 dmsetup 基本用法的读者参考。1. 共享存储“存储正忙”一个反直觉的真相两台服务器挂同一块存储一台宕机后另一台执行mount却报device is busy甚至设备节点还在、容量也在就是挂不上。很多人的第一反应是文件系统坏了于是fsck一路跑下去或者怀疑链路、HBA、多路径。但真正卡住的往往是内核里那层看不见的“映射表”——设备已经不可达Device-Mapper 的映射项却还挂着I/O 全部阻塞在队列里。这就是存储运维里典型的玄学现场。搞懂它比记住任何一条命令都值你能判断该清映射、该重启服务还是该先摘链路而不是靠dmsetup remove_all碰运气。2. Device-Mapper 的分层/dev/sda和挂载点之间到底隔了什么2.1 从块设备到文件系统中间是一个可插拔的映射层很多人把/dev/sda理解成“磁盘本身”其实在多数生产环境里它只是最底层的物理或虚拟块设备上面还叠着 Device-Mapper下文简称 DM这层。DM 是 Linux 内核的一个块设备映射框架LVM、多路径multipath、dm-crypt、dm-snapshot 全都建在它上面。它做的事很纯粹接收上层文件系统发来的 bio按映射表把逻辑地址翻译成底层设备地址再转发下去。所以一条 I/O 的真实路径是应用 → 文件系统 → DM 逻辑设备如/dev/dm-0→ 映射表 targetlinear / multipath / snapshot→ 底层块设备 → 存储。文件系统眼里的“一块盘”其实是 DM 临时拼出来的逻辑视图。问题就出在这里文件系统只认 DM 设备而 DM 映射的生命周期和底层存储的可用性并不同步。底层断了映射还在就成了一个“指向空气”的设备节点。这里有个容易被忽略的细节/dev/dm-N是内核动态分配的编号每次加载映射都可能变真正稳定的标识是/dev/mapper/name这个符号链接。排查时如果盯着/dev/dm-0看重启一次顺序就全乱了血泪经验。2.2 映射表项的生命周期谁创建、谁持有、谁释放一条 DM 映射从生到死有几个明确阶段理解它才能理解为什么“删不掉”阶段触发动作内核状态创建dmsetup create/lvcreate/ multipath 自动映射表写入/dev/dm-N出现激活dmsetup resume开始接受 I/O队列可下发挂起dmsetup suspend队列冻结新 I/O 排队等待移除dmsetup remove引用计数归零后释放强制移除dmsetup remove -f标记 deferred等引用释放关键在“引用计数”。只要还有文件系统挂载、还有进程持有该设备的文件描述符、还有未完成的 I/O 在队列里remove就会被拒绝报Device or resource busy。这跟删除一个正被打开的文件是同一个道理内核不允许你抽掉别人脚下的地板。而remove_all做的事就是遍历所有映射逐个尝试移除——它不是“万能钥匙”只是把“逐个 remove”批量化了。3. 两台服务器抢同一块存储为什么一台挂了另一台就“正忙”3.1 非集群文件系统的独占假设被打破了ext4、xfs 这类本地文件系统有一个隐含前提这块块设备只有我一个使用者在写。它们会在挂载时往设备上写日志区journal、维护超级块、缓存元数据。两台服务器同时挂载同一个非集群文件系统等于两个人同时改同一份账本元数据迟早对不上轻则报错重则整块存储的结构被写坏。注意这不是“Linux 的限制”而是文件系统自己的并发模型决定的。要真正多机共享得换集群文件系统如 OCFS2、GFS2或走 NFS/SMB 这类网络文件系统由服务端做串行化。把 ext4 挂两台机器上本质上是在用一个不支持并发的协议做并发翻车是时间问题。3.2 一台宕机后残留的映射和未完成的 I/O 才是元凶真正的现场往往是这样服务器 A 正常挂载使用服务器 B 也通过多路径看到了同一块 LUN。A 突然宕机断电、内核 panic、链路断A 上的 DM 映射没来得及清理未完成的 I/O 还压在队列里同时 B 侧的多路径发现某条链路失败尝试切换路径或重新认设备于是报“存储正忙”。从 B 的视角看它并没有主动占用什么但底层存储可能还保留着 A 的 reservationSCSI-3 PR持久化预留或者阵列侧认为这条路径仍被 A 持有。这时候光在 B 上dmsetup remove_all是不够的——你得先确认 A 确实不会再回来了再去阵列侧清掉预留否则 B 强行接管只会引发更严重的脑裂split-brain风险。提示判断“A 是否真的死了”不能只看 ping。要看存储阵列的会话表、FC/iSCSI 登录状态、以及 PR 预留持有者。网络不通不等于主机宕机链路抖动也会造成同样的表象。3.3dmsetup remove_all到底做了什么把这条命令拆开看它等价于对/dev/mapper下每个映射执行一次dmsetup remove遇到 busy 就跳过并报错。源码层面它走的是 DM 的 ioctl 接口DM_DEV_REMOVE内核在dm_destroy()路径里检查md-open_count和md-holders非零就返回-EBUSY。所以它从来不是“强删”只是一个批量尝试。# 查看当前所有 DM 映射及其依赖关系 dmsetup ls --tree # 树状展示能看出谁依赖谁 dmsetup info -c # 列出所有映射的状态、打开数 dmsetup table name # 查看某个映射的 target 表确认底层设备 # 逐个安全移除推荐顺序先上层后底层 dmsetup remove name # 正常移除busy 会失败 dmsetup remove -f name # 强制标记删除等引用释放后生效 # 批量移除谨慎遇到 busy 会跳过并报错 dmsetup remove_alldmsetup info -c输出里的Open计数是判断能否移除的第一手依据只要不是 0remove必然失败remove -f也只是挂起等待。--tree则能告诉你移除顺序先摘 snapshot 再摘 origin顺序反了同样会 busy。代码后面的这层逻辑比命令本身重要得多。4. 从表象到根因一套可复现的排查路径4.1 先定位“忙”在哪一层排查要自下而上别一上来就动映射。第一步看块设备层lsblk能看到设备树和挂载点multipath -ll能确认多路径聚合状态和每条 path 的failed/activedmsetup ls --tree确认 DM 层结构。三者对照就能判断“正忙”是卡在多路径、DM 映射还是文件系统挂载残留。第二步看持有者fuser -vm /mountpoint找占用进程lsof找打开的文件描述符/proc/mounts确认是否有残留挂载项有时mount报错但条目已写入。第三步看内核日志dmesg -T | grep -i -E dm-|multipath|scsi|reservationI/O error、path down、PR conflict 都会留痕这是最不会骗人的黑匣子。4.2 清理残留映射的正确顺序确认没有活跃 I/O 之后顺序是卸载文件系统 → 停多路径服务或multipath -f mpath刷掉路径→dmsetup remove逐层删映射 → 必要时dmsetup remove_all兜底。如果卸载时报 busy先fuser -km杀掉占用进程生产环境慎用再umount -l惰性卸载等引用释放。# 典型清理脚本每步都要确认返回码 umount /data 2/dev/null || umount -l /data # 卸载失败则惰性卸载 multipath -f mpatha 2/dev/null # 刷掉指定多路径设备 dmsetup ls --tree # 确认剩余映射 dmsetup remove_all 21 | tee /tmp/dm_remove.log # 批量移除并留日志 dmsetup ls # 验证是否清空顺序颠倒的代价很实在先删底层映射上层 snapshot 的引用会悬空内核可能直接 panic 或进入不可中断睡眠D 状态那时候只能重启。脚本里tee留日志不是形式主义批量移除跳过了哪些、报了什么错事后全靠它复盘。4.3 阵列侧的预留命令层解决不了的那一半如果 DM 层已经干净B 还是挂不上问题多半在阵列侧。SCSI-3 PR 预留是存储用来防止多主机同时写的机制A 宕机后预留可能仍被持有。需要在阵列管理界面或通过sg_persist工具查看并清除# 查看设备上的 PR 预留持有者需要 sg3_utils sg_persist --in --read-keys /dev/sdb sg_persist --in --read-reservation /dev/sdb # 确认 A 不会再回来后清除预留危险操作务必确认前提 sg_persist --out --register-ignore --param-slist0 --device/dev/sdb这一步是命令解决不了的那一半它依赖你对“A 是否真的下线”的业务判断。我一般会这么理解——DM 清理是技术动作PR 清理是决策动作两者混在一起做就是事故的起点。5. 避坑与排查五条现场踩出来的教训5.1 现象dmsetup remove一直报 busyremove -f也没用原因还有进程持有设备 fd或文件系统处于惰性卸载未完成状态引用计数不归零。remove -f只是标记 deferred并不会绕过引用。解决fuser -vm找出进程确认业务允许后终止cat /proc/mounts检查残留挂载必要时dmsetup suspend冻结队列观察是否还有 I/O 下发再决定下一步。5.2 现象清完映射重启设备名从/dev/dm-0变成/dev/dm-3fstab 挂载失败原因/dev/dm-N是动态编号重启或映射重建后顺序变化。解决fstab 和业务配置一律用/dev/mapper/name或 UUID/LABEL绝不用/dev/dm-N。这条是纯血泪经验改配置的成本远低于一次生产事故。5.3 现象双机同时挂载 ext4一台写入后另一台读到脏数据原因本地文件系统不支持多主机并发写元数据缓存各自为政journal 互相覆盖。解决立刻停止双挂对其中一台做只读挂载或彻底卸载用fsck检查结构长期方案是换集群文件系统或网络文件系统。不要试图用mount -o ro加定期刷新来“凑合共享”那是拿数据完整性赌博。5.4 现象multipath -ll显示路径全 failedDM 映射挂着但 I/O 全超时原因链路层已断multipath 的 path checker 还没判定失败I/O 在队列里排队超时表现为“假活”。解决multipath -ll关注path checker状态和failback策略临时multipath -f刷掉设备防止继续堆积同时排查 FC/iSCSI 链路不要在这个状态下跑remove_all否则会把本可恢复的路径信息一起清掉。5.5 现象A 宕机后 B 接管运行一段时间出现文件系统损坏原因A 的残留 I/O 或缓存未刷盘存储侧 PR 预留未清B 接管后两边的写序列交错脑裂导致元数据写坏。解决接管前必须确认 A 完全下线、PR 预留已清、阵列侧会话已断接管后第一时间做文件系统一致性检查。顺序是“确认死亡 → 清预留 → 摘旧映射 → 新机挂载”少一步都可能埋雷。6. 设计取舍为什么 Linux 不给你一把“万能钥匙”dmsetup remove_all被很多人当成救命稻草但它恰恰体现了 Linux 存储栈的一个核心设计哲学默认你清楚自己在做什么内核只保证机制正确不替你做业务决策。DM 的引用计数、deferred removal、PR 预留全都是“防误删、防脑裂”的护栏。它们让你在正常路径上多点几下换来的是异常路径上不至于一删就崩。如果内核允许无条件强删映射双机场景下脑裂只会来得更快更隐蔽。这个取舍有代价故障现场的操作复杂度上去了排障要靠lsblk、multipath -ll、dmsetup ls --tree、dmesg四件套交叉验证而不是一条命令。但换个角度想存储这层的错误成本太高——一次误删映射可能意味着整块 LUN 的数据不可达几小时的恢复窗口。用操作复杂度换安全边界这个交易是划算的。验证方法也很朴素在测试环境里故意制造“一台宕机”的场景观察 B 侧的 DM 状态、PR 预留、multipath 路径判定分别怎么变把每一步的dmesg输出记下来。跑通一次比读十篇文档都管用。进阶方向可以看 DM 的 target 实现drivers/md/dm-linear.c、dm-mpath.c理解 bio 是怎么被重映射的再回头看remove的-EBUSY就一目了然。从那以后我每次处理双机存储故障都强制走一遍“先看树、再看持有者、最后动映射”的流程哪怕现场再急也不跳步。希望帮到你。本文还有配套的精品资源点击获取