本篇文章为大家展示了如何进行virtio的分析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
virtio
virtio是一个通用的io虚拟化框架,hypervisor通过他模拟出一系列的虚拟化设备,并使得这些设备在虚拟机内部通过api调用的方式变得可用。它为客户机提供了一个高效访问块设备的方法。它包含4个部分:前端驱动、后端驱动、vring及通信间统一的接口。与其他的模拟io方式对比,virtio减少了虚拟机的退出和数据拷贝,能够极大地提高IO性能。计算机中存在不同的总线标准,而virtio采用的是pci总线(当然也可以用其他总线来实现)。每一个virtio设备就是一个pci设备。
virtio-blk的后端初始化
virtio-blk代码包保存在hw/virtio-pci.c和hw/virtio-blk.c中,通过如下函数对virtio_blk进行初始化。主要的初始化函数是virtio_blk_init_pci。这里定义了设备的信息。
static void virtio_blk_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->init = virtio_blk_init_pci; //virtio-blk初始化函数 k->exit = virtio_blk_exit_pci; k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; //设备厂商号,所有的virtio设备都为0x1af4 k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; //设备号 k->revision = VIRTIO_PCI_ABI_VERSION; //virtio ABI版本号 k->class_id = PCI_CLASS_STORAGE_SCSI; dc->reset = virtio_pci_reset; dc->props = virtio_blk_properties; //virtio-blk设备所支持的特征 } static TypeInfo virtio_blk_info = { .name = "virtio-blk-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VirtIOPCIProxy), .class_init = virtio_blk_class_init, //virtio-blk设备类型初始化函数 };
virtio_blk后端数据结构如下
virtio-blk首先是一个pci设备,初始化主要分两个阶段:pci设备初始化和设备初始化
以下是它初始化的几个阶段:
PCI设备探测和初始化
虚拟机启动时,bios和系统会扫描pci总线,看看上面有没有挂载的pci设备。如果有,则会创建一个pci_dev结构。一个pci设备用一个pci_dev数据结构表示,创建之后会用pci设备配置空间信息来填充pci_dev,然后调用device_register来将其注册到pci总线上。PCI总线的match和probe函数根据pci_dev数据结构中的Vendor ID和Device ID将设备与注册在PCI总线上的驱动进行匹配,进而匹配到了所有virtio设备所共用的PCI驱动virtio_pci_driver。
static struct pci_device_id virtio_pci_id_table[] = { { 0x1af4, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0 }, }; // PCI_ANY_ID表示匹配任何设备ID static struct pci_driver virtio_pci_driver = { .name = "virtio-pci", //驱动名称 .id_table = virtio_pci_id_table, //驱动所支持的设备ID信息 .probe = virtio_pci_probe,//探测函数(负责PCI设备初始化和进一步的virtio设备探测) .remove = virtio_pci_remove,//设备移除时的处理函数 #ifdef CONFIG_PM .driver.pm = &virtio_pci_pm_ops, //电源管理函数 #endif };
virtio设备的探测和初始化
virtio_pci_driver是该阶段的关键函数,具体流程如下
前端驱动读取io请求放入vring
前端通过notify通知机制通知后端驱动处理io
notify操作使vcpu执行线程退出到qemu应用层,其从vring中获取客户机io请求信息,将请求线程放入aio线程池,然后vcpu线程的处理流程重新返回到客户机
aio线程处理完成后,通知主线程,并向客户机注入中断说明其已完成io操作
客户机相应中断,并获取io请求结果和处理信息,接着继续向上层返回结果
客户机io请求流程
1、读写操作通过系统调用进入到操作系统内核层,首先到达VFS层
2、VFS层继续向下层传递请求,如果页高速缓存命中,而文件又不是直接读写,则IO请求在页高速缓存得到处理
3、如果没有页高速缓存或者页高速缓存MISS,则进入到Mapping Layer(映射层),在这一层主要是根据文件系统信息,解决文件偏移量与块设备中的的逻辑块号的映射,并根据映射将请求下发到Generic Block Layer(通用块层)
4、在通用块层,IO读写请求由struct bio表示,通用块层继续将请求下发到IO Scheduler Layer(IO调度层)
5、在IO调度层,上层传递下来的bio将根据类型、以及逻辑块是否靠近等因素进行调度,最终形成一个req(struct request类型),req将被链接到设备的request_queue中
6、IO调度层继续将请求下发,请求到达了块设备驱动,块设备驱动从request_queue中取下一个个请求进行处理。
上述内容就是如何进行virtio的分析,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。