news 2026/10/30 7:00:32

共享存储 device is busy:Device-Mapper残留映射排查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
共享存储 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)→ 映射表 target(linear / 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 的 reservation(SCSI-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_all

dmsetup info -c输出里的Open计数是判断能否移除的第一手依据:只要不是 0,remove必然失败,remove -f也只是挂起等待。--tree则能告诉你移除顺序,先摘 snapshot 再摘 origin,顺序反了同样会 busy。代码后面的这层逻辑,比命令本身重要得多。

4. 从表象到根因:一套可复现的排查路径

4.1 先定位“忙”在哪一层

排查要自下而上,别一上来就动映射。第一步看块设备层:lsblk能看到设备树和挂载点;multipath -ll能确认多路径聚合状态和每条 path 的failed/active;dmsetup ls --tree确认 DM 层结构。三者对照,就能判断“正忙”是卡在多路径、DM 映射,还是文件系统挂载残留。

第二步看持有者:fuser -vm /mountpoint找占用进程,lsof找打开的文件描述符,/proc/mounts确认是否有残留挂载项(有时mount报错但条目已写入)。第三步看内核日志:dmesg -T | grep -i -E "dm-|multipath|scsi|reservation",I/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 2>&1 | 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-slist=0 --device=/dev/sdb

这一步是命令解决不了的那一半:它依赖你对“A 是否真的下线”的业务判断。我一般会这么理解——DM 清理是技术动作,PR 清理是决策动作,两者混在一起做,就是事故的起点。

5. 避坑与排查:五条现场踩出来的教训

5.1 现象:dmsetup remove一直报 busy,remove -f也没用

原因:还有进程持有设备 fd,或文件系统处于惰性卸载未完成状态,引用计数不归零。remove -f只是标记 deferred,并不会绕过引用。解决:fuser -vm找出进程,确认业务允许后终止;cat /proc/mounts检查残留挂载;必要时dmsetup suspend冻结队列观察是否还有 I/O 下发,再决定下一步。

5.2 现象:清完映射重启,设备名从/dev/dm-0变成/dev/dm-3,fstab 挂载失败

原因:/dev/dm-N是动态编号,重启或映射重建后顺序变化。解决:fstab 和业务配置一律用/dev/mapper/<name>或 UUID/LABEL,绝不用/dev/dm-N。这条是纯血泪经验,改配置的成本远低于一次生产事故。

5.3 现象:双机同时挂载 ext4,一台写入后另一台读到脏数据

原因:本地文件系统不支持多主机并发写,元数据缓存各自为政,journal 互相覆盖。解决:立刻停止双挂,对其中一台做只读挂载或彻底卸载,用fsck检查结构;长期方案是换集群文件系统或网络文件系统。不要试图用mount -o ro加定期刷新来“凑合共享”,那是拿数据完整性赌博。

5.4 现象:multipath -ll显示路径全 failed,DM 映射挂着但 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就一目了然。

从那以后我每次处理双机存储故障,都强制走一遍“先看树、再看持有者、最后动映射”的流程,哪怕现场再急也不跳步。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 6:59:06

SWMM与Lisflood耦合实操:溢流边界传递与时空单位对齐

简介&#xff1a;本资源是一份面向水文与市政工程领域研究者、城市排水系统建模初学者及GIS-SWMM耦合实践者的深度技术指南&#xff0c;聚焦SWMM汉化版&#xff08;SWMMH&#xff09;与Lisflood模型的协同建模全流程。内容覆盖子汇水区科学划分原则&#xff08;避建筑/道路、多…

作者头像 李华
网站建设 2026/10/30 6:59:02

AS5300/AS5500实战部署:从加电到LUN映射全链路指南

简介&#xff1a;本资源是浪潮AS5300/AS5500企业级存储系统的官方安装调试指导手册&#xff0c;面向IT运维工程师、存储系统集成人员及数据中心技术实施人员&#xff0c;聚焦基础功能部署与现场交付场景。文档覆盖设备规格解读、硬件连接&#xff08;含SS5000J系列扩展柜&#…

作者头像 李华
网站建设 2026/10/30 6:56:10

text2vec-base-chinese:轻量级中文句向量模型实战指南

简介&#xff1a;本资源为开源中文文本向量模型 shibing624/text2vec-base-chinese 的完整本地化部署包&#xff0c;面向自然语言处理初学者、语义检索开发者及轻量级RAG应用实践者&#xff0c;解决中文短文本相似度计算、关键词聚类与向量化检索等核心需求。压缩包共198个文件…

作者头像 李华
网站建设 2026/10/30 6:49:15

优秘智能OPC超级数字员工系统:任务输入、资料与人工复核怎样组织

小团队使用AI时&#xff0c;往往已经能够找到不少单项工具&#xff0c;但一项工作仍然需要人解释要求、寻找资料、检查结果和交给下一岗位。工具数量增加&#xff0c;并不自动让任务更容易承接。要把AI用于岗位工作&#xff0c;首先需要明确输入、输出和责任关系。优秘智能OPC超…

作者头像 李华
网站建设 2026/10/30 6:49:15

Rust智能指针Rc<T> 使用总结

Rust 程序设计语言第 15.4 节《Rc<T> 引用计数智能指针》的核心内容总结如下&#xff1a; 1. 为什么需要 Rc<T>&#xff1f; 多所有权场景&#xff1a;在 Rust 严格的所有权规则下&#xff0c;一个值通常只有一个所有者。但在某些数据结构&#xff08;如图、树中多…

作者头像 李华