qemu-img 用法:创建镜像、看懂两个尺寸与 raw/qcow2 互转
本篇是入门路线第 03 篇。如果「虚拟机、磁盘镜像」这些词还陌生,建议先花十分钟读第 01 篇 认识 QEMU:从零看懂虚拟机、镜像与快照——本篇默认你已经知道:镜像就是宿主机上的一个文件,客户机把它当成整块硬盘。
上一篇(第 02 篇)QEMU 安装与第一个 RISC-V 虚拟平台 把虚拟机跑起来时,最后一步用 qemu-img create 建了一个 qcow2 磁盘,但那条命令的输出当时只是一带而过。磁盘镜像是虚拟机的硬盘,后面无论是装完整 Linux、做快照还是克隆虚拟开发板,每天都要和 qemu-img 打交道。本篇把这个工具最基础的三个子命令 create、info、convert 一次讲透,核心是搞清楚一个让很多新手困惑的现象:为什么一个 64MiB 的盘,在宿主机上只占 4KiB 或 196KiB。
本文要回答的问题
qemu-img create -f raw和-f qcow2建出来的镜像本质区别是什么?qemu-img info输出里的virtual size和disk size分别指什么,为什么两个数字差了上万倍?- 为什么
ls看到 64M 的 raw 文件,du却说它只占 4KiB? - qcow2 的
cluster_size、refcount bits这些元数据字段各有什么含义? - raw 和 qcow2 互转之后数据会不会丢,转换过程中稀疏性发生了什么变化?
一、qemu-img 管理的镜像生命周期
qemu-img 是 QEMU 自带的磁盘镜像工具,不启动虚拟机,只在宿主机上对镜像文件做离线操作。它的子命令不少,但日常使用频率最高的是围绕镜像生命周期的三件套:create 负责生盘,info 负责体检,convert 负责换格式。本篇的实验路径就是按这三个动作排的,先各建一块 raw 和 qcow2 盘,再用系统命令和 info 反复对照两个尺寸,最后做一次双向格式转换验证稀疏性的变化。
读这张流程卡抓住一条主线:前三步解决「镜像是什么、占多大地方」,后两步解决「格式之间怎么搬」。每一步的产物都会被下一步拿来对照,所以实验里没有任何一步是孤立的。开始动手前先记住两个格式名的含义:raw 就是「原样」,镜像内容等于一块裸盘的逐字节映像;qcow2 是 QEMU 自己的 Copy-On-Write 第二代格式,在裸盘数据外面包了一层带元数据的容器,稀疏分配、快照、压缩、后端镜像这些高级能力都由这层容器提供,本篇先只看它最基础的稀疏分配。
二、create:各建一个 64MiB 的镜像
qemu-img create 的基本形态是 qemu-img create -f 格式 文件名 容量,容量支持 K、M、G 等后缀,这里的 64M 按二进制兆字节解释,即 64MiB,等于 67108864 字节。下面的终端实录是在同一目录下连续建两块盘的真实输出,注意两条 Formatting 回显行的信息量完全不同。
raw 的回显只有 fmt=raw size=67108864,因为它没有任何格式参数可报——创建动作本质上是向宿主机文件系统申请一个普通文件,并通过「打洞」(hole)让整个文件保持稀疏,后面第三节会用 du 证实这一点。qcow2 的回显则一口气列出五个参数:cluster_size=65536 是簇大小,也就是 qcow2 内部分配空间的最小粒度,64KiB 一簇;extended_l2=off 表示未启用扩展 L2 表;compression_type=zlib 指明压缩算法;lazy_refcounts=off 是延迟引用计数开关;refcount_bits=16 是引用计数的位宽。这些字段在第四节逐行解释,这里先有个印象:回显参数越多,说明这个格式的封装层越厚。
容量参数有一个容易踩的细节:64M 是 Guest 看到的盘容量,不是宿主机立刻分配的字节数。如果想让 raw 镜像在创建时就把空间实打实占满(比如想避免运行时磁盘写满导致虚拟机异常),可以加 -o preallocation=full,但日常实验默认的稀疏模式更省空间,本篇全部使用默认值。
三、两个尺寸:virtual size 与 disk size
建盘之后最反直觉的一幕出现在 ls 和 du 的对照里。ls -lh 报告的是文件的表观大小,也就是文件元数据里登记的长度;du -h 报告的是这个文件在宿主机文件系统上真实占用的数据块大小。两个命令对着同一对镜像给出了差出几个数量级的数字。

图中同一块 64MiB 容量在三种口径下完全分裂:ls 对两个文件都报 64M,du 却只有 4.0K 和 196K,而 info 用 virtual size 与 disk size 两个字段把分歧正式登记下来。读图时先盯住 raw 那一段的 4 KiB,再对照 qcow2 段多出的元数据字段,就能直观看到两种格式的成本差异落在哪。
先看 raw:ls 说它是 64M,du 说只有 4.0K。这并不矛盾。macOS 的 APFS 和 Linux 常见的 ext4、XFS 都支持稀疏文件(sparse file):一个文件可以声明自己有 64MiB 长,但只有真正写入过的区间才分配磁盘块,没写过的区间是空洞,读出来全是零,却不占任何实际空间。刚建出的 raw 镜像一个字节都没写过,那 4KiB 只是文件系统记账所需的最小开销。qemu-img info 把这两个数字正式命名为 virtual size: 64 MiB 和 disk size: 4 KiB——前者是 Guest 里那块盘的容量,后者是宿主上的真实占用,这就是本篇标题里「两个尺寸」的准确含义。
再看 qcow2:同样是 64MiB 的虚拟容量,真实占用是 196KiB,比 raw 贵了将近五十倍。贵出来的部分是容器的元数据税。下图把两种格式在宿主机文件里的排布方式画在一起,读图时盯住「Guest 偏移」和「宿主文件块」之间有没有中间层这条区别。
图中灰色块代表不占磁盘的空洞或未映射区间,蓝色块是真正落盘的数据,紫色块是 qcow2 独有的元数据。raw 这一侧没有任何中间层:Guest 的第 N 个扇区就对应文件里第 N 个扇区的偏移,稀疏性完全依赖宿主机文件系统自身的打洞能力,所以空盘只需 4KiB 记账开销。qcow2 这一侧多了一层间接映射:Guest 偏移先查 L2 表,查到才定位到宿主文件里某个 64KiB 的簇,查不到就返回全零;而文件头、L1/L2 映射表、引用计数表这些元数据即使空盘也必须落盘,192KiB 的文件长度(file length: 192 KiB)就是这么来的,du 口径的 196KiB 与它的微小差额来自文件系统块对齐。理解这张图之后,「qcow2 空盘为什么比 raw 空盘大」就从一个待背的结论变成了可以推出来的结果。
四、info 逐行读:qcow2 的元数据字段
qemu-img info 是使用频率最高的体检命令,拿到任何来源不明的镜像,先跑一遍它再挂盘是个好习惯。raw 的输出很短,上一节已经看过;qcow2 的输出多出 cluster_size 和一整段 Format specific information,下面逐行拆解,字段值全部来自本次实测。
| 字段 | 实测值 | 含义与实际影响 |
|---|---|---|
file format | qcow2 | 容器格式,决定后面所有格式专有字段是否存在 |
virtual size | 64 MiB(67108864 bytes) | Guest 看到的盘容量,转换格式也不会变 |
disk size | 196 KiB | 宿主机真实占用,随 Guest 写入量增长 |
cluster_size | 65536(64 KiB) | 内部分配与映射的最小粒度,簇越小空间利用率越高、映射表越大 |
compat | 1.1 | qcow2 兼容版本,1.1 支持零簇等新特性,老版本 QEMU 需用 0.10 |
compression type | zlib | 压缩簇算法,另一个可选项是 zstd,压缩只在 convert 时生效 |
lazy refcounts | false | 开启后引用计数延迟落盘,崩溃后需 repair,换一点写性能 |
refcount bits | 16 | 引用计数位宽,决定一个簇最多被多少次引用,与快照深度相关 |
corrupt | false | 损坏标志位,异常断电后若为 true 需先跑 qemu-img check 修复 |
extended l2 | false | 扩展 L2 表,开启后可在元数据里缓存簇状态,提速但占更多元数据空间 |
输出末尾的 Child node '/file' 段是 QEMU 块驱动层的节点视图:protocol type: file 说明这个镜像的后端协议就是普通本地文件,file length 是宿主文件本身的长度。这一段在排查嵌套镜像(比如 qcow2 背后再挂一个 qcow2 后端文件,见 qcow2 后端文件与克隆)时特别有用,本篇的单层镜像只需知道它和 disk size 互为印证。
读 info 输出要养成一个固定顺序:先看 file format 确认格式,再看两个尺寸判断镜像写了多少数据,最后看格式专有字段评估快照与压缩能力。这个顺序在后面的扩容、压缩、快照实验里同样适用,完整的操作族见 qcow2 扩容、压缩与快照。
五、convert:raw 与 qcow2 的双向互转
qemu-img convert -O 目标格式 输入 输出 负责格式搬家,注意大写 -O 指定输出格式,小写 -f 才是输入格式——输入格式通常可以省略,工具会自动探测。先把 qcow2 转成 raw,再把最初的 raw 转回 qcow2,做一次完整往返。
第一个方向是 qcow2 转 raw,产物 from-qcow2.img 的 info 输出显示 file format: raw、virtual size: 64 MiB 不变、disk size: 4 KiB。这一步做的事情是「解开容器」:convert 顺着 qcow2 的 L2 映射把 Guest 视角的整块盘逐扇区重放出来,写进一个新的 raw 文件;没写过的区间在新文件里继续以空洞存在,所以空盘转换后依然只有 4KiB 真实占用。如果源盘里写过数据,对应的扇区会在 raw 文件里变成真实分配的区间,稀疏边界完全由「Guest 哪些扇区非零」决定。
第二个方向是 raw 转回 qcow2,产物 roundtrip.qcow2 的 disk size 重新回到 196KiB,cluster_size、refcount bits 等字段与最初直接 create 出来的 qcow2 完全一致。日志末尾的 du 把三个文件并排放在一起:raw 4.0K、两个 qcow2 各 196K。这说明往返转换是对称且无损的——虚拟容量始终是 67108864 字节,变的只是封装形态和真实占用;重新稀疏化发生在转换写入时,convert 会跳过全零区间,不会把 raw 的空洞愚蠢地展开成 qcow2 里的零簇。
两个实战注意点。第一,convert 是流式读写而非原地改名,转换期间源文件和目标文件同时存在,宿主机要预留足够空间,而且转换正在运行的虚拟机磁盘前必须先关机或确保盘是只读状态。第二,想在转换时顺手压缩,给 qcow2 目标加 -c 选项(qemu-img convert -O qcow2 -c 输入 输出),已写入的数据簇会以 zlib 压缩存放,但压缩簇是只读的, Guest 改写该簇时会退回普通簇,更细的压缩与碎片整理话题留待后续专篇。
六、raw 还是 qcow2:选型判断与常见坑
两种格式没有绝对优劣,只有场景适配。把前面实测到的事实整理成决策依据,比死记结论更可靠。
| 维度 | raw | qcow2 |
|---|---|---|
| 空盘真实占用(64MiB 实测) | 4 KiB | 196 KiB |
| 稀疏能力来源 | 宿主机文件系统打洞 | 容器内 L2 映射,跨文件系统保持稀疏 |
| 内部最小分配粒度 | 文件系统块(通常 4KiB) | 簇,默认 64KiB |
| 快照 | 不支持,依赖外部 LVM 等机制 | 原生内置,元数据即可完成 |
| 压缩 / 后端克隆 | 不支持 | 支持 zlib/zstd 压缩与 backing file |
| I/O 路径 | 最短,几乎无额外开销 | 多一层映射,有少量 CPU 开销 |
| 可移植性 | 任何工具都能直接读写 | 需要 QEMU 生态或专门转换 |
| 典型场景 | 性能基准测试、对接 Ceph/块设备 | 日常开发、快照频繁、模板克隆 |
新手最常踩的坑有三个。其一,拿 ls 的大小当真实占用去评估磁盘余量,正确做法是 du 或 qemu-img info 里的 disk size;镜像越写越满后,disk size 会持续逼近 virtual size,qcow2 不会超过虚拟容量,但如果做过快照引用,旧簇不会立刻释放,需要借助压缩与快照删除命令回收。其二,用 cp 复制 qcow2 镜像后发现文件变大,这是因为某些拷贝路径会把稀疏文件展开,保真复制应使用 cp -p 或 qemu-img convert,克隆场景则应改用 backing file 而非整盘复制。其三,把 raw 文件直接 mv 改名后在其他机器上 info 报错,raw 本身不含任何格式魔数,识别完全靠内容与后缀,传输时保留 .img 或 .raw 后缀能省掉很多解释成本。
这一篇在知识体系里的位置
本篇是 SoC 与 QEMU 系列的磁盘基础:上承 QEMU 安装与第一个 RISC-V 虚拟平台 里那笔带过的建盘命令,把镜像格式、两个尺寸与格式转换补成完整闭环;下接同系列的两篇操作深文,qcow2 扩容、压缩与快照 讲镜像建好之后怎么改容量、回收空间和打点回滚,qcow2 后端文件与克隆 则利用本篇第四节提到的引用计数机制,用一个 backing file 快速派生大量虚拟开发板。再往原理方向走,QEMU 如何把镜像里的 Guest 扇区请求接到块设备与 virtio 总线上,可以结合 QEMU/TCG 与功能平台 的设备模型视角理解。
代码获取:本篇全部实验的可复现脚本见公开仓库 derekzhuo/geek-doc.cn 的
experiments/qemu-qcow2-basics目录。克隆后进入该目录执行./01-create-info-convert.sh,终端会依次打印建盘、ls/du对照、info体检与双向转换的完整输出,与本篇实录逐条对应。
实验环境
- 机器/系统:MacBook Pro(Intel x86_64)/ macOS 26
- 工具版本:qemu-img 11.1.2(QEMU 11.1.2,Homebrew 安装)
- 日期:2026-11-07
- 完整命令序列:
qemu-img create -f raw disk-raw.img 64M→qemu-img create -f qcow2 disk.qcow2 64M→ls -lh与du -h对照 → 对两个镜像分别执行qemu-img info→qemu-img convert -O raw disk.qcow2 from-qcow2.img→qemu-img convert -O qcow2 disk-raw.img roundtrip.qcow2→ 再次qemu-img info与du -h验证往返结果
总结
qemu-img 用法的地基是三个子命令加一对尺寸概念:create 生盘时 raw 只做文件打洞、空盘实占 4KiB,qcow2 额外写入文件头与 L2 映射表、空盘实占 196KiB;info 输出里 virtual size 是 Guest 看到的盘容量、disk size 是宿主机真实占用,再加上簇大小、引用计数位宽等格式专有字段,足以给任何镜像做体检;convert 在两种格式间对称搬运数据,转换保持虚拟容量不变并重新按空洞稀疏化,往返后占用与原生镜像一致。把这两个尺寸和稀疏分配的机制吃透,后面的扩容、快照、压缩与后端克隆都只是在 qcow2 的元数据层继续做文章。