zoukankan      html  css  js  c++  java
  • VIRTIO & VHOST

     

    [root@x86compute01 ~]# ps aux | grep vhost
    64055      3950  3.3  0.0 5996552 208716 ?      Sl   4月22 8799:09 /usr/bin/qemu-system-x86_64 -name guest=instance-0000308f,debug-threads=on -S -object secret,id=masterKey0,format=raw,file=/var/lib/libvirt/qemu/domain-134-instance-0000308f/master-key.aes -machine pc-i440fx-zesty,accel=kvm,usb=off,dump-guest-core=off -cpu Skylake-Client,+ds,+acpi,+ss,+ht,+tm,+pbe,+dtes64,+ds_cpl,+vmx,+smx,+est,+tm2,+xtpr,+pdcm,+dca,+osxsave,+tsc_adjust,+avx512f,+clflushopt,+avx512cd,+pdpe1gb -m 4096 -realtime mlock=off -smp 2,sockets=2,cores=1,threads=1 -uuid 0d6ef0e0-6b86-4fff-a425-863877a0b5f2 -smbios type=1,manufacturer=OpenStack Foundation,product=OpenStack Nova,version=2017.1.21,serial=3348e5ef-8069-8419-e811-2efbc05ce5ac,uuid=0d6ef0e0-6b86-4fff-a425-863877a0b5f2,family=Virtual Machine -no-user-config -nodefaults -chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/domain-134-instance-0000308f/monitor.sock,server,nowait -mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc,driftfix=slew -global kvm-pit.lost_tick_policy=discard -no-hpet -no-shutdown -boot strict=on -device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 -device virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x4 -drive file=rbd:volumes/volume-9f71c531-1edd-415f-a0e8-ef5200175b11:id=admin:key=AQA9uABdD4XnLxAAaP1CnahFfgruXJdqE43hrg==:auth_supported=cephx;none:mon_host=172.168.100.5:6789;172.168.100.17:6789;172.168.100.29:6789;172.168.100.41:6789;172.168.100.53:6789,format=raw,if=none,id=drive-scsi0-0-0-0,serial=9f71c531-1edd-415f-a0e8-ef5200175b11,cache=none -device scsi-hd,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,bootindex=1 -netdev tap,fd=30,id=hostnet0,vhost=on,vhostfd=56 -device virtio-net-pci,netdev=hostnet0,id=net0,mac=fa:16:3e:47:c0:08,bus=pci.0,addr=0x3 -chardev file,id=charserial0,path=/var/lib/nova/instances/0d6ef0e0-6b86-4fff-a425-863877a0b5f2/console.log -device isa-serial,chardev=charserial0,id=serial0 -chardev pty,id=charserial1 -device isa-serial,chardev=charserial1,id=serial1 -device usb-tablet,id=input0,bus=usb.0,port=1 -vnc 10.10.16.104:28 -k en-us -device VGA,id=video0,vgamem_mb=16,bus=pci.0,addr=0x2 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x5 -msg timestamp=on
    root       3953  0.0  0.0      0     0 ?        S    4月22  24:32 [vhost-3950]
    64055      5604  486  3.0 10628428 8159336 ?    Sl    2019 2759553:54 /usr/bin/qemu-system-x86_64 -name guest=instance-00001409,debug-threads=on -S -object secret,id=masterKey0,format=raw,file=/var/lib/libvirt/qemu/domain-78-instance-00001409/master-key.aes -machine pc-i440fx-zesty,accel=kvm,usb=off,dump-guest-core=off -cpu Skylake-Client,+ds,+acpi,+ss,+ht,+tm,+pbe,+dtes64,+ds_cpl,+vmx,+smx,+est,+tm2,+xtpr,+pdcm,+dca,+osxsave,+tsc_adjust,+avx512f,+clflushopt,+avx512cd,+pdpe1gb -m 8192 -realtime mlock=off -smp 8,sockets=8,cores=1,threads=1 -uuid c14496ed-c105-4f69-bc5b-4b49ee343aac -smbios type=1,manufacturer=OpenStack Foundation,product=OpenStack Nova,version=2017.1.21,serial=3348e5ef-8069-8419-e811-2efbc05ce5ac,uuid=c14496ed-c105-4f69-bc5b-4b49ee343aac,family=Virtual Machine -no-user-config -nodefaults -chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/domain-78-instance-00001409/monitor.sock,server,nowait -mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc,driftfix=slew -global kvm-pit.lost_tick_policy=discard -no-hpet -no-shutdown -boot strict=on -device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 -drive file=rbd:volumes/volume-c1b01c8c-5725-4fc1-bf5b-d6b39c6dbff2:id=admin:key=AQA9uABdD4XnLxAAaP1CnahFfgruXJdqE43hrg==:auth_supported=cephx;none:mon_host=172.168.100.5:6789;172.168.100.17:6789;172.168.100.29:6789;172.168.100.41:6789;172.168.100.53:6789,format=raw,if=none,id=drive-virtio-disk0,serial=c1b01c8c-5725-4fc1-bf5b-d6b39c6dbff2,cache=none -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1 -netdev tap,fd=28,id=hostnet0,vhost=on,vhostfd=34 -device virtio-net-pci,netdev=hostnet0,id=net0,mac=fa:16:3e:b7:4b:79,bus=pci.0,addr=0x3 -chardev file,id=charserial0,path=/var/lib/nova/instances/c14496ed-c105-4f69-bc5b-4b49ee343aac/console.log -device isa-serial,chardev=charserial0,id=serial0 -chardev pty,id=charserial1 -device isa-serial,chardev=charserial1,id=serial1 -device usb-tablet,id=input0,bus=usb.0,port=1 -vnc 10.10.16.104:2 -k en-us -device VGA,id=video0,vgamem_mb=16,bus=pci.0,addr=0x2 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x5 -msg timestamp=on
    root       5607  0.0  0.0      0     0 ?        S     2019 272:06 [vhost-5604]

    在虚拟化领域,virtio 随处可见。当前,virtio 可以加速 IO、network子系统。

    在 IO 子系统,主要有 virtio-blk, virtio-scsi。同时,有 vhost 相关的 vhost-blk, vhost-scsi, vhost-nvme 这些。

    看起来东西很多很乱,其实只要理解了本质,就可以轻松化解如此多virtio*让人困扰的问题了。

    本质是什么呢?本质就是 virtio 的数据结构,以及操作。

    virtio基本结构

    virtio 的基本数据结构是一个 ring,这个 ring 是一段连续的内存。有了一段内存,就可以为所欲为了。

    virtio 把这段内存分成3个部分,依次是 desc,avail,used。每一块是一个数组,可以顺序索引。他们的元素个数是一样,也就是是 ring 的 长度。

     

    virtio & vhost数据流动

    以VHOST为例,来解释一下数据是如何流动的:

    • client(qemu)创建共享内存,然后通过ioctl与内核通信,告知内核共享内存的信息,这种就是kernel作为server的vhost;或者通过Unix domain来跟其他的进程通信,这叫做vhost-user。下面以Unix domain为例。
    • Unix domain可以使用sendmsg/recvmsg来传递文件描述符,这样效率更高一些;client创建好共享内存,发送描述符到server,server直接mmap这个描述符就可以了,少了一个open的操作。
    • Client和Server可以有多段共享内存,每段之间不连续。每一段都是一个vring。
    • Client初始化好每一段共享内存vring,Server不用初始化。
    • Client发送vring的desc,avail,used这些地址给server,然后server在重新mmap之后,可以根据这个地址算出desc,avail,used这些在server用户进程的地址,然后就可以直接读写了。注意,由于数据指针是client地址,在Server处理读的时候需要转换。
    • 读写:以net为例,两个vring,一个tx,一个rx
    • 共享内存存放desc,avail,used这些信息,以及avail->idx, used->idx这些信息。
    • 当client写的时候,数据放在vring->last_avail_idx这个描述符里,注意last_avail_idx、last_used_idx这两个值,在client和server看到的是不一样的,各自维护自己的信息,作为链表头尾。添加id到avail里面,shared.avail.idx++。注意,client此处的last_avail_idx指向的是描述符的id,而不是avail数组的id,这个跟Server上的last_avail_idx的意思不一样。为什么这样做呢?
      • last_avail_idx 表示可用的数据项,可以直接拿出来用,用完之后,取当前的next;
      • 当回收的时候,也就是client在处理used的时候,可以直接插入到last_avail_idx的前面,类似链表插入到表头;
    • Server收到信号后,从自己记录的last_avail_idx开始读数据,读到avail->idx这里,区间就是[server.last_avail_idx, shared.avail.idx)。
    • Server处理每处理完一条请求,自己的 last_avail_idx ++; 同时插入 id 到 used 里面,插入的位置是 shared.used.idx,然后 shared.used.ix+ +。used.idx此时就像avail->idx一样,表示最新的used的位置。
    • Server通知client处理完数据后,client就可以回收used的描述符了,可回收的区间是[client.last_used_idx, shared.used.idx)。
    • Kickfd,callfd都是在client创建,然后通过unix domain发送给server。client通知server叫kick。

    Vhost是什么

    virtio offload到host叫做vhost。可以在内核态或者用户态实现。在内核态的实现主要在Linux的kernel实现。

    kernel代码位置:drivers/vhost Vhost作为字符设备使用,来与qemu进行交互。跟其他的很多driver一样,利用ioctl。vhost-net 驱动会创建一个名为 /dev/vhost-net 的字符型设备,当 QEMU 通过-netdev tap,fd=,id=hostnet0,vhost=on,vhostfd=这样的参数启动时,QEMU 会打开这个设备(你可以通过 lsof -p $PID 查看,$PID 为 QEMU 的进程号)并通过 ioctl 初始化设备。

    初始化过程中 vhost 驱动会创建一个内核线程名为 vhost-$PID ($PID 为 QEMU 的进程号),这个线程是 vhost 的工作线程(worker thread),工作线程会始终等待 virtqueue 触发,然后处理 virtqueue 上的 buffer,然后送到 tap 设备的文件描述符。反过来,文件描述符的 polling 也是由工作线程完成的,也就是说 vhost-net 在内核模拟了 tx、rx 队列,而并没有完成整个 virtio PIC 设备的模拟,控制平面例如在线迁移、协商等依旧由 QEMU 实现。

     

     

    Vhost-user与vhost的区别

    Vhost是client与kernel(server)的交互,client与内核共享内存,局限性是如果client要发送消息到用户进程,很不方便; Vhost-user使用unix domain来进行通信,两个用户进程共享内存,达到和vhost一样的效果。

    Virtio-blk与virtio-scsi

    他们都是在 virtio spec 里面定义的两种块设备实现。区别是 virtio-blk 是作为 pci 设备挂在 qemu 里面,所以最多只能有16块 virtio-blk 盘。 而 virtio-scsi 作为 scsi 子系统,挂在 scsi 总线上,数量上可以多得多。由于 virtio-scsi 实现了 scsi 的协议 ,所以复杂度来说要高一些。 此时,在 qemu 里面看,这块盘跟普通的 scsi 盘一样,支持 scsi 命令查询,例如 sg3_utils 提供的工具。但是 virtio-blk 盘不支持 scsi 命令。

    相关代码:

    Kerne
    drivers/vhost/vhost.c - common vhost driver code
    drivers/vhost/net.c - vhost-net driver
    virt/kvm/eventfd.c - ioeventfd and irqfd
    
    Qemu
    hw/vhost.c - common vhost initialization code
    hw/vhost_net.c - vhost-net initialization

    # ps -ef | grep kvm1
    libvirt+      3549     1  87 ?        00:22:09 qemu-system-x86_64 -enable-kvm -name kvm1 ... -netdev tap,fd=26,id=hostnet0,vhost=on,vhostfd=28 ...

    可以看到,其中网络部分参数,-netdev tap,fd=26 表示的就是连接主机上的 tap 设备。

    创建的 fd=26 为读写 /dev/net/tun 的文件描述符。

    使用 lsof -p 3549 验证下:

    # lsof -p 3549
    COMMAND    PID USER   FD      TYPE             DEVICE    SIZE/OFF     NODE NAME
    ...
    qemu-system 3549  libvirt-qemu   26u      CHR             10,200         0t107    135 /dev/net/tun
    ...

    可以看到,PID 为 3549 的进程打开了文件 /dev/net/tun,分配的文件描述符 fd 为 26。

    因此,我们可以得出以下结论:在 kvm 虚机启动时,会向内核注册 tap 虚拟网卡,同时打开设备文件 /dev/net/tun,拿到文件描述符 fd,然后将 fd 和 tap 关联,tap 就成了一端连接着用户空间的 qemu-kvm,一端连着主机上的 bridge 的端口,促使两者完成通信。

     
  • 相关阅读:
    WINDOWS黑客基础(5):利用内存来进行获取计算结果
    WINDOWS黑客基础(4):查找进程运行的基址
    WINDOWS黑客基础(3):注入代码
    shell中[[]]和[]的主要区别
    sed的实际用法举例
    linux oracle profile配置
    转 -Linux 自检和 SystemTap (强大的内核调试工具)---包含下载地址
    【转】DBMS_STATS.GATHER_TABLE_STATS详解 2012-04-22 09:20:10
    Linux中的15个‘echo’ 命令实例
    BEA-WEBLOGIC ---http://www.beansoft.biz/weblogic/docs92/index.html
  • 原文地址:https://www.cnblogs.com/dream397/p/13867557.html
Copyright © 2011-2022 走看看