溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

KVM 虛擬化原理中的網(wǎng)絡(luò)IO虛擬化是怎樣的

發(fā)布時(shí)間:2021-12-03 17:20:40 來源:億速云 閱讀:152 作者:柒染 欄目:云計(jì)算

KVM 虛擬化原理中的網(wǎng)絡(luò)IO虛擬化是怎樣的,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

IO 虛擬化簡(jiǎn)介

前面的文章介紹了KVM的啟動(dòng)過程,CPU虛擬化,內(nèi)存虛擬化原理。作為一個(gè)完整的風(fēng)諾依曼計(jì)算機(jī)系統(tǒng),必然有輸入計(jì)算輸出這個(gè)步驟。傳統(tǒng)的IO包括了網(wǎng)絡(luò)設(shè)備IO,塊設(shè)備IO,字符設(shè)備IO等等,在KVM虛擬化原理探究里面,我們最主要介紹網(wǎng)絡(luò)設(shè)備IO和塊設(shè)備IO,其實(shí)他們的原理都很像,但是在虛擬化層又分化開了,這也是為什么網(wǎng)絡(luò)設(shè)備IO虛擬化和塊設(shè)備IO虛擬化要分開講的原因。這一章介紹一下網(wǎng)絡(luò)設(shè)備IO虛擬化,下一章介紹塊設(shè)備IO虛擬化。

傳統(tǒng)的網(wǎng)絡(luò)IO流程

這里的傳統(tǒng)并不是真的傳統(tǒng),而是介紹一下在非虛擬化環(huán)境下的網(wǎng)絡(luò)設(shè)備IO流程。我們平常所使用的Linux版本,比如Debian或者CentOS等都是標(biāo)準(zhǔn)的Linux TCP/IP協(xié)議棧,協(xié)議棧底層提供了driver抽象層來適配不同的網(wǎng)卡,在虛擬化中最重要的是設(shè)備的虛擬化,但是了解整個(gè)網(wǎng)絡(luò)IO流程后去看待虛擬化就會(huì)更加容易理解了。

標(biāo)準(zhǔn)的TCP/IP結(jié)構(gòu)

在用戶層,我們通過socket與Kernel做交互,包括創(chuàng)建端口,數(shù)據(jù)的接收發(fā)送等操作。
在Kernel層,TCP/IP協(xié)議棧負(fù)責(zé)將我們的socket數(shù)據(jù)封裝到TCP或者UDP包中,然后進(jìn)入IP層,加入IP地址端口信息等,進(jìn)入數(shù)據(jù)鏈路層,加入Mac地址等信息后,通過驅(qū)動(dòng)寫入到網(wǎng)卡,網(wǎng)卡再把數(shù)據(jù)發(fā)送出去。如下圖所示,比較主觀的圖。

KVM 虛擬化原理中的網(wǎng)絡(luò)IO虛擬化是怎樣的

在Linux的TCP/IP協(xié)議棧中,每個(gè)數(shù)據(jù)包是有內(nèi)核的skb_buff結(jié)構(gòu)描述的,如下圖所示,socket發(fā)送數(shù)據(jù)包的時(shí)候后,進(jìn)入內(nèi)核,內(nèi)核從skb_buff的池中分配一個(gè)skb_buff用來承載數(shù)據(jù)流量。

KVM 虛擬化原理中的網(wǎng)絡(luò)IO虛擬化是怎樣的

發(fā)送數(shù)據(jù)和接收數(shù)據(jù)驅(qū)動(dòng)層都采用DMA模式,驅(qū)動(dòng)加載時(shí)候會(huì)為網(wǎng)卡映射內(nèi)存并設(shè)置描述狀態(tài)(寄存器中),也就是內(nèi)存的起始位,長(zhǎng)度,剩余大小等等。發(fā)送時(shí)候?qū)?shù)據(jù)放到映射的內(nèi)存中,然后設(shè)置網(wǎng)卡寄存器產(chǎn)生一個(gè)中斷,告訴網(wǎng)卡有數(shù)據(jù),網(wǎng)卡收到中斷后處理對(duì)應(yīng)的內(nèi)存中的數(shù)據(jù),處理完后向CPU產(chǎn)生一個(gè)中斷告訴CPU數(shù)據(jù)發(fā)送完成,CPU中斷處理過程中向上層driver通知數(shù)據(jù)發(fā)送完成,driver再依次向上層返回。在這個(gè)過程中對(duì)于driver來說,發(fā)送是同步的。接收數(shù)據(jù)的流程和發(fā)送數(shù)據(jù)幾乎一致,這里就不細(xì)說了。DMA的模式對(duì)后面的IO虛擬化來說很重要。

image_1aqger4b915nf19k11gjv1lc21atm9.png-46.6kB

KVM 網(wǎng)絡(luò)IO虛擬化

準(zhǔn)確來說,KVM只提供了一些基本的CPU和內(nèi)存的虛擬化方案,真正的IO實(shí)現(xiàn)都由qemu-kvm來完成,只不過我們?cè)诮榻BKVM的文章里都默認(rèn)qemu-kvm和KVM為一個(gè)體系,就沒有分的那么仔細(xì)了。實(shí)際上網(wǎng)絡(luò)IO虛擬化都是由qemu-kvm來完成的。

KVM 全虛擬化IO

還記得我們第一章節(jié)的demo里面,我們的“鏡像”調(diào)用了 out 指令產(chǎn)生了一個(gè)IO操作,然后因?yàn)榇瞬僮鳛槊舾械脑O(shè)備訪問類型的操作,不能在VMX non-root 模式下執(zhí)行,于是VM exits,模擬器接管了這個(gè)IO操作。

switch (kvm->vcpus->kvm_run->exit_reason) {
        case KVM_EXIT_UNKNOWN:
            printf("KVM_EXIT_UNKNOWN\n");
            break;
        // 虛擬機(jī)執(zhí)行了IO操作,虛擬機(jī)模式下的CPU會(huì)暫停虛擬機(jī)并
        // 把執(zhí)行權(quán)交給emulator
        case KVM_EXIT_IO:
            printf("KVM_EXIT_IO\n");
            printf("out port: %d, data: %d\n", 
                kvm->vcpus->kvm_run->io.port,  
                *(int *)((char *)(kvm->vcpus->kvm_run) + kvm->vcpus->kvm_run->io.data_offset)
                );
            break;
        ...

虛擬機(jī)退出并得知原因?yàn)?KVM_EXIT_IO,模擬器得知由于設(shè)備產(chǎn)生了IO操作并退出,于是獲取這個(gè)IO操作并打印出數(shù)據(jù)。這里其實(shí)我們就最小化的模擬了一個(gè)虛擬IO的過程,由模擬器接管這個(gè)IO。

在qemu-kvm全虛擬化的IO過程中,其實(shí)原理也是一樣,KVM捕獲IO中斷,由qemu-kvm接管這個(gè)IO,由于采用了DMA映射,qemu-kvm在啟動(dòng)時(shí)候會(huì)注冊(cè)設(shè)備的mmio信息,以便能獲取到DMA設(shè)備的映射內(nèi)存和控制信息。

static int pci_e1000_init(PCIDevice *pci_dev)
{
    e1000_mmio_setup(d); 
    // 為PCI設(shè)備設(shè)置 mmio 空間
    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); 
    pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->io);
    d->nic = qemu_new_nic(&net_e1000_info, &d->conf, object_get_typename(OBJECT(d)), d->dev.qdev.id, d);   
    add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); 
}

對(duì)于PCI設(shè)備來說,當(dāng)設(shè)備與CPU之間通過映射了一段連續(xù)的物理內(nèi)存后,CPU對(duì)PCI設(shè)備的訪問只需要像訪問內(nèi)存一樣訪問既可以。IO設(shè)備通常有兩種模式,一種是port模式,一種是MMIO模式,前者就是我們demo里面的in/out指令,后者就是PCI設(shè)備的DMA訪問方式,兩種方式的操作都能被KVM捕獲。

于是qemu-kvm將此操作代替Guest完成后并執(zhí)行相應(yīng)的“回調(diào)”,也就是向vCPU產(chǎn)生中斷告訴IO完成并返回Guest繼續(xù)執(zhí)行。vCPU中斷和CPU中斷一樣,設(shè)置相應(yīng)的寄存器后中斷便會(huì)觸發(fā)。

在全虛擬化環(huán)境下,Guest中的IO都由qemu-kvm接管,在Guest中看到的一個(gè)網(wǎng)卡設(shè)備并不是真正的一塊網(wǎng)卡,而是由物理機(jī)產(chǎn)生的一個(gè)tap設(shè)備。知識(shí)在驅(qū)動(dòng)注冊(cè)的時(shí)候?qū)⒁恍﹖ap設(shè)備所支持的特性加入到了Guest的驅(qū)動(dòng)注冊(cè)信息里面,所以在Guest中看到有網(wǎng)絡(luò)設(shè)備。

KVM 虛擬化原理中的網(wǎng)絡(luò)IO虛擬化是怎樣的

如上圖所示,qemu接管了來自Guest的IO操作,真實(shí)的場(chǎng)景肯定是需要將數(shù)據(jù)再次發(fā)送出去的,而不是像demo一樣打印出來,在Guest中的數(shù)據(jù)包二層封裝的Mac地址后,qemu層不需要對(duì)數(shù)據(jù)進(jìn)行拆開再解析,而只需要將數(shù)據(jù)寫入到tap設(shè)備,tap設(shè)備和bridge之間交互完成后,由bridge直接發(fā)送到網(wǎng)卡,bridge(其實(shí)NIC綁定到了Bridge)開啟了混雜模式,可以將所有請(qǐng)求都接收或者發(fā)送出去。

以下來自這篇文章的引用

當(dāng)一個(gè) TAP 設(shè)備被創(chuàng)建時(shí),在 Linux 設(shè)備文件目錄下將會(huì)生成一個(gè)對(duì)應(yīng) char 設(shè)備,用戶程序可以像打開普通文件一樣打開這個(gè)文件進(jìn)行讀寫。當(dāng)執(zhí)行 write()操作時(shí),數(shù)據(jù)進(jìn)入 TAP 設(shè)備,此時(shí)對(duì)于 Linux 網(wǎng)絡(luò)層來說,相當(dāng)于 TAP 設(shè)備收到了一包數(shù)據(jù),請(qǐng)求內(nèi)核接受它,如同普通的物理網(wǎng)卡從外界收到一包數(shù)據(jù)一樣,不同的是其實(shí)數(shù)據(jù)來自 Linux 上的一個(gè)用戶程序。Linux 收到此數(shù)據(jù)后將根據(jù)網(wǎng)絡(luò)配置進(jìn)行后續(xù)處理,從而完成了用戶程序向 Linux 內(nèi)核網(wǎng)絡(luò)層注入數(shù)據(jù)的功能。當(dāng)用戶程序執(zhí)行 read()請(qǐng)求時(shí),相當(dāng)于向內(nèi)核查詢 TAP 設(shè)備上是否有需要被發(fā)送出去的數(shù)據(jù),有的話取出到用戶程序里,完成 TAP 設(shè)備的發(fā)送數(shù)據(jù)功能。針對(duì) TAP 設(shè)備的一個(gè)形象的比喻是:使用 TAP 設(shè)備的應(yīng)用程序相當(dāng)于另外一臺(tái)計(jì)算機(jī),TAP 設(shè)備是本機(jī)的一個(gè)網(wǎng)卡,他們之間相互連接。應(yīng)用程序通過 read()/write()操作,和本機(jī)網(wǎng)絡(luò)核心進(jìn)行通訊。

類似這樣的操作

fd = open("/dev/tap", XXX)
write(fd, buf, 1024);
read(fd, buf, 1024);

bridge可能是一個(gè)Linux bridge,也可能是一個(gè)OVS(Open virtual switch),在涉及到網(wǎng)絡(luò)虛擬化的時(shí)候,通常需要利用到bridge提供的VLAN tag功能。

以上就是KVM的網(wǎng)絡(luò)全虛擬化IO流程了,我們也可以看到這個(gè)流程的不足,比如說當(dāng)網(wǎng)絡(luò)流量很大的時(shí)候,會(huì)產(chǎn)生過多的VM的切換,同時(shí)產(chǎn)生過多的數(shù)據(jù)copy操作,我們知道copy是很浪費(fèi)CPU時(shí)鐘周期的。于是qemu-kvm在發(fā)展的過程中,實(shí)現(xiàn)了virtio驅(qū)動(dòng)。

KVM Virtio 驅(qū)動(dòng)

基于 Virtio 的虛擬化也叫作半虛擬化,因?yàn)橐笤贕uest中加入virtio驅(qū)動(dòng),也就意味著Guest知道了自己運(yùn)行于虛擬環(huán)境了。
KVM 虛擬化原理中的網(wǎng)絡(luò)IO虛擬化是怎樣的

vhost-net 繞過了 QEMU 直接在Guest的front-end和backend之間通信,減少了數(shù)據(jù)的拷貝,特別是減少了用戶態(tài)到內(nèi)核態(tài)的拷貝。性能得到大大加強(qiáng),就吞吐量來說,vhost-net基本能夠跑滿一臺(tái)物理機(jī)的帶寬。
vhost-net需要內(nèi)核支持,Redhat 6.1 后開始支持,默認(rèn)狀態(tài)下是開啟的。

KVM的網(wǎng)絡(luò)設(shè)備IO虛擬化經(jīng)過了全虛擬化->virtio->vhost-net的進(jìn)化,性能越來越接近真實(shí)物理網(wǎng)卡,但是在小包處理方面任然存在差距,不過已經(jīng)不是一個(gè)系統(tǒng)的瓶頸了,可以看到KVM在經(jīng)過了這多年的發(fā)展后,性能也是越發(fā)的強(qiáng)勁,這也是他領(lǐng)先于其他虛擬化的重要原因之一。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI