转载:http://blog.csdn.net/quqi99/article/details/47321023

X86体系早期没有在硬件设计上对虚拟化提供支持,因此虚拟化完全通过软件实现。一个典型的做法是通过优先级压缩(Ring Compression)和二进制代码翻译(Binary Translation)相结合,VMM在特权级ring 0, Guest操作系统在非特权级ring 1, Guest应用程序在ring 3。由于Guest操作系统工作在非特权级ring 1中,这样当它执行特权指令时就会触发异常,由VMM截获异常并采用软件方式模拟指令并返回。但由于X86体系在设计之初没有考虑虚拟化,所以一小部分特权指令在ring 1下没有引发异常,所以需要对操作系统的二进制代码进行扫描,一旦发现不友好的指令,将就其换成支持虚拟化的指令块,这些指令块与VMM合作访问受限的虚拟资源,或者显式地触发异常让VMM进一步处理。这种打补丁的方式很难在构架上保证其完整性。于是,下层的硬件加入了虚拟化功能,硬件截获操作系统对敏感指令的执行或者对敏感资源的访问后通过异常的方式报告给VMM,这样就解决了虚拟化的问题。Intel VTx技术是这一方向的代表,它引入了一个新的特殊执行模式用于运行虚拟机,这个模式会将特权指令统统截取并报告给VMM,VMM本身工作在正常模式下,在接收到处理的报告后,通过对目标指令的解码,找到相应的虚拟化模块进行模拟
    对于内存的虚拟化则需要用硬件实现从GVA(Guest Virtual Address)到GPA(Guest Physical Address)再到HPA(Host Physical Address)的两次地址转换。传统非虚拟化的操作系统只通过硬件MMU完成一次从GVA到GPA的虚拟化。
    在I/O的虚拟化方面,VTd技术在北桥引入DMA重映射硬件来提供设备重映射和设备直接分配的功能。设备所有的DMA传输都会被DMA重映射硬件截取,然后根据设备对应的I/O页表,对DMA中的地址进行转换,使设备只能访问特定的内存。这样,设备就可以直接分配给Guest机使用,驱动提供给设备的GPA经过重映射,变为HPA,使得DMA操作得以完成。
    在网络虚拟化方面,VTd技术可以将一个物理网卡直接分配给一个虚机使用,但扩展性差,因为一台服务支持的PCI设备数是有限的,远远不能满足越来越来的Guest机数量。因此SRIOV(Single
Root I/O Virtualization)被引入来解决这个问题。SRIOV是PCIe(PCI
Express)规范的一个扩展,定义了本质上可以共享的设备。它允许一个PCIe设备,通过是网卡,为每个与其相连的客户机复制一份资源(例如内存资源、中断和DMA数据流),使得数据处理不再依赖VMM。
    在GPU虚拟化方面,如XenGT,它是由Intel在Xen基础上开发的GPU虚拟化开源方案。客户机操作系统不需要任何改动,特权操作会被Xen截获,并且转发到中介(Mediator),中介为每个客户机创建一个GPU上下文(Context),并且在其中模拟特权操作。

1, 普通虚拟化

传统的KVM实现中,kvm.ko是内核的一个模块主要用来捕获虚机的是上述的针对CPU、内存MMU的特权指令然后负责模拟,对I/O的模拟则是再通过用户态的Qemu进程模拟的,Qemu负责解释I/O指令流,并通过系统调用让Host操作系统上的驱动去完成真正的I/O操作。这其中用户态与内核态切换了两次,数据也需要复制


   用户态的Qemu在启动Guest操作系统时,可以先通过KVM在用户态的字符接口/dev/kvm获取Guest的地址空间(fd_vm)
和KVM本身(fd_kvm),然后通过fd_vm将Guest的物理空间mmap到Qemu进程的虚拟空间(虚机就是一个进程,虚机进程的用户虚拟空间就是Guest的物理空间,映射之后Qemu的虚拟设备就可以很方便的存取Guest地址空间里的数据了),并根据配置信息创建vcpu[N]线程(对虚机来说,虚机进程的线程对应着Guest的处理器),然后Qemu将操作fd_vcpu[N]在自己的进程空间mmap一块KVM的数据结构区域(即下图的shared,包括:Guest的IO信息,如端口号,读写方向,内存地址)。该数据结构用于Qemu和kvm.ko交互,Qemu通过它调用虚拟设备注册的回调函数来模拟设备的行为,并将Guest IO的请求换成系统请求发给Host系统。

图中vm-exit代表处理器进入host模式,执行kvm和Qemu的逻辑。vm-entry代表处理器进入Guest模式,执行整个Guest系统的逻辑。Qemu通过三个文件描述符同kvm.ko交互,然后kvm.ko通过vmcs这个数据结构同处理器交互,最终达到控制Guest系统的效果。其中fd_kvm主要用于Qemu同KVM本身的交互,比如获取KVM的版本号,创建地址空间、vcpu等fd_vcpu主要用于控制处理器的模式切换,设置进入Guest

mode前的处理器状态等等(内存寻址模式,段寄存器、控制寄存器、指令指针等),同时Qemu需要通过fd_vcpu来mmap一块KVM的数据结构区域。fd_vm主要用于Qemu控制Guest的地址空间,向Guest注入虚拟中断等。

2, VirtIO

VirtIO通过共享内存的方式为Guest和Qemu提供了高速的硬盘与网络I/O通道(因为Guest的地址空间早就mmap到Qemu的进程空间)。VirtIO只需要增加VirtIO
Driver和VirtIO Device即可直接读写共享内存。如下图,Guest
VirtIO驱动通过访问port地址空间向Qemu的VirtIO设备发送IO发起消息。而设备通过读写irqfd或者IOCTL
fd_vm将I/O的完成情况通知给Guest的驱动。irqfd和ioeventfd是KVM为用户程序基于内核eventfd机制提供的通知机制,以实现异步的IO处理(这样发起IO请求的vcpu将不会阻塞)。之所以使用PIO而不是MMIO,是因为KVM处理PIO的速度快于MMIO。


3, vhost
   VirtIO通过共享内存减小了Guest与Qemu之间数据复制的开销,但Qemu到内核的系统调用还是跑不了。有必要将模拟I/O的逻辑将VirtIO Device挪到内核空间避免系统调用。
   vhost在内核态再增加一个vhost-net.ko模
块, vhost client与vhost backend通过共享内存(virtqueues, unit socket), 文件描述符(ioeventfds), (中断号)irqfds实现快速交换数据的机制。


4, vhost-user
   vhost使用了内核态TCP/IP栈导致从Guest到kvm.ko仍然有一个用户态内核态的切换以及数据的拷贝。一些用户态栈议栈如openonload直接在用户态实现网卡驱动再实现BSD兼容的socket接口供上层应用使用,vhost-user(需要将vhost-backend移到用户态)的共享内存技术用于将这些用户态网络设备(又如snabb switch)和用户态的Guest连接起来

5, DPDK support for vhost-user [3]
   DPDK便是一个在用户态可以直接操作物理网卡的库函数,它和vhost-user结合便可以实现类似于snabb
switch一样性能强劲的用户态交换机了。2015年6月16日dpdk vhost-user ports特性合并到了ovs社区[1],
dpdkvhostuser port将创建unix socket将虚机的virtio-net虚拟网卡的网卡缓冲区共享给物理机上的ovs
port设备。

理论部分结束,下面开始安配置配置dpdk + vhost-user
1, sudo apt-get install fuse libfuse-dev dh-autoreconf openssl libssl-dev
   Git clone git://dpdk.org/dpdk
2, 检查硬件网卡是否支持dpdk,在lib/librte_eal/common/include/rte_pci_dev_ids.h文件中搜索0x153A能搜到。
   hua@hua-ThinkPad-T440p:/bak/OpenStack$ sudo lspci -nn|grep Ethernet
   00:19.0 Ethernet controller [0200]: Intel Corporation Ethernet Connection I217-LM [8086:153a] (rev 04)
3, 修改文件config/common_linuxapp
   CONFIG_RTE_BUILD_COMBINE_LIBS=y  #产生单个库文件
   CONFIG_RTE_LIBRTE_VHOST=y        #enable对vhost的支持
4, 编译,安装。DPDK IVSHMEM可以在host-to-guest或guest-to-guest之间采用零拷贝技术
   make config T=x86_64-ivshmem-linuxapp-gcc
   make install T=x86_64-ivshmem-linuxapp-gcc
5, 编译eventfd模块
   cd $DPDK/lib/librte_vhost/eventfd_link/
   make
   sudo insmod eventfd_link.ko
6, uio提供了用户态驱动开发的框架, dpdk中的uid驱动需要定期去poll/select

/dev/uioX检查设备是否有中断产生。uio也是通过mmap将设备的内存映射到用户空间,当然用户态也可以通过/sys/class/uio/uioX/maps/mapX来实现对设备内存的访问,所以发送和接受数据是通过操作设备的内存来完成的。
   DPDK除了可以使用uio驱动,还可以使用VFIO驱动。VFIO是用户态的PCI设备驱动开发的框架,它向用户态提供访问硬件设备的接口,也向用户态提供配置IOMMU的接口。它需要内核与BIOS的支持。
   sudo modprobe uio
   sudo insmod x86_64-ivshmem-linuxapp-gcc/kmod/igb_uio.ko
   $ sudo ./tools/dpdk_nic_bind.py --status
    Network devices using DPDK-compatible driver
    ============================================
    <none>
    Network devices using kernel driver
    ===================================
    0000:00:19.0 'Ethernet Connection I217-LM' if=eth0 drv=e1000e unused=igb_uio *Active*
    Other network devices
    =====================
    <none>
   $ sudo ./tools/dpdk_nic_bind.py --force --bind=igb_uio eth0
   $ sudo tools/dpdk_nic_bind.py -u 0000:00:19.0  # 恢复

7, 大页支持,添加/etc/default/grub后执行update-grub
(centos是执行:grub2-mkconfig --output=/boot/grub2/grub.cfg),
通过mount一个大页文件系统,然后通过mmap的文件映射,使得该文件系统中的内存操作实现大页调度,从而解决TLB shrashing的问题。
   GRUB_CMDLINE_LINUX=iommu=pt intel_iommu=on default_hugepagesz=1G hugepagesz=1G hugepages=8
   之后mount它,mount -t hugetlbfs -o pagesize=1G none /dev/hugepages

8, 编译ovs
   git clone https://github.com/openvswitch/ovs.git
   cd ovs && ./boot.sh
  
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var
--with-dpdk=/bak/openstack/dpdk/x86_64-ivshmem-linuxapp-gcc/
--enable-ssl
   但报错“cannot link with dpdk”,无法继续了。

opendp是一个基于DPDK的用户态协议栈,当需要三层VPN或者NAT时或者其他应用时需要它,https://github.com/opendp/dpdk-odp/wiki

DPDK support for vhost-user的更多相关文章

  1. 译文:ovs+dpdk中的“vHost User NUMA感知”特性

    本文描述了"vHost User NUMA感知"的概念,该特性的测试表现,以及该特性为ovs+dpdk带来的性能提升.本文的目标受众是那些希望了解ovs+dpdk底层细节的人,如果 ...

  2. ovs+dpdk numa感知特性验证

    0.介绍 本测试是为了验证这篇文章中提到的DPDK的NUMA感知特性. 简单来说,在ovs+dpdk+qemu的环境中,一个虚拟机牵涉到的内存共有三部分: DPDK为vHost User设备分配的De ...

  3. DPDK在OpenStack中的实现

    随着云计算与大数据的快速发展,其对数据中心网络的性能和管理提出了更高的要求,但传统云计算架构存在多个I/O瓶颈,由于云平台基本上是采用传统的X86服务器加上虚拟化方式组建,随着40G.100G高速网卡 ...

  4. VNF网络性能提升解决方案及实践

    VNF网络性能提升解决方案及实践 2016年7月 作者:    王智民 贡献者:     创建时间:    2016-7-20 稳定程度:    初稿 修改历史 版本 日期 修订人 说明 1.0 20 ...

  5. vyatta的fork开源版本

    https://www.reddit.com/r/networking/comments/3dvwfy/who_here_is_using_vyos/ Vyatta came in two flavo ...

  6. OVS 总体架构、源码结构及数据流程全面解析

    在前文「从 Bridge 到 OVS」中,我们已经对 OVS 进行了一番探索.本文决定从 OVS 的整体架构到各个组件都进行一个详细的介绍. OVS 架构 OVS 是产品级的虚拟交换机,大量应用在生产 ...

  7. vyatta的fork开源版本vyos

    vyatta的fork开源版本vyos 来源: https://www.reddit.com/r/networking/comments/3dvwfy/who_here_is_using_vyos/ ...

  8. SRS之RTMP的TCP线程(即监听线程)

    本文分析的是 SRS 针对 rtmp 的端口建立的 tcp 线程.具体建立过程: SRS之监听端口的管理:RTMP RTMP 的 TCP 线程中各个类之间 handler 的关系图 1. RTMP之T ...

  9. DPDK之(八)——vhost库

    转:http://www.cnblogs.com/danxi/p/6652725.html vhost库实现了一个用户空间的virtio net server,允许用户直接处理virtio ring队 ...

随机推荐

  1. web浏览器中javascript

    1.异步载入一个js代码function loadasync(url) { var head = document.getElementsByTagName("head")[0]; ...

  2. html5跨域数据传递(postMessage)

    在html5中有个支持跨域传递的方法postMessage,可是实现iframe之间的数据传递! 代码如下:数据发送页面 <!DOCTYPE HTML> <html lang=&qu ...

  3. leetcode[87] Partition List

    题目:给定一个链表和一个数x,将链表中比x小的放在前面,其他的放在后头.例如: Given 1->4->3->2->5->2 and x = 3,return 1-> ...

  4. Android学习路径(四)文件项目学习的名单,android显示单元经常使用的

    1.的该项目文件所谓名单AndroidManifest.xml文件.该文件,但有很大的利用,例:app名字.图标,app支持的版本app等等.以下我就介绍下这个清单文件的各个參数的作用. <ma ...

  5. 编解码器的学习笔记(十):Ogg系列

    Ogg是一个自由和开放的标准容器格式,由Xiph.Org 维修基金. Ogg格式不受软件专利的限制,它的目的是有效地处理高品质的流媒体和数字媒体. Ogg意指一种文件格式,能够纳入各式各样自由和开放源 ...

  6. .net微软消息队列(msmq)简单案例

    1.首先我们需要安装消息队列服务,它是独立的消息记录的服务,并保存在硬盘文件中. 我们添加名为:DMImgUpload的私有消息队列. 2.定义消息队列的连接字符串建议采用IP: (1)FormatN ...

  7. 松瀚SN8P2711 2722 ADC初始化程序及应用--汇编源码

    /* 松瀚 SN8P2711 2722 ADC初始化程序 及应用实例 */ INIT_ADC: MOV A, #0XB2 // 启动ADC电路 使能AIN通道 B0MOV ADM, A MOV A,# ...

  8. QQ三方登录步骤详解

    首先,登录QQ互联:http://connect.qq.com/intro/login  ,注册成为开发者 选择申请加入,并创建你的应用. 创建成功后可以获取到appid和appkey 在网站的主页引 ...

  9. 生成自己的Webapi帮助文档(一)

    最近Webapi接口的开发刚刚进入尾声,随之而来的是让用户知道接口的详细参数信息,看过淘宝的接口文档,但网上没找到他的实现方式 虽然新建Webapi时C#也会给你一个帮助文档的Area,但是总觉得有些 ...

  10. 【学习笔记】锋利的jQuery(二)DOM操作

    一.获取DOM节点 //找祖宗 parent() parents() closest() //找后代 children(); find(); //找兄弟 next()/nextAll() prev() ...