一、virtio概述
virtio是一种轻量级的虚拟化I/O框架,它的核心思想是将设备的复杂功能转移到虚拟机监控程序(VMM)上。virtio将外部设备的访问逻辑封装在虚拟机监控程序内部,只向虚拟机交付一组简单而统一的接口。这些接口可以看作是一种通用的虚拟IO设备规范。
virtio规范主要包括以下几种类型的设备:
- 块设备(blk):提供块级别的读写操作。
- 网卡设备(net):提供网络通信服务。
- 内存设备(mem):提供直接内存访问等服务。
- 控制台设备(console):提供与虚拟机控制台的通信服务。
virtio驱动负责在虚拟机中对这些设备的操作进行处理,以及和宿主机的交互。
二、virtio驱动实现
1. virtio设备的表示
在Linux系统中,virtio设备表示为一组及其描述符。描述符中包含了virtio设备的各种信息,通过它可以实现virtio设备数据的读写等操作。驱动通过描述符寻找到相应的设备,并进行控制和管理。
2. 设备的初始化
虚拟机在运行时,virtio驱动会被加载到内核中,并和virtio设备进行初始化连接的过程。主要过程包括以下几个环节:
- 读取VM的配置信息。
- 填充设备描述符信息。
- 将设备描述符传递到虚拟机中,随后虚拟机完成设备初始化的过程。
虚拟机完成设备初始化后,virtio驱动可以开始与设备进行交互。
3. 数据处理
在进行数据处理时,virtio驱动主要负责两个过程:读取数据和写入数据。具体过程如下:
- 读取数据:首先,驱动程序发送对应的读取请求给virtio设备。虚拟机完成数据的读取后,驱动会将数据从虚拟机中读出并发送到应用程序中。
- 写入数据:在写入数据的时候,应用程序会将数据发送给virtio驱动,随后驱动会将数据发送给虚拟机并请求写入。
三、代码示例
static struct virtio_driver virtio_blk_driver = { .name = "virtio_blk", .id_table = id_table, .features = features, .feature_table_size = ARRAY_SIZE(features), .probe = virtblk_probe, .remove = virtblk_remove, }; module_virtio_driver(virtio_blk_driver);
struct virtio_device *vdev; struct virtqueue *vq; int rc; char buf[512]; /* 首先初始化device */ vdev = virtio_find_device(&virtio_blk_driver, PCI_VENDOR_ID_REDHAT_QUMRANET, VIRTIO_BLK_DEVICE_ID, NULL); /* 如果没有找到可用的virtio设备就退出 */ if (!vdev){ printk(KERN_ERR "virtio_blk: no devices found!\n"); return -ENODEV; } /* 使用模块提供的方法进行请求队列的初始化 */ rc = virtqueue_initialize(vdev, 0, virtblk_request); /* 如果返回值小于0,则表示初始化失败 */ if (rc < 0){ printk(KERN_ERR "virtio_blk: can not initialize virtqueue\n"); ret = -ENODEV; goto error; } /* 初始化成功,则取出请求队列 */ vq = vdev->vqs ? vdev->vqs[0] : NULL; if (!vq) { printk(KERN_ERR "virtio_blk: NO vq\n"); goto error; } /* 将写入的数据添加到队列中去 */ struct scatterlist sg_list; sg_init_one(&sg_list, buf, sizeof(buf)); struct virtio_blk_req *req = kzalloc(sizeof(struct virtio_blk_req), GFP_KERNEL); virtblk_setup_req(req, VIRTIO_BLK_T_IN, 0, &sg_list, 0); virtqueue_push(vq, req, 0, GFP_KERNEL);