概述
本教程介绍如何在 FnOS(飞牛NAS)上使用 virtio-9p(Plan 9)协议将宿主机目录共享到 KVM 虚拟机中。9p 是一种轻量级的文件共享协议,无需网络配置,通过 virtio 总线直接在宿主机和虚拟机之间传输文件,性能优于 NFS/Samba 等网络共享方案。
适用场景
- 宿主机与虚拟机之间共享文件/模板
- 虚拟机访问宿主机上的大容量存储
- 多台虚拟机共享同一目录(如模板库、ISO 仓库等)
环境要求
| 项目 |
要求 |
| 宿主机 |
FnOS(基于 Debian 12)或其他支持 KVM/QEMU 的 Linux |
| 虚拟机 |
需要内核支持 9p(Linux 内核 2.6.14+,主流发行版默认支持) |
| QEMU |
支持 virtio-9p-pci 设备 |
操作步骤
第一步:确认宿主机共享目录存在
确保要共享的目录已创建,以 /vol1/1000/templates 为例:
ls -ld /vol1/1000/templates
如果目录不存在,先创建:
sudo mkdir -p /vol1/1000/templates
第二步:修复目录权限
QEMU 进程以 libvirt-qemu 用户运行,需要确保从根目录到共享目录的整条路径链都有可访问权限:
sudo chmod 755 /vol1 /vol1/1000 /vol1/1000/templates
⚠️ 关键:即使共享目录本身权限正确,如果上级目录(如 /vol1 或 /vol1/1000)缺少 o+x 权限,QEMU 仍然无法访问,会报 Permission denied 错误。
第三步:关闭虚拟机
9p 设备需要在虚拟机配置中添加,修改配置前必须先关闭虚拟机:
sudo virsh shutdown <虚拟机名称>
如果虚拟机未响应 shutdown,可以强制关闭:
sudo virsh destroy <虚拟机名称>
确认已关闭:
sudo virsh list --all | grep <虚拟机名称>
第四步:添加 9p 共享设备(一键命令)
使用以下一键命令将 9p 文件系统设备注入到虚拟机 XML 配置中:
VM_NAME="ubuntu24-clone-01"
SHARE_DIR="/vol1/1000/templates"
SHARE_TAG="templates_share"
virsh dumpxml $VM_NAME > /tmp/${VM_NAME}.xml && \
sed -i "/<\/devices>/i \ <filesystem type=\"mount\" accessmode=\"passthrough\">\n <driver type=\"path\"\/>\n <source dir=\"${SHARE_DIR//\//\\/}\"\/>\n <target dir=\"${SHARE_TAG}\"\/>\n <\/filesystem>" /tmp/${VM_NAME}.xml && \
virsh define /tmp/${VM_NAME}.xml && \
rm -f /tmp/${VM_NAME}.xml
输出:
Domain 'ubuntu24-clone-01' defined from /tmp/ubuntu24-clone-01.xml
参数说明:
| 变量 |
说明 |
示例 |
VM_NAME |
虚拟机名称 |
ubuntu24-clone-01 |
SHARE_DIR |
宿主机要共享的目录路径 |
/vol1/1000/templates |
SHARE_TAG |
共享标签名(虚拟机内挂载时引用) |
templates_share |
XML 配置解读:
添加到 <devices> 内的配置如下:
<filesystem type="mount" accessmode="passthrough">
<driver type="path"/>
<source dir="/vol1/1000/templates"/>
<target dir="templates_share"/>
</filesystem>
| 属性 |
说明 |
type="mount" |
挂载类型,将宿主机本地目录共享 |
accessmode="passthrough" |
直接使用宿主机文件权限,性能最佳 |
<driver type="path"/> |
使用路径驱动 |
<source dir="..."/> |
宿主机上的共享目录路径 |
<target dir="..."/> |
共享标签名,不是虚拟机内的挂载路径 |
第五步:启动虚拟机
sudo virsh start <虚拟机名称>
第六步:虚拟机内挂载共享目录
通过 SSH 或 VNC 进入虚拟机,执行以下操作:
1. 加载 9p 内核模块(通常已自动加载):
sudo modprobe 9p
sudo modprobe 9pnet
sudo modprobe 9pnet_virtio
2. 创建挂载点:
sudo mkdir -p /mnt/templates
3. 挂载共享目录:
sudo mount -t 9p -o trans=virtio,version=9p2000.L templates_share /mnt/templates
⚠️ 注意:templates_share 是第四步中设置的 SHARE_TAG 标签名,必须与配置中的 <target dir="..."/> 完全一致,否则会报 no channels available 错误。
4. 验证挂载:
ls /mnt/templates
df -h /mnt/templates
mount | grep 9p
正常输出示例:
templates_share on /mnt/templates type 9p (rw,relatime,sync,dirsync,access=client,trans=virtio)
第七步:设置开机自动挂载(可选)
将挂载配置写入 /etc/fstab,实现开机自动挂载:
echo 'templates_share /mnt/templates 9p trans=virtio,version=9p2000.L,_netdev 0 0' | sudo tee -a /etc/fstab
验证 fstab 配置是否正确(不会实际挂载,只检查语法):
sudo mount -a
fstab 参数说明:
| 参数 |
说明 |
templates_share |
设备名(即共享标签) |
/mnt/templates |
虚拟机内的挂载点 |
9p |
文件系统类型 |
trans=virtio |
使用 virtio 传输层 |
version=9p2000.L |
使用 Linux 扩展的 9p 协议 |
_netdev |
延迟挂载,确保 virtio 设备就绪后再挂载 |
accessmode 模式对比
| 模式 |
权限映射 |
性能 |
适用场景 |
passthrough |
直接使用宿主机 UID/GID |
⭐⭐⭐ 最优 |
QEMU 以 root 运行,或路径链权限已正确配置 |
mapped-xattr |
通过扩展属性映射权限 |
⭐⭐ 良好 |
QEMU 以非 root 用户运行,文件系统支持 xattr |
mapped-file |
通过隐藏文件映射权限 |
⭐ 一般 |
文件系统不支持 xattr 时的备选方案 |
如果使用 passthrough 模式遇到权限问题,可以切换为 mapped-xattr:
VM_NAME="ubuntu24-clone-01"
virsh dumpxml $VM_NAME > /tmp/${VM_NAME}.xml && \
sed -i 's/accessmode="passthrough"/accessmode="mapped-xattr"/' /tmp/${VM_NAME}.xml && \
virsh define /tmp/${VM_NAME}.xml && \
rm -f /tmp/${VM_NAME}.xml
共享多个目录
可以同时共享多个目录,每个目录使用不同的标签名。重复第四步,修改变量即可:
# 共享第二个目录
VM_NAME="ubuntu24-clone-01"
SHARE_DIR="/vol1/1000/isos"
SHARE_TAG="isos_share"
virsh dumpxml $VM_NAME > /tmp/${VM_NAME}.xml && \
sed -i "/<\/devices>/i \ <filesystem type=\"mount\" accessmode=\"passthrough\">\n <driver type=\"path\"\/>\n <source dir=\"${SHARE_DIR//\//\\/}\"\/>\n <target dir=\"${SHARE_TAG}\"\/>\n <\/filesystem>" /tmp/${VM_NAME}.xml && \
virsh define /tmp/${VM_NAME}.xml && \
rm -f /tmp/${VM_NAME}.xml
虚拟机内挂载:
sudo mkdir -p /mnt/isos
sudo mount -t 9p -o trans=virtio,version=9p2000.L isos_share /mnt/isos
删除 9p 共享
如果不再需要共享,执行以下步骤:
1. 虚拟机内卸载:
sudo umount /mnt/templates
如果设置了自动挂载,还需删除 fstab 中对应行:
sudo sed -i '/templates_share/d' /etc/fstab
2. 宿主机上移除 9p 设备:
VM_NAME="ubuntu24-clone-01"
virsh dumpxml $VM_NAME > /tmp/${VM_NAME}.xml && \
sed -i '/<filesystem type="mount"/,/<\/filesystem>/d' /tmp/${VM_NAME}.xml && \
virsh define /tmp/${VM_NAME}.xml && \
rm -f /tmp/${VM_NAME}.xml
⚠️ 注意:上面的命令会删除所有 9p 共享。如果只想删除特定的共享,建议使用 virsh edit 手动编辑。
常见问题
Q1: 挂载时报 no channels available for device xxx
原因:标签名不匹配或拼写错误。
解决:确认虚拟机 XML 中 <target dir="..."/> 的值与 mount 命令中的设备名完全一致:
# 查看配置中的标签名
virsh dumpxml <虚拟机名称> | grep "target dir"
Q2: 启动虚拟机报 Permission denied
原因:QEMU 进程无权访问共享目录或其上级目录。
解决:确保整条路径链都有可执行权限:
sudo chmod 755 /vol1 /vol1/1000 /vol1/1000/templates
或者改用 mapped-xattr 模式(参见上方 accessmode 模式对比章节)。
Q3: 虚拟机内无法写入共享目录
原因:passthrough 模式下,虚拟机内用户的 UID/GID 需与宿主机上文件的权限匹配。
解决方案:
- 方案一:在宿主机上对共享目录设置宽松权限
chmod 777
- 方案二:确保虚拟机内用户 UID 与宿主机目录 owner 一致
- 方案三:改用
mapped-xattr 模式
Q4: 9p 性能不如预期
优化建议:
- 挂载时添加
cache=loose 选项提升读取性能(适合只读场景):
sudo mount -t 9p -o trans=virtio,version=9p2000.L,cache=loose templates_share /mnt/templates
- 挂载时添加
msize=262144 增大传输块大小:
sudo mount -t 9p -o trans=virtio,version=9p2000.L,msize=262144 templates_share /mnt/templates