qemu 共享内存设备——ivshmem

背景

ivshmem是QEMU提供的一种宿主机与虚拟机之间或多个虚拟机之间共享内存的特殊设备。它有两种形式:

  • ivshmem-plain: 简单的共享内存区域
  • ivshmem-doorbel: 除了共享内存,还能提供基于中断的通信机制

这种设备在虚拟机内部表现为PCI设备,共享的内存区域则以PCI BAR的形式存在。ivshmem PCI设备提供3个BAR:

  • BAR0: 设备寄存器
  • BAR1: MSI-X表
  • BAR2: 共享内存区域

简单的共享内存场景只使用BAR2。如果需要基于中断实现额外通信,需要用到BAR0和BAR1。

只考虑共享内存这个场景,是如何做到内存共享的呢?在不同的虚拟机(这些虚拟机会共享内存)内,ivshmem device的BAR2地址(GPA)可能不同,但是对应的HPA是一样的,hypervisor会为不同虚拟机的ivshmem device的BAR2内存区域建立EPT映射,因此就达到共享内存的目的!
即:

  • GPA1->HPA1
  • GPA2→HPA1

简单使用

  1. qemu启动参数添加:-device ivshmem-plain,memdev=ivshmem -object memory-backend-file,id=ivshmem,share=on,mem-path=/dev/shm/shm1,size=16M

  2. 启动虚拟机,可以看到:
    在这里插入图片描述

  3. 虚机内使用测试程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <assert.h>
 
#define SHM_SIZE (16 * 1024 * 1024)
 
int main(int argc, char **argv) {
    char *p;
    int fd;
    int i;
 
    fd = open("/sys/bus/pci/devices/0000:00:04.0/resource2", O_RDWR);
    assert(fd != -1);
 
    p = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    assert(p != NULL);
 
    for (i = 0; i < 8; i++) {
        printf("%c", p[i]);
    }
 
    munmap(p, SHM_SIZE);
    close(fd);
 
    return 0;
}
  1. Host 内向共享内存写入:echo "SIGN_01" > /dev/shm/shm1

  2. guest内运行程序:

$ sudo ./a.out
SIGN_01

源码分析

qemu-9.2.0
目前ivshmem设备源码位置在hw/misc/ivshmem.c下面,可以看出,它是一个misc设备。

static const TypeInfo ivshmem_plain_info = {
    .name          = TYPE_IVSHMEM_PLAIN,
    .parent        = TYPE_IVSHMEM_COMMON,
    .instance_size = sizeof(IVShmemState),
    .class_init    = ivshmem_plain_class_init,
};

参考

posted @ 2024-12-24 17:14  main_c  阅读(2)  评论(0)    收藏  举报  来源