基于virtio-serial的虚拟机和主机数据传输机制
virtio是Linux虚拟机平台上统一的虚拟IO接口驱动。通常主机为了让客户机像在真实环境中一样运行,需要为客户机创建各式各样的虚拟设备,如磁盘,网卡,显卡,时钟,USB 等。这些虚拟设备大大降低了客户机的性能。使用virtio虚拟机guest不用关注如何创建各种虚拟硬件设备(如磁盘,网卡,显卡等),可以用统一的虚拟设备,因此大大提高虚拟机的性能,这个统一的虚拟设备就是virtio。本文会...
virtio是Linux虚拟机平台上统一的虚拟IO接口驱动。通常主机为了让客户机像在真实环境中一样运行,需要为客户机创建各式各样的虚拟设备,如磁盘,网卡,显卡,时钟,USB 等。这些虚拟设备大大降低了客户机的性能。使用virtio虚拟机guest不用关注如何创建各种虚拟硬件设备(如磁盘,网卡,显卡等),可以用统一的虚拟设备,因此大大提高虚拟机的性能,这个统一的虚拟设备就是virtio。
本文会侧重于virtio一个有趣的应用:如何使用virtio在虚拟机guest和主机host之间传递消息,对于KVM一般首先考虑的是使用socket在host和guest之间传递消息,但使用virtio传递消息有两点优势:
- 对虚拟机和主机的网络设置没有任何要求
- 效率更高
本文是基于VirtioSerial在主机和虚拟机之间传递消息, virtio-serial已经用在了很多的开源项目中,比如:Matahari, Spice, libguestfs和Anaconda等。下面就简述如何在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;
}
参考资料:
更多推荐
所有评论(0)