调研在Andriod上实现对VHD虚拟磁盘的挂载的可行性(两个方案都可以成功挂载支持读写但写入性能损耗严重高达80%)

 初始目标:实现在Andriod上对VHD虚拟磁盘进行挂载,目的是将在手机上取证得到的数据(几百-几万的小文件)先存储到Andriod上挂载的VHD虚拟磁盘这个大文件里,到windows上就能直接挂载VHD虚拟磁盘读写,比原先的到windows上再释放数以千计的小文件速度要快很多。

技术难点:Android上首先并不支持直接挂载VHD虚拟磁盘,其次我们取证得到的文件往磁盘里写需要多线程进行,基于提升速度的需求,那么Andriod上实现的读写VHD的写入速率需要达到要求。

方案1:libguestfs

https://gitcode.com/gh_mirrors/li/libguestfs?utm_source=csdn_github_accelerator&isLogin=1

这是最初找到的一个用于访问和修改虚拟机磁盘映像的库和工具。里面包含了支持对vhd磁盘的读写功能,当时的方案是先在linux上编译并实现功能在考虑向安卓移植。

 linux上编译过程中发现这个库非常庞大,支持的虚拟磁盘格式非常多,所以决定先编译 编译完再进行阉割,把vhd这个虚拟磁盘需要的部分抽离出来,结果就是光编译就花费了两天时间,其中需要的依赖非常繁多并且有很多坑(readme里有部署文档,以后这个库或许还有利用价值),编译成功后,

挂载流程:

export LD_LIBRARY_PATH=/home/kali/Desktop/vhdx/libguestfs/lib/.libs:$LD_LIBRARY_PATH

libguestfs 需要 supermin appliance(一个虚拟的 Linux 环境),如果 appliance 目录缺失,会报错:

sudo apt-get install supermin

cd /home/kali/Desktop/vhdx/libguestfs

make -j$(nproc) applianc

export LIBGUESTFS_PATH=/home/kali/Desktop/vhdx/libguestfs/appliance


sudo guestfish -a /home/gs/1.vhdx
然后在 guestfish> 提示符下输入:
run

list-filesystems
这将列出虚拟磁盘中的文件系统。

选择你要访问的分区,例如 /dev/sda1,运行:
mount /dev/sda1 /

完成操作后,先卸载:
umount /
然后退出 guestfish:
exit

在挂载过程中,我们发现了问题,就是libguestfs似乎是挂载在一个虚拟的linux环境下,在linux的实际目录中这个挂载并不存在,虽然依然可以对这个vhd进行读写,但是在这样的虚拟环境下多线程和速度几乎都难以达标,并且在移植安卓过程中会遇到更多问题,接着对代码分析,发现代码中似乎没有对vhd进行处理的部分,于是详细调研后发现libguestfs更多是基于qemu来实现的,提供的更多是命令行参数来调用qemu,于是我们将研究的重心转移到了qemu。

方案2:qemu-nbd (NBD:Network Block Device(网络块设备))

在调研最初,曾经使用qemu-img 创建和转换过vhd的格式,所以对于qemu能够对vhd虚拟磁盘操作是有信息的,在重心转移后很快就发现了qemu-nbd可以挂载vhd虚拟磁盘,于是在linux编译了qemu库和nbd-cilent等依赖

//编译
git clone --depth 1 https://github.com/qemu/qemu.git
cd qemu
git submodule update --init
./configure
make
sudo make install

//挂载

sudo modprobe nbd max_part=16 //加载16个nbd模块
sudo qemu-nbd --connect=/dev/nbd0 /home/kali/Desktop/vhdx/test.vhdx //挂载
sudo fdisk -l /dev/nbd0 //检查分区信息
sudo mount /dev/nbd0p1 /mnt/guestfs //发现nbd0p1是我们要的ntfs分区 接着挂这个ntfs分区

//卸载并断开
sudo umount /mnt/guestfs
sudo qemu-nbd --disconnect /dev/nbd0

初步挂载后发现这个方式挂载后非常直观,令人遐想成功就在不远处。

由于直接挂载成了实体机的文件系统,所以多线程可以不用考虑必然可行,(但当时发生了极大失误,没有在linux里测试写入性能,虽然性能损失早有预料,但是损失达到80%是在认知外,导致了没能及时止损)

接下来我们开始准备对Andriod移植qemu-nbd功能,前期的调研我们已知qemu在Andriod上可用但nbd服务安卓上处于关闭状态。于是尝试在安卓内核中的config里手动打开nbd服务,再重新编译,事实上该方案可行并成功编译安卓内核启用nbd服务,接下来尝试利用安卓内现有的qemu库来测试qemu-nbd(这时候我已经被发配去研究fuse了,这部分细节不清楚),结果就是成功用qemu-nbd在安卓挂载了,挂载的命令和linux内相似,最后测试写入速度,

dd if=/dev/random of=/data/local/tmp/mnt/has-content/test123.txt bs=512 count=40960

1mb/s 损失90%, 当然 这里的每次写入的设置量比较小,但是即使调高bs值,速度变快了,但是损失的比例仍然很高。

方案3:fuse

FUSE(Filesystem in Userspace) 是一个用于在用户空间(User Space)创建文件系统的接口,主要用于 Linux 和 Unix 系统。它允许非特权用户在无需修改内核的情况下实现自定义文件系统,并通过 libfuse 库与内核交互

https://github.com/libfuse/libfuse/tree/master/example 

将libfuse里的实例在linux环境下编译,主要是两个实例,hello.c和cuse.c。两个实例分别是可以将磁盘映射成文件和字符设备。

示例1:hello.c
gcc -o fusexmp hello.c $(pkg-config --cflags --libs fuse3)

如果编译成功,你可以运行:
mkdir mountpoint
./fusexmp mountpoint
这将在 mountpoint 目录挂载一个 FUSE 文件系统(hello文件)。

示例2:cuse.c

gcc -Wall cuse.c $(pkg-config --cflags --libs fuse3) -o cuse
以 root 权限运行
sudo ./cuse -M 240 -m 0 --name=mydevice
创建 /dev/mydevice 设备,major 号 240,minor 号 0。
首先检查 /dev/mydevice 是否存在:
ls -l /dev/mydevice
如果 /dev/mydevice 存在,但 ls -l 显示:
crw-r--r-- 1 root root 240, 0 Mar 10 10:00 /dev/mydevice
表示 只有 root 用户有读权限,但没有写权限。
你可以手动添加写权限:
sudo chmod 666 /dev/mydevice
然后再尝试写入:
echo -n "Hello from CUSE\n" | sudo tee /dev/mydevice

发现两个示例代码的共同点,在 hello.c 中,.read .open .wite这些接口被放入 fuse_operations 结构体中,在 cuse.c 中,这些接口被放入 cuse_lowlevel_ops 结构体,所以我们想到找到vhd的库,将这个 VHD 读写逻辑抽离,并结合 FUSE 来实现一个挂载接口。达到对vhd虚拟磁盘操作的目的,并在linux里交叉编译。需要用到的交叉编译代码有如下:

这部分代码在https://github.com/xapi-project/blktap/tree/master/vhd/lib

自己编写了结合后的fuse代码 vhd_mount_fuse.c

然后交叉编译 

gcc -Wall -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE \
    vhd_fuse_mount.c libvhd.c canonpath.c relative_path.c xattr.c \
    $(pkg-config --cflags --libs fuse3) -lguestfs -luuid -lattr -o vhd_fuse
编译成功后生成vhd_fuse这样一个可执行文件 然后

sudo vhd_fuse -o allowother ~/mnt1 -f 

把vhd磁盘映射到~/mnt 文件下 

sudo hexdump -C ~/mnt1 | more 该命令还可以检查当前该挂载点里的设备以16进制显示出来

然后将该文件用loop映射成为一个块设备

sudo losetup -fP --show ~/mnt1 会显示loop的设备号

fdisk -l /dev/loop2再检查块设备下的分区 找到我们需要进行读写的分区例如 loop2p2

然后挂载loop2p2分区到一个路径下并尝试测试向里面写入文件的速度

mount一下 发现挂载到了 /media/kali/新加卷上 接着

dd if=/dev/zero of=/media/kali/新加卷/testfile1 bs=4096 count=40960 进行速度测试

再直接对系统进行速度测试

dd if=/dev/zero of=~/Desktop/testfile1 bs=4096 count=40960 发现性能损耗高达80%

 发现损耗过高后尝试分别把fuse和libvhd分开在andriod上编译并测试损耗是出现在哪一部分

https://blog.youkuaiyun.com/u011376563/article/details/123229520

经测试,即使不通过vhd 在三星S21上 用fuse读文件的性能损失近90%

由此可见 fuse的性能非常差,经过讨论分析,得出fuse性能损耗严重的原因:FUSE(Filesystem in Userspace)在用户态运行,它的性能瓶颈主要来自 用户态-内核态切换、额外的拷贝操作、单线程模型、缓存处理等。相比内核文件系统(如 ext4、xfs),FUSE 在某些场景下性能可能下降 5~10 倍,甚至更多。由于用户态和内核态之间多次的切换,性能损耗不可避免,此方案似乎没有解决的空间。

在fuse方案的失败后,又尝试过考虑能否直接通过修改ntfs-3g(andriod和linux中对于ntfs格式磁盘操作的一个库)里的读写接口来对vhdx进行操作,看了ntfs-3g的源码,判断大概率也依赖fuse架构来实现,也在用户层操作而非内核层,预估效率远低于ext4格式,这次吸取教训,优先测试在andriod里直接用ntfs-3g挂载磁盘后写入的效率,

用ntfs-3g挂载磁盘,首先确保测试磁盘.raw里是否有ntfs分区,由于ntfs-3g无法直接挂载dd磁盘,只能挂载块设备或者分区,挂载块设备则需要通过losetup指令先将dd磁盘映射到loop上再进行挂载,中间多了一层映射担心影响性能导致测试结果不准确,于是通过

sudo fdisk -l /home/kali/Desktop/vhdx/test.raw 命令找到ntfs分区起始偏移量

接着使用sudo ntfs-3g -o offset=65536 /home/kali/Desktop/vhdx/test.raw /mnt/ntfs_test
偏移到ntfs分区来进行挂载

用上面同样的方式分别测试写入磁盘速度和写入安卓本地速度

损耗高达90%,由此可以推论,Andriod中ntfs-3g直接对vhdx虚拟磁盘进行读写操作的效率瓶颈也十分严重,再详细研究源码后,得出结论:

NTFS在Linux/Android上的优化很少,ntfs-3g主要是为兼容WindowsNTFS分区而设计的,而不是为了性能优化。没有写缓存,每次写入都会经过fuse层,效率比ext4低得多。Android可能没有启用direct_io,ntfs-3g默认运行在非内核模式,所有I/O操作都会有额外的系统调用。

想要解决性能瓶颈的问题,较为困难,需要对Andriod内核进行改动或者对写缓存机制进行修正,暂时未能有可靠方案支撑。

如果有可实现的方案欢迎指导。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值