☰
共享存储挂载失败?深入Device-Mapper与dmsetup remove_all
2026/11/3 2:59:05 网站建设 项目流程

简介:这份资源面向 Linux 运维与存储管理人员,聚焦同一存储设备挂载到两台服务器后,一台挂载成功、另一台无法挂载或提示“存储正忙”的典型故障,帮助读者理解多机共享存储时的设备映射冲突成因与处理思路。资源包共 1 个 docx 文档,约 11KB,内容围绕 Linux 存储管理展开,重点讲解 Device-Mapper 框架下 dmsetup 命令的使用,包括通过 dmsetup remove_all 清理映射关系后重新挂载的排错路径,并延伸介绍快照、clone、镜像等映射管理能力,便于读者建立从问题定位到命令操作的完整认知。目前已有 1650 人学习,适合需要处理共享存储挂载异常、补充 dmsetup 实操经验的初中级运维人员参考。

1. 存储正忙:为什么一台服务器挂掉,另一台连挂载都做不到

一台服务器宕机,另一台执行mount却报device is busy,或者干脆卡住不动——这个现象反直觉的地方在于:挂掉的那台机器明明已经不干活了,为什么还占着存储不放?很多人第一反应是网络没断干净、对端没释放,于是反复重试、重启服务,甚至把交换机端口拔了再插,问题依旧。真正的答案藏在 Linux 块设备栈里:存储设备被打开时,内核会在设备上留下持有者记录,Device-Mapper 层还会维护一套映射表,这些状态不会因为对端进程死亡而自动消失。搞懂这套机制,你才知道dmsetup remove_all为什么能救场,也才知道它什么时候不该用。存储这块的玄学,多半来自对「谁持有设备」这件事没有可见性。

2. Device-Mapper 的分层结构:块设备请求到底经过了谁

2.1 从/dev/sda到逻辑设备:设备文件只是入口

很多人把/dev/sda当成存储本身,其实它只是一个字符或块设备节点,是用户态访问内核块设备子系统的入口。真正处理 I/O 的是内核里的块层(block layer),它把读写请求排队、合并、下发到具体驱动。中间如果启用了 LVM、加密、多路径或者快照,请求还要穿过 Device-Mapper 这一层。

Device-Mapper 的设计动机很直接:把「物理设备」和「逻辑设备」解耦。物理设备可能是一块盘、一个 iSCSI LUN、一个光纤通道卷;逻辑设备则是上层文件系统看到的那个块设备。映射关系由一张表描述,表里每一行是一个 target,声明「逻辑地址区间 → 目标类型 + 目标参数」。常见的 target 类型包括linear(线性映射)、striped(条带)、snapshot(快照)、crypt(加密)、multipath(多路径)。

这种分层带来的直接后果是:一个逻辑设备背后可能挂着多个物理设备,一个物理设备也可能被多个逻辑设备引用。当两台服务器通过共享存储看到同一个 LUN 时,各自内核里都会建立自己的映射表,彼此不知道对方的存在。这就是冲突的根源——不是网络问题,是两套独立的内核状态在抢同一块物理介质。

2.2 映射表与持有者:dmsetup info能看到什么

要排查「存储正忙」,第一步不是急着删映射,而是看清楚当前有哪些映射、谁在持有。dmsetup info输出的是每个 mapped device 的元信息,关键字段包括名字、UUID、状态、打开计数。

# 列出所有 Device-Mapper 设备的基本信息 dmsetup info # 查看某个具体设备的详细状态,包括 open count dmsetup info -c --noheadings -o name,open,segments <device_name> # 查看映射表内容,确认 target 类型和起止扇区 dmsetup table <device_name>

open字段就是打开计数。只要这个值大于 0,说明还有内核对象持有该设备——可能是挂载中的文件系统、正在运行的 LVM 逻辑卷、一个没退出的dd进程,或者 multipath 守护进程。dmsetup remove在 open count 非零时会直接失败,报的就是device is busy那类错误。dmsetup remove_all则是批量删除所有映射,它同样受 open count 约束,并不会强行剥离正在使用的设备。

这里有个容易忽略的点:dmsetup table显示的是逻辑扇区到物理扇区的映射,单位是 512 字节扇区。如果你看到0 2097152 linear 8:16 2048,意思是逻辑设备从扇区 0 开始、长度 2097152 个扇区,线性映射到主设备号 8、次设备号 16 的物理设备,起始偏移 2048 扇区。这个信息在判断「两台服务器是不是映射到了同一个物理 LUN」时非常有用——对比两边的major:minor和偏移,就能确认是不是真的在抢同一块介质。

2.3 共享存储的两种语义:为什么「同时挂」本身就是危险动作

共享块存储(比如通过 SAN 或 iSCSI 暴露的 LUN)在协议层面允许多个 initiator 同时连接,但「允许连接」不等于「允许同时挂载文件系统」。绝大多数本地文件系统(ext4、xfs)没有分布式锁机制,它们假设自己是块设备的唯一使用者。两台服务器各自 mount 同一个 LUN,各自在内存里维护自己的超级块缓存、日志、位图,写回时互相覆盖,轻则文件系统损坏,重则整卷不可读。

所以「一台挂了另一台挂不上」这个现象,从设计角度看反而是保护:内核在打开设备时发现已有持有者(或者底层驱动报告设备被占用),拒绝再次打开,避免两个未协调的写入者同时操作。真正该问的不是「怎么强行挂上」,而是「这个共享场景到底该用什么文件系统或访问方式」。集群文件系统、分布式锁管理器、或者干脆走网络文件协议,才是共享语义下的正解。dmsetup remove_all解决的是「残留映射导致本机无法重新建立映射」这类本机状态问题,不是「让两个写入者安全共存」的银弹。

3.dmsetup remove_all到底做了什么:从命令到内核的调用链

3.1 命令执行路径:用户态工具如何驱动内核

dmsetup是用户态工具,它通过/dev/mapper/control这个字符设备与内核的 Device-Mapper 驱动通信。每次操作本质是一次 ioctl:用户态构造dm_ioctl结构体,填入命令码(如DM_DEV_REMOVE、DM_DEV_REMOVE_ALL)、设备名或 UUID、标志位,然后ioctl(fd, DM_IOCTL, &ioctl_struct)下发。

remove_all对应的是DM_DEV_REMOVE_ALL命令。内核侧的处理逻辑大致是:遍历全局的 mapped device 列表,对每个设备尝试执行删除流程。删除一个 mapped device 的核心步骤包括——检查 open count 是否为零、从全局 IDR 中摘除、销毁对应的dm_table、释放 target 私有数据、最后销毁gendisk并让设备节点失效。任何一步失败(最常见就是 open count 非零),该设备的删除就中止,命令返回错误。

/* 内核侧删除 mapped device 的关键判断(示意,非逐行源码) */ if (atomic_read(&md->holders)) { /* 还有持有者,拒绝删除 */ return -EBUSY; } /* 摘除全局注册、销毁映射表、释放 gendisk */ dm_destroy(md);

这段逻辑解释了为什么remove_all有时「删不干净」:它按顺序尝试,遇到 busy 的设备就跳过或报错,不会强行拆掉正在被文件系统使用的映射。你在终端看到的报错,就是内核-EBUSY一路传回用户态的结果。

3.2 删除顺序与依赖:为什么先卸文件系统再删映射

一个 mapped device 上可能叠着多层:物理 LUN → multipath 映射 → LVM PV → LVM LV → 文件系统。删除必须自顶向下。如果文件系统还挂着,LV 的 open count 就不为零;LV 没删,PV 的引用计数也降不下来;PV 还在,底层 multipath 映射就删不掉。

常见的实现思路是严格按栈序操作:

# 1. 先确认挂载点并卸载文件系统 findmnt /dev/mapper/<vg>-<lv> umount /dev/mapper/<vg>-<lv> # 2. 停用 LVM 逻辑卷,释放对 PV 的引用 lvchange -an /dev/<vg>/<lv> # 3. 如果 PV 还在,用 vgchange 停用整个卷组 vgchange -an <vg> # 4. 确认 open count 归零后再删映射 dmsetup info -c -o name,open <device_name> dmsetup remove <device_name>

跳过任何一步,后面的删除都会撞上device is busy。血泪经验是:不要一上来就remove_all,先dmsetup info看清楚 open count,再顺着挂载点、LVM、multipath 一层层往下拆。remove_all适合的场景是「确认没有任何文件系统在用、只是残留了一堆无用映射」的清理,而不是运行中系统的急救按钮。

3.3 残留映射的判定:怎么区分「真占用」和「假占用」

有时候dmsetup info显示 open count 为 0,但dmsetup remove仍然失败。这种情况多半是设备节点还被某个进程以O_EXCL方式打开,或者 udev 正在处理该设备的事件队列。排查手段是查/proc/*/fd里有没有指向该设备的文件描述符,以及看dmesg里有没有驱动层的占用报告。

# 查找仍持有该块设备的进程 for pid in /proc/[0-9]*; do ls -l "$pid/fd" 2>/dev/null | grep -q "<device_name>" && echo "$pid" done # 查看内核日志中与 device-mapper 相关的最近记录 dmesg | tail -50 | grep -i -E "dm-|device-mapper|busy"

如果确认没有进程持有、open count 也为零,但删除仍报错,可以尝试dmsetup remove --force(部分版本支持)或者先dmsetup suspend再删。注意--force是危险操作,它绕过部分检查,只在明确知道设备无用时使用。另一种「假占用」来自 multipath:multipathd会持续持有路径设备,需要先multipath -f <mpath>刷新映射,再删底层 dm 设备。

4. 避坑与排查:共享存储场景下的常见翻车记录

4.1 现象:remove_all执行后设备还在,报错被刷屏

原因:remove_all遍历时遇到 busy 设备会报错并继续,终端输出大量device or resource busy,但实际只删掉了空闲的那几个。解决:不要依赖remove_all的批量语义,先用dmsetup info -c -o name,open列出 open count 非零的设备,逐个处理其上层依赖,最后再删。批量命令适合收尾,不适合排障。

4.2 现象:卸载文件系统后 open count 仍不为零

原因:文件系统虽然 umount 了,但 LVM 逻辑卷仍处于激活状态,或者有进程通过已删除的路径持有 fd(deleted 文件)。解决:lvchange -an停用逻辑卷,再用lsof或遍历/proc/*/fd确认没有残留 fd;必要时vgchange -an停用整个卷组。注意停用前确认没有其他 LV 在被使用。

4.3 现象:两台服务器都看到同一个 LUN,一台挂载后另一台报正忙

原因:这是共享块设备的预期行为,不是故障。本地文件系统没有跨节点协调能力,内核拒绝第二个写入者。解决:确认业务是否真的需要双写。如果只是主备切换,备机不应同时挂载;如果需要并发访问,应改用集群文件系统或网络文件协议。强行用remove_all绕过只会把问题推迟到数据损坏那一刻。

4.4 现象:dmsetup remove报Device or resource busy,但dmsetup info显示 open 为 0

原因:udev 事件处理、multipath 守护进程或驱动层的临时引用。解决:先multipath -f刷新多路径映射,等待 udev 队列排空(udevadm settle),再重试删除;仍失败则查dmesg确认是否有驱动层错误,不要盲目加--force。

4.5 现象:删除映射后重新扫描,设备名变了

原因:Device-Mapper 设备名和/dev/dm-N的次设备号分配依赖创建顺序,删除重建后编号可能变化。解决:不要用/dev/dm-N硬编码,改用/dev/mapper/<name>或/dev/disk/by-id/下的稳定链接;在 fstab 和脚本里统一使用 UUID 或 WWID 引用。

5. 设计取舍:为什么内核不替你「自动清理」共享冲突

回到最初的问题:为什么 Linux 不设计成「对端挂了就自动释放」?因为块设备层根本没有「对端」这个概念。每个内核实例独立管理自己的映射表和持有者计数,共享介质上的状态对彼此不可见。要实现跨节点协调,需要分布式锁、租约或者集群文件系统,这些属于上层协议,不属于块层职责。块层选择的是保守策略:宁可拒绝打开,也不允许两个未协调的写入者同时操作。这个取舍牺牲了「自动恢复」的便利,换来了数据一致性上的最低保障。

dmsetup remove_all的价值也在这个框架下才说得清:它是本机状态清理工具,不是跨节点协调工具。它能解决的是「本机残留映射挡住了重新建映射」这类问题,解决不了「两个节点同时写同一块盘」的语义冲突。我一般会这么理解:把dmsetup当成块设备栈的「黑匣子读取器」,先用info和table看清状态,再决定删什么、按什么顺序删,而不是把它当成一键修复的后悔药。

验证方法上,我习惯在操作前后各跑一次dmsetup info -c -o name,open,segments和findmnt,对比 open count 和挂载点变化,确认每一步都符合预期再往下走。共享存储场景还要额外确认两边的dmsetup table是否指向同一组major:minor,避免误判。从那以后我每次碰共享 LUN 的挂载问题,都强制先看 open count 和映射表,再动手删任何东西——这个习惯帮我避开了好几次把生产卷拆坏的风险。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询