磁盘文件打开过程
通过gdb追踪openat系统调用得知,raw格式文件会使用raw_open_common()
函数打开,调用栈如下:
通过分析源码得到qemu打开磁盘文件的过程:QEMU使用一个BlockDriverState结构体来管理块设备驱动信息,其中就包括了操作和数据。块设备创建的关键就在于初始化BlockDriverState结构体。
1 | struct BlockDriverState { |
drv
成员表示该设备使用的驱动,提供对设备读、写、探测等的操作方法;opaque
成员表示具体格式的数据,会在具体的操作函数中类型转换成需要的信息。如:指定以raw格式创建磁盘文件,则drv
成员中将是raw
相关的操作函数;而opaque
将执行raw
格式数据相关的结构体,即BDRVRawState
。这样有了BlockDriverState结构就能对特定格式的文件(.opaque
)使用特定的驱动(.drv)
了。
其中初始化BlockDriverState结构的主函数是bdrv_open_inherit()
。由于qemu持支多种协议访问文件,如使用nbs协议访问网络块设备(-drive nbs:file=xxx
),使用file协议访问本地文件(-drive file=xxx
)等。所以在bdrv_open_inherit()
会解析命令行指定的协议类型、解析指定的文件格式等来找到正确的驱动。
1 | static BlockDriverState *bdrv_open_inherit() { |
bdrv_open_inherit
中会通过尝试解析命令行字符串的方式找到对应的format和protocol,也会通过读取文件内容做解析的方式查找:
1 | static int find_image_format(){ |
上面的代码就展示了它读取文件内容做探测的过程。
这里只考虑qemu打开本地文件作为块设备的方法,即使用file协议创建块设备的方法。
bdrv_open_inherit()
中会调用bdrv_open_common()
来初始化BlockDriverState的.opaque
成员。
1 | static int bdrv_open_common(...){ |
首先解析命令行参数,使用bdrv_fine_format
找到对应的驱动drv
,之后在bdrv_open_driver
中使用驱动drv
对文件进行读、写、打开等操作。
1 | static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, |
以打开raw格式的文件纹理在bdrv_open_driver
中调用了raw_open_common()
回调函数对文件进行打开,对应上面的drv->bdrv_file_open()
。在raw_open_common()
中就用了raw格式的打开方法,从而创建BlockDriverState的opaque
成员。
1 | static int raw_open_common(BlockDriverState *bs, QDict *options, |
至此BlockDriverState结构初始化完毕,驱动由drv
成员管理,设备的数据由opaque
成员管理。
BlockDriverState中的opaque
成员是个void *
的类型,操作时由drv
指向的驱动转换为正确的类型。如上面说的,raw格式文件用BDRVRawState
结构存储,同理vmdk类型的文件对应的就是vmdk的driver:BlockDriver bdrv_vmdk
,再由driver取出需要的类型:
1 | vmdk_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, |
使用uImage启动和我们的启动方式的差异
uImage是专门为uboot设计的一种镜像格式。
1 | $ file uImage |
可以使用uboot的bootm
命令启动存在内存中的镜像。命令格式为bootm <img> <ramdisk> <fdt>
启动ramdisk
表示ramdisk的位置,fdt
表示设备树文件的位置,这两个参数是可以省略的,可以传递-
表示参数为空。