4、虚拟化与存储:AVF (Android Virtualization Framework) 下的存储隔离策略

各位同学,今天我们来聊聊虚拟化环境下的存储问题。说实话,车载系统里搞虚拟化,最头疼的就是存储怎么隔离。你想想看,一个芯片上跑着好几个虚拟机,有跑IVI的,有跑仪表盘的,甚至还有跑ADAS的。它们的数据要是混在一起,那可就乱套了。

Android Automotive从Android 13开始正式引入了AVF(Android Virtualization Framework)。我最早接触这个框架是在一个项目里,客户要求在同一颗SoC上同时运行Android和Linux系统。嗯,那时候踩了不少坑,今天我把这些经验整理出来,希望能帮大家少走弯路。

4.1 AVF存储架构概览

AVF的存储架构,说白了就是「宿主管理、客户隔离」。宿主(Host)拥有物理存储设备的完全控制权,而每个虚拟机(Guest)只能看到分配给它的虚拟磁盘。

我个人习惯把AVF的存储分为三个层次:

  • 物理层:eMMC、UFS或者NVMe,这是真正的硬件
  • 虚拟化层:通过pKVM或crosvm实现的块设备映射
  • 客户层:虚拟机内部看到的/dev/vda、/dev/mmcblk等设备

这里有个关键点:AVF使用的是pKVM(Protected KVM)技术。它和传统的KVM不一样,pKVM在内存和存储层面做了更强的隔离。我在项目中遇到过一个问题,就是客户虚拟机直接访问物理块设备,结果导致宿主系统崩溃。后来才发现,pKVM要求所有块设备访问都必须经过虚拟化层转发。

核心原则:虚拟机永远不能直接操作物理存储设备。所有I/O请求必须通过Virtio-blk或Virtio-fs等虚拟化协议转发。

4.2 存储隔离的三种策略

在实际项目中,我总结出三种常用的存储隔离策略。每种策略都有它的适用场景,我们一个一个来看。

4.2.1 全盘虚拟化(Full Disk Virtualization)

这是最直接的方式。宿主系统为每个虚拟机分配一个独立的磁盘镜像文件,比如qcow2或raw格式。虚拟机看到的就是一个完整的块设备。

// 在宿主系统中创建虚拟磁盘
// 分配一个8GB的qcow2镜像给IVI虚拟机
$ qemu-img create -f qcow2 /data/avf/disks/ivi_disk.qcow2 8G

// 分配一个2GB的qcow2镜像给仪表盘虚拟机
$ qemu-img create -f qcow2 /data/avf/disks/cluster_disk.qcow2 2G

// 启动虚拟机时挂载
$ crosvm run \
    --block /data/avf/disks/ivi_disk.qcow2 \
    --block /data/avf/disks/cluster_disk.qcow2 \
    vmlinux

这种方式的优点是隔离性最强。一个虚拟机再怎么折腾,也影响不到其他虚拟机的数据。但缺点也很明显——存储利用率低。每个虚拟机都要预分配空间,哪怕它只用了一半。

我的建议:如果对安全性要求极高,比如ADAS系统需要存储关键驾驶数据,就用全盘虚拟化。我曾经在一个ADAS项目里,客户要求存储数据不能被任何其他系统读取,全盘虚拟化+加密是最稳妥的方案。

4.2.2 共享文件系统(Shared File System)

有些场景下,多个虚拟机需要共享某些数据。比如IVI系统和仪表盘系统都需要读取导航地图数据。这时候全盘虚拟化就不太合适了,因为数据要拷贝两份。

AVF支持通过Virtio-fs实现文件系统级别的共享。宿主系统暴露一个目录给多个虚拟机,但通过权限控制实现隔离。

// 宿主系统配置共享目录
// 只读共享给所有虚拟机
$ crosvm run \
    --shared-dir /data/maps:maps:type=fs:ro \
    --shared-dir /data/userdata:userdata:type=fs:rw \
    vmlinux

// 在虚拟机内部,挂载共享目录
# mount -t virtiofs maps /mnt/maps
# mount -t virtiofs userdata /mnt/userdata

这里要注意的是权限控制。虽然Virtio-fs支持读写权限设置,但底层的文件系统权限还是要靠宿主系统来保证。我建议在宿主系统层面使用Linux的namespace和cgroup做二次隔离。

避坑指南:我曾经遇到过一个性能问题。当多个虚拟机同时写入共享目录时,Virtio-fs的锁机制会导致严重的性能下降。后来我们改用异步写入+缓存策略才解决。如果你的场景有高并发写入需求,建议先做压力测试。

4.2.3 混合策略(Hybrid Approach)

在实际的车载项目中,我用的最多的其实是混合策略。说白了就是「系统分区隔离,数据分区共享」。系统分区用全盘虚拟化保证安全,数据分区用共享文件系统提高效率。

分区类型 隔离策略 典型用途 性能要求
系统分区 全盘虚拟化 /system, /vendor, /product 中等
数据分区 共享文件系统 /data, /cache
日志分区 全盘虚拟化 /logs
媒体分区 共享文件系统 /media, /maps

你看这个表格,系统分区用全盘虚拟化,是因为系统文件一旦被篡改,整个系统就废了。而媒体分区用共享文件系统,是因为地图数据、音乐文件这些,多个虚拟机都需要访问,而且数据量很大。

4.3 存储性能优化

说到性能,这是虚拟化存储的老大难问题。我在一个项目中,客户抱怨虚拟机启动速度比原生系统慢了3倍。查了半天,发现是磁盘I/O路径太长了。

AVF下的一次写操作,要经过这么几个步骤:

  1. 虚拟机应用发起write()系统调用
  2. 虚拟机内核的ext4文件系统处理
  3. Virtio-blk驱动封装请求
  4. Virtio环传输到宿主系统
  5. 宿主系统的Virtio-blk后端处理
  6. 宿主内核的文件系统或块设备层
  7. 最终写入物理存储

嗯,整整7步。每一步都有开销。怎么优化?我分享几个实战经验:

  • 使用IO线程池:在crosvm配置中增加io线程数,默认是1个,建议改成CPU核心数
  • 开启异步IO:Virtio-blk支持异步模式,能大幅提升吞吐量
  • 调整缓存策略:writeback模式比writethrough快,但数据安全性差一些
// 优化后的crosvm启动配置
$ crosvm run \
    --block /data/avf/disks/system.qcow2,async=on,cache=writeback \
    --block /data/avf/disks/data.qcow2,async=on,cache=writeback \
    --io-threads 4 \
    vmlinux

一个小技巧:如果你的虚拟机对存储延迟特别敏感,比如仪表盘系统需要快速读写日志,可以考虑使用内存盘(tmpfs)作为临时存储。虽然重启后数据会丢失,但性能提升非常明显。我在一个项目中用这个方案,把日志写入延迟从50ms降到了1ms以内。

4.4 安全与加密

车载系统的存储安全,现在越来越受重视。特别是AVF场景下,多个虚拟机共享同一块物理存储,加密就变得尤为重要。

AVF支持两种加密方式:

  • 文件级加密(FBE):每个文件独立加密,密钥由宿主系统管理
  • 块级加密(dm-crypt/LUKS):整个虚拟磁盘加密,性能开销较大

我个人更推荐FBE。为什么呢?因为车载场景下,不同虚拟机的数据敏感度不一样。比如IVI系统的导航数据可以不用加密,但ADAS系统的驾驶数据必须加密。FBE可以做到精细化控制。

// 在虚拟机内部启用FBE
// 设置加密策略
$ vdc cryptfs enablecrypto inplace default

// 查看加密状态
$ vdc cryptfs cryptocomplete

// 为特定目录设置加密密钥
$ vdc keymaster add_key --key-type=aes-256-gcm \
    --key-path=/data/encryption_keys/adas.key

注意:加密会带来性能损耗。我在一个项目中测试过,启用FBE后,随机读写性能下降了约15%。如果你的系统对性能要求极高,建议只对敏感数据分区启用加密,不要全盘加密。

4.5 实战:配置一个安全的AVF存储方案

好了,理论讲完了,我们来动手配置一个实际方案。假设我们要在一个车载平台上运行三个虚拟机:IVI、仪表盘和ADAS。

我的设计思路是这样的:

  1. IVI系统:全盘虚拟化,不加密,性能优先
  2. 仪表盘系统:全盘虚拟化+FBE加密,安全与性能平衡
  3. ADAS系统:全盘虚拟化+块级加密,安全第一
  4. 共享地图数据:Virtio-fs只读共享
// 宿主系统配置脚本
#!/bin/bash

# 创建虚拟磁盘
qemu-img create -f qcow2 /data/avf/ivi_disk.qcow2 16G
qemu-img create -f qcow2 /data/avf/cluster_disk.qcow2 4G
qemu-img create -f qcow2 /data/avf/adas_disk.qcow2 8G

# 启动虚拟机
crosvm run \
    --block /data/avf/ivi_disk.qcow2,async=on,cache=writeback \
    --block /data/avf/cluster_disk.qcow2,async=on,cache=writeback \
    --block /data/avf/adas_disk.qcow2,async=on,cache=writethrough \
    --shared-dir /data/maps:maps:type=fs:ro \
    --io-threads 4 \
    --mem 4096 \
    vmlinux

你看,ADAS的磁盘我用了writethrough模式,虽然慢一点,但数据安全性更高。而IVI和仪表盘用writeback,性能更好。

嗯,这就是AVF下存储隔离的核心内容。说实话,虚拟化存储这块水很深,每个项目都有自己的特殊需求。但只要你掌握了全盘虚拟化、共享文件系统和混合策略这三种基本模式,再结合加密和性能优化手段,大部分场景都能应对。

下一章我们会聊聊OTA升级时的存储管理策略,那又是另一个有意思的话题了。