ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

kpartx命令详解:轻松挂载多分区磁盘镜像

kpartx命令详解:轻松挂载多分区磁盘镜像 1. kpartx 到底解决什么问题从一次“挂不上镜像”说起做嵌入式Linux开发、玩树莓派镜像、或者帮朋友恢复一张整盘备份的人几乎都遇到过同一个尴尬手里拿到一个xxx.img文件明明里面有好几个分区用mount -o loop去挂载系统却只认出一个分区或者干脆报错。我第一次被这个问题卡住是在改树莓派系统镜像的时候。树莓派官方系统镜像里默认有两个分区一个是 FAT 格式的 boot 分区存放固件、内核和cmdline.txt另一个是 ext4 的 rootfs 根文件系统分区。我当时想改一下cmdline.txt里的串口参数结果发现整个镜像文件只能被当作单个块设备挂载根本没有办法直接访问第二个分区。后来查了一圈才知道这类场景需要用到 kpartx。这个命令的名字拆开看就是“kernel partition parts x扩展”核心作用是把磁盘镜像里的分区表解析出来并通过 Linux 内核的 device mapper 机制为每个分区生成一个独立的块设备节点。生成之后每个分区就可以像普通磁盘分区一样被mount、被fsck、被读取和修改。所以 kpartx 不是什么花哨工具它是 Linux 磁盘管理、嵌入式镜像操作、系统备份恢复场景里非常实用的“桥接工具”。如果你是运维、嵌入式开发、玩开源硬件、或者经常跟整盘镜像打交道的人这个命令值得花十分钟搞明白。这篇文章我把原理讲清楚再给几个可以直接照抄的完整案例以及我踩过的坑和排查经验。2. 安装与环境准备三分钟让环境就绪2.1 各主流发行版的安装方式kpartx 来自multipath-tools软件包绝大多数发行版仓库里都有安装很简单。发行版安装命令Debian / Ubuntu / Raspberry Pi OSsudo apt install multipath-toolsCentOS / RHEL / Rocky / Fedorasudo dnf install multipath-toolsopenSUSEsudo zypper install multipath-toolsArch / Manjarosudo pacman -S multipath-tools装完之后可以用kpartx -v看一下版本号确认命令可用。有些精简定制系统里可能找不到这个包那就需要看有没有kpartx的独立二进制或者直接使用 util-linux 里的losetup -P作为替代方案这个我在后面会专门对比。2.2 依赖的内核模块与权限问题kpartx 依赖两个底层能力loop设备支持以及device mapperdevicemapper。绝大多数现代发行版默认都启用了但如果你用的是很精简的容器环境、或者自编译的内核需要先确认一下。检查 loop 设备是否可用ls /dev/loop*如果列表是空的尝试加载模块sudo modprobe loop确认 device mapper 是否工作ls -l /dev/mapper/control如果/dev/mapper/control不存在需要加载 dm_mod 模块并确认device-mapper相关服务正常。实际操作中只要是正常安装的完整 Linux 系统这两个依赖通常都不需要额外配置。我在 WSL 里也测试过 kpartx基本可以用但偶尔会遇到 loop 设备数量限制这个问题的排查方法我会在第五部分专门讲。2.3 使用权限建议kpartx 创建映射设备、调用 losetup 关联镜像文件都需要 root 权限。普通用户直接跑几乎肯定会遇到 Permission denied 或者找不到设备节点的问题。命令行前面加sudo是基本操作。如果你在脚本里反复调用建议不要偷懒省掉sudo检查逻辑而是提前判断当前用户是否为 root 或拥有 sudo 权限不然脚本跑一半才崩溃会很被动。提示千万不要用sudo kpartx -a去处理一个你已经挂载正在使用的物理磁盘分区这点在第 5.4 节会展开讲。3. 核心命令详解每个参数都不是白给的3.1 命令语法与常用参数kpartx 的基本用法可以总结成一句话kpartx 参数 设备或镜像文件。最常用的几个参数如下参数作用典型场景-l列出镜像或设备中的分区映射情况但不真正创建映射动手前先看清楚分区表-a根据分区表创建设备映射生成 /dev/mapper/loopXpY 节点挂载镜像前的核心步骤-d删除之前创建的映射卸载镜像后的清理步骤-u更新某个现有映射分区表变化后使用修改了分区表之后刷新-v输出详细执行过程排查问题时强烈推荐-s同步模式等 udev 处理完映射节点再返回在脚本里用避免设备节点还没生成就继续执行这几个参数里-l和-a是使用频率最高的-v和-s则是写脚本时的可靠保障。我最常用的组合是sudo kpartx -avs /path/to/disk.img-a创建映射-v打印每个映射的起始扇区和大小-s确保映射节点创建完成后再返回三个参数配合起来非常顺手。3.2 映射名称规则与设备节点创建映射之后kpartx 会按照设备名 分区号的规则生成映射设备节点。假设你的镜像被关联到了/dev/loop0那么第一个分区对应/dev/mapper/loop0p1第二个分区对应/dev/mapper/loop0p2第 N 个分区对应/dev/mapper/loop0pN这里有一个很容易忽略的细节kpartx -a之后原本的/dev/loop0这个设备仍然存在但它的角色变成了“整个磁盘的块设备”而你真正要挂载的分区是/dev/mapper/loop0p1这些映射设备。刚接触的人经常会不小心去mount /dev/loop0结果报错说找不到文件系统实际上他们应该挂载的是loop0p1。如果你使用的是更加现代的方式先手动losetup -P关联镜像则内核会自动生成/dev/loop0p1这类节点这时不通过 kpartx 也可以直接挂载分区。两种方式殊途同归kpartx 的优势在于兼容性好、历史久、在各类脚本和教程中出现频率极高。3.3 创建映射时系统后台到底做了什么理解 kpartx 的工作原理对排查问题很有帮助。当你执行sudo kpartx -av disk.imgkpartx 大致做了这几件事第一如果传入的参数是一个普通文件而非块设备它会先调用 losetup找一个空闲的 loop 设备把镜像文件关联上去。这一步等同于你手动执行sudo losetup -f disk.img。第二它读取这个块设备上的分区表支持 MBR 和 GPT解析出每个分区的起始扇区、扇区数量以及分区类型。第三根据分区表信息调用 device mapper 的接口创建名称为loopXpY的映射设备并把这些映射设备挂到/dev/mapper/目录下。整个过程可以用一个比喻来理解你把一张写着“仓库货架分布图”的图纸交给 kpartx它照着图纸搭出一个个独立的“取货口”每个取货口对应一个真实的分区。之后你只需要去对应的取货口取货而不用每次都把整个仓库的门打开绕过货架从头找。这也是为什么 kpartx 对单个文件系统镜像比如只包含一个根文件系统的.ext4文件没有必要使用因为单个文件系统本身就可以直接用mount -o loop挂载不需要解析分区表。3.4 查看分区信息的标准动作在任何写操作之前先用-l看一下分区表信息这是一个非常值得养成的习惯。sudo kpartx -l disk.img输出会显示每个分区的映射名称、扇区数量、起始扇区等关键信息。例如loop0p1 : 0 131072 /dev/loop0 8192 loop0p2 : 0 9216000 /dev/loop0 139264从左到右分别是映射设备名、分区扇区数量、底层块设备、分区起始扇区。这些信息能帮你确认这个镜像里到底有几个分区分区大概多大是否和预期一致。如果镜像文件本身有问题或者分区表损坏这里会有异常输出或者直接报错。提示先-l看清楚再-a动手这个顺序虽然简单但能帮你避免大部分低级错误尤其是对着一块不认识的“祖传镜像”时。4. 完整实操案例修改树莓派系统镜像里的配置和文件系统4.1 案例背景与准备假设你现在有一个树莓派系统镜像raspios.img你需要做两件事修改/boot/cmdline.txt里的内核启动参数并且往 rootfs 分区里放一个自己的配置文件。环境假设一台 Linux 主机已安装 kpartx目标镜像文件路径为/data/raspios.img。先创建一个挂载点目录备用mkdir -p /mnt/rpi-boot /mnt/rpi-rootfs4.2 用 kpartx -l 看清分区表先不要急着映射用-l预览一下sudo kpartx -l /data/raspios.img输出示例loop0p1 : 0 262144 /dev/loop0 8192 loop0p2 : 0 17563648 /dev/loop0 270336这表明镜像有两个分区第一个分区从扇区 8192 开始大小约 128MB第二个分区从扇区 270336 开始大小约 8GB。树莓派系统一般就是 boot 分区加 rootfs 分区看到这个输出心里就有底了。4.3 创建映射并挂载 boot 分区接下来执行创建映射sudo kpartx -avs /data/raspios.img输出类似add map loop0p1 (253:0): 0 262144 linear 7:0 8192 add map loop0p2 (253:1): 0 17563648 linear 7:0 270336确认/dev/mapper/loop0p1和/dev/mapper/loop0p2都已经出现后挂载 boot 分区sudo mount /dev/mapper/loop0p1 /mnt/rpi-bootboot 分区是 FAT 格式挂载后可以直接读写里面的文件ls /mnt/rpi-boot sudo sed -i s/consoleserial0,115200// /mnt/rpi-boot/cmdline.txt这里cmdline.txt是树莓派引导时读取的内核参数文件修改后可以立即生效适用于调整串口、显示输出、根文件系统挂载参数等。4.4 挂载 rootfs 分区并修改内容挂载 rootfs 分区的方式一样sudo mount /dev/mapper/loop0p2 /mnt/rpi-rootfs如果需要修改 rootfs 里的内容比如把某个配置文件放进去sudo cp /data/my-config.conf /mnt/rpi-rootfs/etc/my-config.conf如果 rootfs 分区是 ext4 格式直接写入没有任何问题。但如果需要修改的是系统目录比如/etc/passwd、/etc/fstab要特别注意不能只修改文件内容还要考虑文件权限、selinux/AppArmor 上下文等因素否则可能在真实硬件上出现奇怪的问题。嵌入式开发中常见的做法是通过 chroot 进入 rootfs 执行脚本但这就涉及 chroot 环境下的/proc、/dev挂载等额外操作本文先不展开。4.5 卸载清理先 umount 再 kpartx -d修改完成后清理步骤的顺序非常重要。先卸载分区sudo umount /mnt/rpi-boot sudo umount /mnt/rpi-rootfs再删除映射sudo kpartx -d /data/raspios.img最后释放 loop 设备sudo losetup -d /dev/loop0这个顺序不能乱。如果映射没删除就试图losetup -d会提示设备忙如果 umount 没完成就kpartx -d正在使用映射设备的进程会受影响严重时可能导致正在写入的数据损坏。很多时候你会发现明明umount执行成功了kpartx -d却报/dev/loop0忙。这时候通常是某个进程的工作目录还在挂载点里面或者文件被进程打开着。可以用lsof D /mnt/rpi-rootfs查看是哪个进程占用了目录找到后退出那个进程再清理。如果对镜像文件直接执行过kpartx -a在有些版本上执行kpartx -d并不会自动释放 loop 设备所以我习惯于在清理映射后手动执行一遍losetup -d确保零残留。4.6 重新打包与校验如果你只是修改镜像内容不涉及分区表调整那么把修改后的 img 文件直接拿来写 SD 卡或作为产物分发即可不需要重新打包。如果你在修改过程中动了分区表比如用 parted 调整了分区大小那就要注意分区表一致性。kpartx 创建映射时读取的是当时的分区表如果分区表发生了变化需要先删掉旧映射再用kpartx -av重新创建否则挂载到的还是旧分区信息。写入 SD 卡或实际设备前建议先用下面的命令做一次文件系统一致性检查sudo fsck -f /dev/mapper/loop0p1 sudo fsck -f /dev/mapper/loop0p2这一步在修改过文件系统内容、或者经历过不正常的断电中断后格外重要。树莓派系统镜像里 rootfs 分区是 ext4Linux 的 ext4 在异常卸载后会有日志恢复机制但运行 fsck 确认无损是最稳妥的做法。4.7 一个快速脚本把“映射、挂载、卸载、清理”封装起来实际工作中我通常会写一个简单的 bash 函数来管理这套流程避免每次敲一长串命令还容易漏步骤。#!/bin/bash # Usage: img-mount.sh image-file IMG$1 if [ -z $IMG ]; then echo Usage: $0 image-file exit 1 fi sudo kpartx -avs $IMG echo Mapping created. Use kpartx -l $IMG to verify.再看一个清理函数#!/bin/bash # Usage: img-umount.sh image-file IMG$1 LOOP_DEV$(losetup -j $IMG | awk -F: {print $1}) sudo kpartx -d $IMG if [ -n $LOOP_DEV ]; then sudo losetup -d $LOOP_DEV fi脚本里先通过losetup -j找到绑定到该镜像的 loop 设备然后再关联删除避免手动判断编号。这种封装方式在需要批量处理多个镜像、或是在 CI 流水线里自动处理镜像时非常实用。5. 常见问题与排查技巧实录5.1 分区映射设备不出现或消失执行kpartx -av之后/dev/mapper/loop0p1一直没有出现这是新手最常见的问题。排查思路分几步第一确认 kpartx 是否真的识别到了分区。看-v输出如果输出里就没有add map行说明分区表解析本身就有问题。第二检查 udev 是否来得及创建设备节点。这也是我推荐使用-s参数的原因。没有-s时kpartx 可能已经返回但 udev 还在异步处理设备节点创建脚本里紧接着去mount就会找不到设备。第三确认设备节点是否被创建但路径不对。有些系统里映射设备可能挂在/dev/dm-0、/dev/dm-1这种路径下然后由 udev 在/dev/mapper/下创建软链接。如果/dev/mapper/目录下没有对应名称可以查看ls -l /dev/dm-*确认是不是名称映射的问题。第四检查内核日志dmesg | tail -50如果出现类似device-mapper: create ioctl failed: Device or resource busy的信息说明同名映射已经存在需要先用kpartx -d清理。5.2 target is busy 与占用根源umount时报target is busy或者kpartx -d时报设备忙九成是有进程还在使用分区。常见的占用来源包括某个 shell 的当前工作目录还停留在挂载点内文本编辑器、文件管理器打开了挂载点内的文件chroot 环境没有退出系统服务通过 systemd mount 单元接管了挂载点解决思路是先用lsof或fuser找到占用进程lsof D /mnt/rpi-rootfs或者fuser -vm /mnt/rpi-rootfs然后根据输出结果结束对应进程或者切换 shell 目录后再执行 umount。如果是 systemd 挂载单元导致的需要先systemctl stop相关单元。5.3 loop 设备耗尽与模块参数kpartx -a时如果提示找不到可用的 loop 设备通常是因为空闲 loop 设备数量被用完了。查看当前 loop 使用情况losetup -a默认情况下系统最多支持 8 个 loop 设备/dev/loop0到/dev/loop7现代发行版一般已经调整到 256 个。如果不够用可以通过修改内核模块参数临时增加sudo sh -c echo options loop max_loop128 /etc/modprobe.d/loop.conf sudo modprobe -r loop sudo modprobe loop注意卸载 loop 模块前要确保所有 loop 设备都没在被使用否则会报错。这种场景常见于长期在服务器上处理大量镜像文件的自动化流水线排查到这一步基本就能解决了。5.4 修改后的镜像无法启动的几个常见元凶我在嵌入式开发和系统镜像维护过程中见过不少“改了镜像之后设备起不来”的案例kpartx 本身一般不是罪魁祸首但和 kpartx 相关的操作流程常常是间接原因。第一个常见元凶是/etc/fstab里使用了 UUID 引用分区但修改镜像内容时不小心改变了文件系统的 UUID。比如拿 mkfs 重建文件系统、或者拷贝文件时用了错误的工具UUID 一变启动时根分区就找不到了。第二个常见元凶是树莓派这类设备的引导参数里使用了PARTUUID。树莓派的cmdline.txt里一般会有rootPARTUUIDxxxx这个 PARTUUID 是分区表级别的标识跟文件系统 UUID 不是一回事。如果你用表编辑器改动过分区表PARTUUID 可能会变化就必须同步修改cmdline.txt里的引用。第三个常见元凶是分区表类型被意外改变。比如原本是 GPT 分区表处理镜像时用了某些老旧工具写成了 MBR导致引导器找不到分区。用fdisk -l和parted -l都可以快速确认分区表类型。第四个常见元凶是文件系统大小和分区大小不匹配。比如分区表里写了 8GB 分区但文件系统实际只有 4GB或者反过来。这种情况下系统能启动但空间异常或者启动时根文件系统检查失败。可以用resize2fs或e2fsck修复。5.5 kpartx 与 losetup、partx 的混淆问题很多人在网上搜资料时会看到losetup -P和partx也能完成类似工作容易搞混。losetup -P /dev/loop0 disk.img是把镜像关联到 loop 设备并且让内核扫描分区表自动生成/dev/loop0p1之类的分区节点。这种方法很现代、依赖 util-linux 和内核的分区扫描能力在很多新系统上效果不错。但它的局限在于分区节点是内核直接生成的普通块设备没有经过 device mapper在某些需要持久映射、多路径、快照的高级场景下不如 kpartx 灵活。partx -a /dev/loop0是让内核重新读取并注册某个块设备上的分区它修改的是内核里的分区表视图不会在/dev/mapper下建节点。partx更多用于让内核识别新写入的分区表比如在裸设备上刚用 fdisk 分完区后执行一遍partx -a让系统立刻认识新分区省去重启。kpartx 的价值在于它面向 device mapper 生态在嵌入式镜像、多路径存储、系统备份恢复等场景中稳定可靠而且可以直接对普通镜像文件操作。日常使用中你不需要在这几个工具之间纠结记住一点就行处理“整个磁盘镜像文件、里面有多个分区、想挂载某个分区”的场景kpartx 是第一选择。5.6 常见错误速查表现象原因解决办法No such file or directory镜像文件路径不对或 loop 设备不存在检查文件路径执行ls /dev/loop*确认 loop 设备device-mapper: create ioctl failed同名映射已存在先kpartx -d清除旧映射mount: cant read superblock挂载了整盘而不是分区挂载/dev/mapper/loopXpY而不是/dev/loopXtarget is busy有进程占用挂载点用lsof/fuser找到进程并处理loop: No such device or addressloop 模块未加载sudo modprobe loop修改后无法启动UUID / PARTUUID 引用不一致检查/etc/fstab与cmdline.txt引用6. 使用心得与几个容易忽略的细节6.1 操作顺序是成功的一半kpartx 的完整操作链看起来很简单但顺序错了就会出各种莫名问题。我个人总结的黄金顺序是kpartx -l查看分区信息和预期是否一致kpartx -avs创建映射并等 udev 完成设备节点创建mount /dev/mapper/loopXpY挂载具体分区执行所需修改umount卸载所有挂载的分区kpartx -d删除映射losetup -d释放 loop 设备这个顺序适用于绝大多数 kpartx 实战场景写脚本时也建议固化成函数避免漏步骤。尤其是最后两步很多人只做到 umount 就以为完事了结果下次处理时发现一堆残留的/dev/mapper设备或者 loop 设备被占满。6.2 脚本化自动化要点如果要在自动化场景中反复使用 kpartx有几个细节值得注意。第一尽量用-s参数强制同步模式避免设备节点还没生成就被后续命令使用。第二清理时要动态查找 loop 设备而不是硬编码/dev/loop0因为并发任务可能分配不同的空闲设备。第三所有 kpartx 操作都要有 root 权限脚本里建议在开头统一检查id -u。第四处理完成后最好验证一下清理结果确认/dev/mapper下没有残留映射。#!/bin/bash if [ $(id -u) -ne 0 ]; then echo Please run as root exit 1 fi6.3 与备份恢复组合使用kpartx 最常见的实际应用之一就是从整盘镜像里恢复单个文件或目录。假设你有一台设备的完整备份device.img现在只想找回里面/etc目录下的一个配置文件。用 kpartx 挂载对应分区找到文件拷贝出来再按顺序清理整个过程只需要几分钟不需要把整张镜像恢复到物理设备上。这种处理方式比“整个设备恢复”效率高得多也是企业环境里文件级恢复常用的思路。更进一步你还可以用 kpartx 挂载镜像后配合rsync增量同步部分目录到目标系统这在批量部署和系统迁移时非常实用。6.4 尽量避免在物理磁盘上滥用 kpartxkpartx 不只可以处理镜像文件它也可以直接对物理设备操作比如sudo kpartx -a /dev/sdb这条命令会把/dev/sdb整块磁盘的分区表解析出来并创建/dev/mapper/sdb1之类的映射设备。但这里有个隐患如果该磁盘本身已经挂载了分区或者系统已经通过内核认出了/dev/sdb1你相当于同时存在两套访问同一分区的路径处理不当很容易造成数据不一致甚至误操作覆盖数据。我的建议是除非你明确知道自己在做什么否则不要对正在使用的物理磁盘使用kpartx -a。处理镜像文件才是 kpartx 最舒服的舞台。6.5 一个小技巧处理 GPT 分区表的注意事项kpartx 对 GPT 分区表的支持已经非常成熟但有一个细节值得注意GPT 分区表在磁盘开头不仅有保护性 MBR还有 GPT 头所以第一个分区的起始扇区一般不是 2048 就是 4096 的倍数和 MBR 时代常见的 63 扇区对齐方式不同。处理 GPT 镜像时用kpartx -l看到的第一分区起始扇区通常会很大这完全正常不用慌。真正需要担心的是有些老旧的第三方工具在处理 GPT 镜像时不认识分区表会把镜像误判为“没有分区”这时用 kpartx 反而能正确识别。6.6 个人使用体会折腾 Linux 这么多年kpartx 是我在处理系统镜像、磁盘备份、嵌入式开发时离不开的一个小工具。它不像 fdisk、mkfs 那么常被提起但真正遇到“多分区镜像挂不上”的问题时它几乎是最顺手的解决方案。我现在的习惯是在公司内部的镜像构建脚本里、个人玩树莓派的折腾过程中、以及给客户做系统迁移方案时都会默认把 kpartx 纳入标准工具链。它没有什么学习成本规则简单、输出直观只要记住“先 -l 查看、再 -a 映射、最后 -d 清理”这套流程基本就不会出错。最后再分享一个细节如果你经常在一个不太熟悉的 Linux 环境里工作第一次执行 kpartx 之前先确认它是否已经安装。很多精简服务器镜像默认不装 multipath-tools遇到问题时临时 apt install 虽然不慢但第一次报错时能被这个原因节省几分钟排查时间也挺值得的。
返回列表