磁盘文件打开过程
通过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表示设备树文件的位置,这两个参数是可以省略的,可以传递-表示参数为空。