virtio是Linux虚拟机平台上统一的虚拟IO接口驱动。通常主机为了让客户机像在真实环境中一样运行,需要为客户机创建各式各样的虚拟设备,如磁盘,网卡,显卡,时钟,USB 等。这些虚拟设备大大降低了客户机的性能。使用virtio虚拟机guest不用关注如何创建各种虚拟硬件设备(如磁盘,网卡,显卡等),可以用统一的虚拟设备,因此大大提高虚拟机的性能,这个统一的虚拟设备就是virtio。


本文会侧重于virtio一个有趣的应用:如何使用virtio在虚拟机guest和主机host之间传递消息,对于KVM一般首先考虑的是使用socket在host和guest之间传递消息,但使用virtio传递消息有两点优势:

  1. 对虚拟机和主机的网络设置没有任何要求
  2. 效率更高

本文是基于VirtioSerial在主机和虚拟机之间传递消息, virtio-serial已经用在了很多的开源项目中,比如:Matahari, Spice, libguestfsAnaconda等。下面就简述如何在virtio中构建虚拟机和主机之间数据传输的机制。

一.  通过libvirt在虚拟机创建时启动virtio通道

使用libvirt在虚拟机的启动配置xml中加入:

<channel type='unix'>
 <source mode='bind' path='/var/lib/libvirt/qemu/vm.data'/>
 <target type='virtio' address='virtio-serial' port='2'/>
</channel>

注意:path路径要放在qemu下,不然会报Failed to bind socket: Permission denied错误

如何虚拟机已经运行,则先关闭虚拟机,再修改配置文件:

# virsh destroy vm2
# vi /etc/libvirt/qemu/vm2.xml 
# virsh define /etc/libvirt/qemu/vm2.xml 
# virsh start vm2

虚拟机启动后,在虚拟机操作系统中可以发现新的字符设备:

# ls -l  /dev/vport0p2
crw-rw----. 1 root root 247, 2 Aug 14 18:14 /dev/vport0p2

虚拟机中对这个字符设备的读写操作即相当于对virtio通道的读写,以此可以实现与主机的通信。

2. 虚拟机端程序

在上面xml定义的应用场景,虚拟机中会发现一个新的字符设备,对这个设备进行读写即可完成与主机的通信,下面是我写的一个简单的例子:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

#define CONTROL_PORT "/dev/vport0p2"

int main(void) {

	int fd;
	char * buf= "Test";
	int len, ret;
	
	fd = open(CONTROL_PORT, O_RDWR);
	if (fd == -1) {
		fprintf(stderr, "can't open vport\n");
		return -1;
	}
	
	ret = write(fd, buf, sizeof(buf));
		if (ret == -1) {
			fprintf(stderr, "can't write data to vport\n");
			return -1;
		}
	
	ret = close(CONTROL_PORT);
	if (ret < 0) {
		fprintf(stderr, "can't close vport\n");
		return -1;
	}

	return 0;
}

3. 主机端程序

由于virtio通道通过libvirt启动配置xml中的相关定义,在host本地映射为一个文件,主机程序可以通过unix套接字的方式对virtio通道进行读写,实现与虚拟机的消息数据传递:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>

#define PORT "/var/lib/libvirt/qemu/vm.data"

int main(void) {
	
	int sock_fd;
	struct sockaddr_un sock;
	char buf[128];
	int ret;

	sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if ( sock_fd == -1 ) {
		fprintf(stderr, "Error: Socket can not be created !! \n");
        fprintf(stderr, "errno : %d\n", errno);
		return -1;
	}

	sock.sun_family = AF_UNIX;
	memcpy(&sock.sun_path, PORT, sizeof(sock.sun_path));
	ret = connect(sock_fd, (struct sockaddr *)&sock, sizeof(sock));
	if ( ret == -1) {
		fprintf(stderr, "Connect Failed!\n");
		return -1;
	}
	
	read(sock_fd, buf, 128);
	printf("%s\n",buf);

	close(sock_fd);

	
	return 0;
}

 


参考资料:

1. 基于virtio在虚拟机与主机之间传递消息

2. Communication between Guests and Hosts

3. http://www.linux-kvm.org/page/VMchannel_Requirements

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐