vsock 开发实践-virtio-vsock

vsock 开发实践-virtio-vsock

一、背景

我们知道在不使用tcp/ip通信的情况下,传统实现guest和host之间的通信主要是基于virtio-serial 串口通信,但是virtio-serial 有一定的局限性。

特性Virtio-serialVirtio-vsock
底层机制基于虚拟串口(字符设备),模拟传统物理串口基于套接字(Socket),共享内存直接通信
数据传输模式流式传输(类似字节流)面向消息的数据包传输
寻址方式通过设备路径(如 /dev/vport0p1通过 CID(Context ID)和端口号寻址
典型延迟较高(依赖虚拟中断机制)低至微秒级(绕过网络栈)
适用场景对比
场景推荐技术理由
虚拟机控制台交互Virtio-serial兼容性强,适合低频次命令行操作
实时日志采集Virtio-vsock高吞吐、低延迟,避免日志堆积
热迁移元数据同步Virtio-vsock减少网络抖动对迁移过程的影响
容器与 Host 通信Virtio-vsock适配 Kata Containers 等轻量级沙箱架构
传统设备模拟Virtio-serial模拟串口/打印机等设备无需协议改造

总的来说,virtio-serial 对内核无要求,兼容性更强且稳定,但是性能低,适合于小规模数据传输的通用场景下使用。而virtio-vsock 对内核有要求,更适合于高吞吐低延迟对性能要求更高的场景下。

二、安装vsock

使用 virtio-vsock 对hypervisor以及guest 有一定的限制要求,两者的kernel 都必须 4.8+ ,同时qemu 2.8+

  • Host kernel requirements: CONFIG_VHOST_VSOCK=m
  • Guest kernel requirements: CONFIG_VIRTIO_VSOCKETS=m
2.1 宿主机

加载vhost_vsock内核模块

hypervisor # cat /boot/config-`uname -r` | grep -i VHOST_VSOCK
hypervisor #modprobe vhost_vsock
hypervisor #ls -l /dev/vhost-vsock
crw------- 1 root root 10, 53 May  4 11:55 /dev/vhost-vsock
hypervisor # ls -l /dev/vsock
crw-rw-rw- 1 root root 10, 54 May  4 11:55 /dev/vsock
2.2 qemu启动

当前主流虚拟化平台(如 QEMU 2.8+ 和 Libvirt 4.4+)已原生支持 vhost-vsock-pci 设备,适用于云原生和高性能通信场景。

libvirt 配置vsock device

A vsock host/guest interface. The model attribute defaults to virtio. Since 5.2.0 model can also be ‘virtio-transitional’ or ‘virtio-non-transitional’, see virtio device models for more details. The optional attribute address of the cid element specifies the CID assigned to the guest. If the attribute auto is set to yes, libvirt will assign a free CID automatically on domain startup. Since 4.4.0 The optional driver element allows to specify virtio options, see Virtio-related options for more details. Since 7.1.0

<devices>
  <vsock model="virtio">
    <!-- 自动分配 Guest CID -->  
    <cid auto="yes"/>
    <!-- 可选:绑定固定 CID(需全局唯一) -->  
    <!-- <cid address="3"/> -->
  </vsock>
</devices>

qemu 启动加上 vhost-vsock-pci 参数, 并指定虚机唯一的cid编号

hypervisor #sudo qemu-system-x86_64 -m 4G -hda /home/matt/ubuntuvm0.img \
             -device vhost-vsock-pci,id=vhost-vsock-pci0,guest-cid=3 -vnc :0 \
             --enable-kvm
2.3 虚机侧

qemu 启动的虚机理应已经有vsock device,检查一下 vsock

(上文提到需要内核4.8+, 故CentOS8下能看到而CentOS7 看不到)

vm # cat /boot/config-`uname -r` | grep -i VIRTIO_VSOCK
vm # ls -l /dev/vsock
crw-rw-rw- 1 root root 10, 55 May  4 13:21 /dev/vsock
三、Host/Guest 通信开发

启动一台 CentOS 8.2 x86_64 的虚拟机用于开发测试

3.1 hypervisor/server端
python语言
#!/usr/bin/env python3

import socket

CID = socket.VMADDR_CID_HOST
PORT = 9999

s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
s.bind((CID, PORT))
s.listen()
(conn, (remote_cid, remote_port)) = s.accept()

print(f"Connection opened by cid={remote_cid} port={remote_port}")

while True:
    buf = conn.recv(64)
    if not buf:
        break

    print(f"Received bytes: {buf}")
c 语言
#include <sys/socket.h>
#include <linux/vm_sockets.h>
#include <string.h>
#include <stdio.h>

int main()
{
	int s = socket(AF_VSOCK, SOCK_STREAM, 0);

	struct sockaddr_vm addr;
	memset(&addr, 0, sizeof(struct sockaddr_vm));
	addr.svm_family = AF_VSOCK;
	addr.svm_port = 9999;
	addr.svm_cid = VMADDR_CID_HOST;

	bind(s, &addr, sizeof(struct sockaddr_vm));

	listen(s, 0);

	struct sockaddr_vm peer_addr;
	socklen_t peer_addr_size = sizeof(struct sockaddr_vm);
	int peer_fd = accept(s, &peer_addr, &peer_addr_size);

	char buf[64];
	size_t msg_len;
	while ((msg_len = recv(peer_fd, &buf, 64, 0)) > 0) {
		printf("Received %lu bytes: %.*s\n", msg_len, msg_len, buf);
	}

	return 0;
}
go 语言
// Retrieve host's context ID from /dev/vsock. More on this later.
cid := localContextID()

// Establish a connection-oriented VM socket.
socket, err := unix.Socket(unix.AF_VSOCK, unix.SOCK_STREAM, 0)
if err != nil {
    return err
}

// Bind socket to local context ID, port 1024.
sockaddr := &unix.SockaddrVM{
    CID:  cid,
    Port: 1024,
}
if err := unix.Bind(socket, sockaddr); err != nil {
    return err
}

// Listen for up to 32 incoming connections.
fd, err := unix.Listen(socket, 32)
if err != nil {
    return err
}

// Use fd to read and write data to and from a VM.
3.2 guest/client 端
python语言
#!/usr/bin/env python3

import socket

CID = socket.VMADDR_CID_HOST
PORT = 9999

s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
s.connect((CID, PORT))
s.sendall(b"Hello, world!")
s.close()
c 语言
#include <sys/socket.h>
#include <linux/vm_sockets.h>
#include <string.h>

int main()
{
	int s = socket(AF_VSOCK, SOCK_STREAM, 0);

	struct sockaddr_vm addr;
	memset(&addr, 0, sizeof(struct sockaddr_vm));
	addr.svm_family = AF_VSOCK;
	addr.svm_port = 9999;
	addr.svm_cid = VMADDR_CID_HOST;

	connect(s, &addr, sizeof(struct sockaddr_vm));

	send(s, "Hello, world!", 13, 0);

	close(s);

	return 0;
}
go 语言
// Establish a connection-oriented VM socket.
socket, err := unix.Socket(unix.AF_VSOCK, unix.SOCK_STREAM, 0)
if err != nil {
    return err
}

// Connect socket to hypervisor context ID, port 1024.
sockaddr := &unix.SockaddrVM{
    CID:  2,
    Port: 1024,
}
if err := unix.Connect(socket, sockaddr); err != nil {
   return err
}

// Use fd to read and write data to and from the hypervisor.
3.3 wireshark抓包
ip link add type vsockmon
ip link set vsockmon0 up
wireshark -k -i vsockmon0
ip link set vsockmon0 down
ip link del vsockmon0

参考:

https://mdlayher.com/blog/linux-vm-sockets-in-go/

https://wiki.qemu.org/Features/VirtioVsock

https://libvirt.org/formatdomain.html

https://gist.github.com/nrdmn/7971be650919b112343b1cb2757a3fe6#c

https://stefano-garzarella.github.io/posts/2020-02-20-vsock-nested-vms-loopback/#local-communication

https://libvirt.org/formatdomain.html#vsock

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

robin5911

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值