一.KVM虛擬機(jī)創(chuàng)建的用戶操作 對于用戶或者管理員來說,虛擬機(jī)的創(chuàng)建有著很多的方法,例如:kvm自帶命令行工 具、使用virsh命令來創(chuàng)建、使用具有圖形界面的virt-manager等等。但是它們底層實現(xiàn)的原理都是一樣的,而且它們基本上都是通過開源的虛擬化庫Libvirt來開發(fā)的。下面就講一講三種用戶可以創(chuàng)建虛擬機(jī)的方式。 1.1 利用kvm自帶命令行工具進(jìn)行創(chuàng)建 kvm常用命令如下:
具體創(chuàng)建一個虛擬機(jī)的步驟如下: (1)生成硬盤鏡像文件 root@host:kvm-img create -f rawtest.img 10G 其中“-f raw”指定鏡像文件的格式為“raw”,“10G”指定鏡像文件大小。 (2)從光盤啟動虛擬機(jī)來安裝操作系統(tǒng) root@host:kvm -boot d -hda test.img-cdrom test.iso -m 512 其中“-boot d”指定虛擬機(jī)從光盤啟動,“-hda test.img”指定硬盤鏡像的位置,“-cdrom test.iso”指定光盤鏡像的位置,“-m 512”指定虛擬機(jī)的內(nèi)存為512M。 (3)安裝操作系統(tǒng)后便可直接從硬盤啟動虛擬機(jī) root@host:kvm -boot c -hda test.img-m 512 1.2 利用virsh命令行工具進(jìn)行創(chuàng)建 1.2.1 virsh工具簡介 Virsh是由一個名叫l(wèi)ibvirt的軟件提供的管理工具,提供管理虛擬機(jī)比較高級的能力。Virsh可以管理KVM以及xen等虛擬機(jī)。 下面是virsh的一些常見的命令行選項:
1.2.2 virsh命令來創(chuàng)建虛擬機(jī)步驟 (1)生成硬盤鏡像文件 root@host:kvm-img create -f rawtest.img 10G (2)編寫xml配置文件,這一步在1.2.3節(jié)具體介紹 (3)創(chuàng)建并運行虛擬機(jī) root@host:virsh create test.xml 其中“test.xml”指定步驟(2)中創(chuàng)建的xml文件 這樣一個虛擬機(jī)便創(chuàng)建起來了。 1.2.3 xml配置文件的編寫 利用virsh工具創(chuàng)建虛擬機(jī)必須編寫xml配置文件,該文件指定虛擬機(jī)的各項參數(shù),比如虛擬機(jī)名稱、磁盤鏡像的位置、內(nèi)存大小、顯示配置等等。下面給出一個簡單的配置文件的例子。 #test.xml
<domain type='qemu'> <name>windowsXP</name> <uuid></uuid> <memory>500000</memory> <currentMemory>500000</currentMemory> <vcpu>1</vcpu> <os> <type arch='i686'machine='pc'>hvm</type> <boot dev='hd'/> <boot dev='cdrom'/> </os> <devices> <emulator>/usr/bin/qemu-system-x86_64</emulator> <disk type='file' device='cdrom'> <sourcefile='/home/turnupthesun/kvm/windowsXP.iso'/> <target dev='hdc'/> <readonly/> </disk> <disk type='file' device='disk'>
<sourcefile='/home/turnupthesun/kvm/windowsXP.img'/> <target dev='hda'/> </disk> <graphicstype='vnc' port='14' listen='127.0.0.1'/> </devices> </domain> 下面介紹其中幾個比較重要的元素及屬性。 (1)<domain>元素的type屬性指定運行域的虛擬機(jī)管理器,針對kvm應(yīng)當(dāng)選擇“qemu”。 (2)<name>元素的內(nèi)容指定域的名字。 (3)<memory>元素和<currentMemory>元素的內(nèi)容非別指定啟動時為域分配的最大內(nèi)存和實際分配的內(nèi)存。 (4)<os></os>元素之間的內(nèi)容用來指定操作系統(tǒng)啟動的一些信息。其中重復(fù)的<boot>元素形成了一個啟動順序表,比如例子中先從磁盤啟動,磁盤無法啟動再從光盤啟動。 (5)<disk>元素的device屬性指明不同的設(shè)備,<source>標(biāo)簽的file屬性指明這些設(shè)備的位置。 1.3 如何通過圖形化界面virt-manager來創(chuàng)建虛擬機(jī) Virt-manger既虛擬機(jī)管理器,是創(chuàng)建和管理虛擬客戶端的圖形工具。具體的操作步驟為: ① 從控制臺窗口啟動這個工具,從root身份輸入virt-manager命令,點擊file菜單 的”新建”選項。 ② virt-manager顯示兩種虛擬化方法:Qemu/KVM或者Xen,這里選擇Qemu/KVM作 為hypervisor。 ③ 選擇虛擬機(jī)名稱和指定一種安裝方法,通過網(wǎng)絡(luò)安裝服務(wù)器或者本地CD/DVD驅(qū)動包括本地ISO文件,在此我用本地ISO的安裝方法。 ④ 輸入本地ISO文件路徑和文件名(假設(shè)本地ISO的路徑就在根目錄下,名稱為Mini-BT3.6.1.iso) ⑤ 設(shè)置虛擬機(jī)使用的內(nèi)存容量和處理器數(shù)量。 ⑥ 配置虛擬機(jī)的存儲方法。對于存儲后端有兩種選擇:物理存儲設(shè)備或者使用之前建立的磁盤文件。如果處于簡單測試,創(chuàng)建文件作為存儲后端。當(dāng)創(chuàng)建虛擬磁盤時,默認(rèn)為10GB。 ⑦ 網(wǎng)絡(luò)配置,在這里選擇NAT方式。 這樣一個虛擬機(jī)就開始啟動起來了,將會出現(xiàn)啟動界面,最后出現(xiàn)虛擬機(jī)中操作系統(tǒng)的界面。
二.libvirt函數(shù)庫如何實現(xiàn)虛擬機(jī)創(chuàng)建 2.1 virsh工具”create”命令源碼 在libvirt軟件包安裝完成之后,就可以看到libvirt的源碼,這個源碼實現(xiàn)了很多的開發(fā)虛擬化軟件的用戶接口,也就是開發(fā)的API。里面也實現(xiàn)了工具virsh,這個工具也實現(xiàn)了很多的功能。在/tools下面有一個virsh.c,這個文件里面實現(xiàn)virsh的功能,這里就具體把創(chuàng)建這部分代碼選取出來。 /* * "create" command */ static const vshCmdInfo info_create[] ={ {"help", N_("create a domain from an XML file")}, {"desc", N_("Create a domain.")}, {NULL, NULL} }; static const vshCmdOptDef opts_create[]= { {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containingan XML domain description")}, #ifndef WIN32 {"console", VSH_OT_BOOL, 0, N_("attach to console after creation")}, #endif {"paused", VSH_OT_BOOL, 0, N_("leave the guest pausedafter creation")}, {"autodestroy", VSH_OT_BOOL, 0, N_("automatically destroythe guest when virsh disconnects")}, {NULL, 0, 0, NULL} }; static bool cmdCreate(vshControl *ctl, const vshCmd*cmd) { virDomainPtr dom; const char *from = NULL; bool ret = true; char *buffer; #ifndef WIN32 int console = vshCommandOptBool(cmd, "console"); #endif unsigned int flags = VIR_DOMAIN_NONE; if (!vshConnectionUsability(ctl, ctl->conn)) return false; if (vshCommandOptString(cmd, "file", &from) <= 0) return false; if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) return false; if (vshCommandOptBool(cmd, "paused")) flags |= VIR_DOMAIN_START_PAUSED; if (vshCommandOptBool(cmd, "autodestroy"))
flags |= VIR_DOMAIN_START_AUTODESTROY; dom = virDomainCreateXML(ctl->conn, buffer, flags); VIR_FREE(buffer); if (dom != NULL) { vshPrint(ctl, _("Domain %s created from %s\n"), virDomainGetName(dom), from); #ifndef WIN32 if (console) cmdRunConsole(ctl, dom,NULL); #endif virDomainFree(dom); } else { vshError(ctl, _("Failed to create domain from %s"), from); ret = false; } return ret; } 代碼的講解: ⑴ typedef struct{ const char *name; const char *data; }vshCmdInfo; 上面這個結(jié)構(gòu)體是關(guān)于命令的鍵值對信息,命令一般包含兩個名稱:命令的名字和命令的描述信息。 ⑵ typedef struct{ const char *name; vshCmdOptType type; unsigned int flags; const char *help; }vshCmdOptDef; 上面這個結(jié)構(gòu)體是關(guān)于命令選項的定義,該結(jié)構(gòu)體一般包括四個字段:選項的名稱,選項類型,標(biāo)志,幫助信息。其中選項類型包括:bool類型,字符串類型,整型,字符數(shù)據(jù),剩余的參數(shù)。 ⑶ 在cmdCreate主程序中有一個特別重要的函數(shù):virDomainCreateXML(),這個函數(shù)的最初原型是: virDomainPtr virDomainCreateXML (virConnectPtr conn,const char*xmlDesc,unsigned int flags),這個函數(shù)是基于一個指定的XML文件來創(chuàng)建一個虛擬機(jī),其中conn表示一個指向hypervisor的連接,xmlDesc表示一個XML文件,flags表示命令選項的標(biāo)志。 2.2 通過libvirt創(chuàng)建虛擬機(jī)的關(guān)鍵API 通過分析2.1中的virsh源碼我們可以看出,使用libvirt進(jìn)行虛擬機(jī)創(chuàng)建要調(diào)用兩個關(guān)鍵的API-- virFileReadAll和virDomainCreateXML,下面分別進(jìn)行說明。 2.2.1 virFileReadAll 該函數(shù)原型為intvirFileReadAll(const char *path, int maxlen, char **buf),功能是將參數(shù)“path”指定路徑的文件內(nèi)容讀到一個緩沖區(qū)中,并將緩沖區(qū)地址記錄在參數(shù)“*buf”中,而參數(shù)“maxlen”指定文件的最大長度。利用該API,我們可以將xml配置文件都到一個緩沖區(qū)中,以方便接下來的使用。 2.2.2virDomainCreateXML 該函數(shù)原型為virDomainPtr virDomainCreateXML (virConnectPtrconn, const char * xmlDesc, unsigned int flags),功能是根據(jù)參數(shù)“xmlDesc”定義的配置方式創(chuàng)建一個域并返回該域的指針。參數(shù)“conn”是指向虛擬機(jī)管理器的指針,而通過設(shè)置不同的“flags”標(biāo)志,可以使創(chuàng)建的域具有不同的屬性。
三. 利用libvirt庫編寫自己的虛擬機(jī)創(chuàng)建程序 Virsh命令用來創(chuàng)建虛擬機(jī)的命令是:virsh create,這個命令主要是從給定的XML文件生成客戶端并啟動客戶端。 下面用一個測試?yán)觼碚f明如何通過virsh命令來創(chuàng)建虛擬機(jī)的。 具體的操作實踐步驟是:
701.img10G,也就是創(chuàng)建一個大小為10G的虛擬硬盤。 2. 編寫一個xml文件,這個文件里面包含啟動操作系統(tǒng)的一些特征,比如:內(nèi)存容量,操作系統(tǒng)位置,虛擬硬盤位置等等,其實有很多的字段,可以簡寫一個xml文件,如果有些字段沒有定義,那么系統(tǒng)就會默認(rèn),下面給出一個xml文件,命名為701.xml,程序為: <domain type='qemu'> <name>linux10.0421</name> <uuid></uuid> <memory>512000</memory> <currentMemory>512000</currentMemory> <vcpu>1</vcpu> <os> <type arch='i686' machine='pc'>hvm</type> <boot dev='cdrom'/> <boot dev='hd'/> </os> <devices> <emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='file' device='cdrom'> <source file='/usr/src/ubuntu-10.04-desktop-i386.iso'/> <target dev='hdc'/> <readonly/> </disk> <disk type='file' device='disk'> <sourcefile='/var/lib/libvirt/images/701.img'/> <target dev='hda'/> </disk> <graphics type='vnc' port='5901'listen='127.0.0.1'/> </devices> </domain> 3. 接著編寫一個c文件,名稱為701.c這個文件主要實現(xiàn)的功能就是調(diào)用這個xml文件來創(chuàng)建并啟動虛擬機(jī)。這個c程序代碼為: #include<stdio.h> #include<stdlib.h> #include<memory.h> #include<libvirt/libvirt.h> const char *from=NULL; static virConnectPtr conn=NULL; #define VIRSH_MAX_XML_FILE 10*1024*1024 void closeConn() { if(conn!=NULL) virConnectClose(conn); } int cmdCreate() { virDomainPtr dom; char *buffer; unsigned int flags=VIR_DOMAIN_NONE; conn=virConnectOpen("qemu:///system"); if(conn==NULL) { fprintf(stderr,"failed to connect tohypervisor/n"); closeConn(); return 0; } if(virFileReadAll(from,VIRSH_MAX_XML_FILE,&buffer)<0) return 0; dom=virDomainCreateXML(conn,buffer,flags); memset(buffer,0,sizeof(buffer)); if(dom!=NULL){
fprintf(stdout,"Domain %screated from %s\n",virDomainGetName(dom),from); virDomainFree(dom); } else{ fprintf(stdout,"Failed to createdomain from %s",from); } } int main(int argc,char *argv[]) { if(argc<2){ fprintf(stdout,"there are too fewparameters,should has two more parameters!"); } from=*++argv; cmdCreate(); return 0; } 4. 在命令窗口中先執(zhí)行g(shù)cc -lvirt -o 701 701.c ,然后執(zhí)行./701 701.xml,就可以看到這個虛擬機(jī)被創(chuàng)建并啟動起來了。
四.KVM內(nèi)核如何實現(xiàn)底層虛擬機(jī)創(chuàng)建功能 4.1 KVM虛擬機(jī)創(chuàng)建和運行虛擬機(jī)的流程 開源的Lbvirt庫實現(xiàn)了很多的虛擬化API,這些API的實現(xiàn)還是要靠底層的KVM內(nèi)核的實現(xiàn),下面重點講講KVM內(nèi)核中是如何實現(xiàn)虛擬機(jī)創(chuàng)建和運行功能的操作系統(tǒng)層的實現(xiàn)。 KVM虛擬機(jī)創(chuàng)建和運行虛擬機(jī)分為用戶態(tài)和核心態(tài)兩個部分,用戶態(tài)主要提供應(yīng)用程序接口,為虛擬機(jī)創(chuàng)建虛擬機(jī)上下文環(huán)境,在libkvm中提供訪問內(nèi)核字符設(shè)備/dev/kvm的接口;內(nèi)核態(tài)為添加到內(nèi)核中的字符設(shè)備/dev/kvm,模塊加載進(jìn)內(nèi)核后,即可進(jìn)行接口用戶空間調(diào)用創(chuàng)建虛擬機(jī)。在創(chuàng)建虛擬機(jī)過程中,kvm字符設(shè)備主要為客戶機(jī)創(chuàng)建kvm數(shù)據(jù)結(jié)構(gòu),創(chuàng)建該虛擬機(jī)的虛擬機(jī)文件描述符及其相應(yīng)的數(shù)據(jù)結(jié)構(gòu)以及創(chuàng)建虛擬機(jī)處理器及其相應(yīng)的數(shù)據(jù)結(jié)構(gòu)。kvm創(chuàng)建虛擬機(jī)的流程如下圖: 根據(jù)上圖就可以大致知道虛擬機(jī)創(chuàng)建和運行的流程了。首先申明一個kvm_context_t變量用以描述用戶態(tài)虛擬機(jī)上下文信息,然后調(diào)用kvm_init()函數(shù)初始化虛擬機(jī)上下文信息;函數(shù)kvm_create()創(chuàng)建虛擬機(jī)實例,該函數(shù)通過ioctl系統(tǒng)調(diào)用創(chuàng)建虛擬機(jī)相關(guān)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)并且返回文件描述符給用戶態(tài)kvm_context_t數(shù)據(jù)結(jié)構(gòu);創(chuàng)建完內(nèi)核虛擬機(jī)數(shù)據(jù)結(jié)構(gòu)后,再創(chuàng)建內(nèi)核pit以及mmio等外設(shè)模擬設(shè)備,然后調(diào)用kvm_create_vcpu()函數(shù)來創(chuàng)建虛擬處理器,kvm_create_vcpu()函數(shù)通過系統(tǒng)調(diào)用向由vm_fd文件描述符指向的虛擬文件調(diào)用創(chuàng)建虛擬處理器,并將虛擬處理器的文件描述符返回給用戶態(tài)程序,供以后的調(diào)度使用;創(chuàng)建完虛擬處理器后,由用戶態(tài)的QEMU程序申請客戶機(jī)用戶空間,用以加載和運行客戶機(jī)代碼;為了使得客戶虛擬機(jī)正確執(zhí)行,必須要在內(nèi)核中為客戶機(jī)建立正確的內(nèi)存映射關(guān)系,即影子頁表信息。因此,申請客戶機(jī)內(nèi)存地址空間之后,調(diào)用函數(shù)kvm_create_phys_mem()創(chuàng)建客戶機(jī)內(nèi)存映射關(guān)系,該函數(shù)主要通過ioctl系統(tǒng)調(diào)用向vm_fd指向隊的虛擬文件調(diào)用設(shè)置內(nèi)核數(shù)據(jù)結(jié)構(gòu)中客戶機(jī)內(nèi)存映射關(guān)系,主要建立影子頁表信息;當(dāng)創(chuàng)建好虛擬處理器和影子頁表后,即可讀取客戶機(jī)到指定分配的空間中,然后調(diào)度虛擬處理器運行。調(diào)度虛擬機(jī)的函數(shù)為kvm_run(),該函數(shù)通過ioctl系統(tǒng)調(diào)用調(diào)用由虛擬處理器文件描述符指向的虛擬文件調(diào)度處理函數(shù)kvm_run()調(diào)度虛擬處理器的執(zhí)行,該系統(tǒng)調(diào)用將虛擬處理器vcpu信息加載到物理處理器中,通過vm_entry執(zhí)行進(jìn)入客戶機(jī)執(zhí)行。在客戶機(jī)正常運行期間kvm_run()函數(shù)不返回,只有發(fā)生以下兩種情況時,函數(shù)返回:1,發(fā)生了I/O事件,如客戶機(jī)發(fā)出讀寫I/O的指令;2,產(chǎn)生了客戶機(jī)和內(nèi)核KVM都無法處理的異常。I/O事件處理完畢后,通過重新調(diào)用KVM_RUN()函數(shù)繼續(xù)調(diào)度客戶機(jī)的執(zhí)行。 4.2 KVM虛擬機(jī)創(chuàng)建和運行虛擬機(jī)的主要函數(shù)分析以及流程 1.函數(shù)kvm_init():該函數(shù)在用戶態(tài)創(chuàng)建一個虛擬機(jī)上下文,用以在用戶態(tài)保存基本的虛擬機(jī)信息,這個函數(shù)是創(chuàng)建虛擬機(jī)的第一個需要調(diào)用的函數(shù),函數(shù)返回一個kvm_context_t結(jié)構(gòu)體。該函數(shù)原型的實現(xiàn)在libkvm.c中,該函數(shù)原型是: kvm_context_t kvm_init(struct kvm_callbacks*callbacks,void *opaque); 參數(shù):callbacks為結(jié)構(gòu)體kvm_callbacks變量,該結(jié)構(gòu)體包含指向函數(shù)的一組指針,用于在客戶機(jī)執(zhí)行過程中因為I/O事件退出到用戶態(tài)的時候處理的回調(diào)函數(shù)。參數(shù)opaque一般未使用。 函數(shù)執(zhí)行基本過程:打開字符設(shè)備dev/kvm,申請?zhí)摂M機(jī)上下文變量kvm_context_t空間,初始化上下文的基本信息:設(shè)置fd文件描述符指向/dev/kvm,禁止虛擬機(jī)文件描述符vm_fd(-1),設(shè)置I/O事件回調(diào)函數(shù)結(jié)構(gòu)體,設(shè)置IRQ和PIT的標(biāo)志位以及內(nèi)存頁面記錄的標(biāo)志位。 用戶態(tài)數(shù)據(jù)結(jié)構(gòu)kvm_context_t用以描述虛擬機(jī)實例的用戶態(tài)上下文信息。在kvm_common.h文件里面有kvm_context的結(jié)構(gòu)體定義。 structkvm_context { /// Filedescriptor to /dev/kvm int fd; int vm_fd; int vcpu_fd[MAX_VCPUS]; struct kvm_run *run[MAX_VCPUS]; /// Callbacks that KVM uses to emulatevarious unvirtualizable functionality struct kvm_callbacks *callbacks; void *opaque; /// A pointer to the memory used as thephysical memory for the guest void *physical_memory; /// is dirty pages logging enabled for allregions or not int dirty_pages_log_all; /// memory regions parameters struct kvm_memory_regionmem_regions[KVM_MAX_NUM_MEM_REGIONS]; /// do not create in-kernel irqchip if set int no_irqchip_creation; /// in-kernel irqchip status int irqchip_in_kernel; }; 各個數(shù)據(jù)域的解釋為: int fd :指向內(nèi)核標(biāo)準(zhǔn)字符設(shè)備/dev/kvm的文件描述符。 int vm_fd:指向所創(chuàng)建的內(nèi)核虛擬機(jī)數(shù)據(jù)結(jié)構(gòu)相關(guān)文件的文件描述符。 intvcpu_fd[MAX_VCPUS]:指向虛擬機(jī)所有的虛擬處理器的文件描述符數(shù)組。 struct kvm_run*run[MAX_VCPUS]:指向虛擬機(jī)運行環(huán)境上下文的指針數(shù)組。 struct kvm_callbacks*call_backs: 回調(diào)函數(shù)結(jié)構(gòu)體指針,該結(jié)構(gòu)體用于處理用戶態(tài)I/O事件。 void *opaque:指針(還未弄清楚) int dirty_page_log_all:設(shè)置是否記錄臟頁面的標(biāo)志。 int no_ira_creation: 用于設(shè)置是否再kernel里設(shè)置irq芯片。 int_irqchip_in_kernel:內(nèi)核中irqchip的狀態(tài) structkvm_callbacks:該結(jié)構(gòu)體用于在用戶態(tài)中處理I/O事件,在KVM中調(diào)用KVM_QEMU實現(xiàn),主要包含的數(shù)據(jù)域為: int (*inb)(void *opaque, uint16_t addr,uint8_t *data):用于模擬客戶機(jī)執(zhí)行8位的inb指令。 int (*inw)(void *opaque, uint16_t addr,uint16_t *data):用于模擬客戶機(jī)執(zhí)行16位的inw指令。 int (*inl)(void *opaque, uint16_t addr,uint32_t *data):用于模擬客戶機(jī)執(zhí)行32位的inl指令。 int (*outb)(void *opaque, uint16_t addr,uint8_t data):用于模擬客戶機(jī)執(zhí)行8位的outb指令。 int (*outw)(void *opaque, uint16_t addr,uint16_t data):用于模擬客戶機(jī)執(zhí)行16位的outw指令。 int (*outl)(void *opaque, uint16_t addr,uint32_t data):用于模擬客戶機(jī)執(zhí)行32位的outl指令。 int (*mmio_read)(void *opaque, uint64_taddr, uint8_t *data,int len):用于模擬客戶機(jī)執(zhí)行mmio讀指令。 int (*mmio_write)(void *opaque, uint64_taddr, uint8_t *data,int len):用于模擬客戶機(jī)執(zhí)行mmio寫指令。 int (*debug)(void *opaque, void *env,struct kvm_debug_exit_arch *arch_info):用戶客戶機(jī)調(diào)試的回調(diào)函數(shù)。 int (*halt)(void *opaque, int vcpu):用于客戶機(jī)執(zhí)行halt指令的響應(yīng)。 int (*shutdown)(void *opaque, void *env):用于客戶機(jī)執(zhí)行shutdown指令的響應(yīng)。 int (*io_window)(void *opaque):用于獲得客戶機(jī)io_windows。 int (*try_push_interrupts)(void *opaque):用于注入中斷的回調(diào)函數(shù)。 void (*push_nmi)(void *opaque):用于注入nmi中斷的函數(shù)。 void (*post_kvm_run)(void *opaque, void*env);用戶得到kvm運行狀態(tài)函數(shù)。 int (*pre_kvm_run)(void *opaque, void*env);用于獲得kvm之前運行狀態(tài)的函數(shù) int (*tpr_access)(void *opaque, int vcpu,uint64_t rip, int is_write);獲得tpr訪問處理函數(shù) int (*powerpc_dcr_read)(int vcpu, uint32_tdcrn, uint32_t *data);用于powerpc的dcr讀操作 nt (*powerpc_dcr_write)(int vcpu, uint32_tdcrn, uint32_t data);用于powerpc的dcr寫操作 int (*s390_handle_intercept)(kvm_context_tcontext, int vcpu,struct kvm_run *run);用于s390的中斷處理。 int (*s390_handle_reset)(kvm_context_tcontext, int vcpu,struct kvm_run *run);用于s390的重設(shè)處理。 }
當(dāng)客戶機(jī)執(zhí)行I/O事件或者停機(jī)操作等事件時,KVM會交給用戶態(tài)的QEMU模擬外部I/O事件,調(diào)用這個結(jié)構(gòu)體指向的相關(guān)的函數(shù)進(jìn)行處理。 Struct kvm_run: 用于KVM運行時一些的一些狀態(tài)信息。主要包含的數(shù)據(jù)域為: __u8 request_interrupt_window; __u8 padding1[7]; __u32 exit_reason; __u8 ready_for_interrupt_injection; __u8 if_flag; __u8 padding2[2]; /* in (pre_kvm_run), out (post_kvm_run) */ __u64 cr8; __u64 apic_base; union { /* KVM_EXIT_UNKNOWN */ struct { __u64 hardware_exit_reason; 記錄退出原因 } hw; /* KVM_EXIT_FAIL_ENTRY */ 客戶機(jī)執(zhí)行過程中執(zhí)行VM_ENTRY失敗。 struct { __u64hardware_entry_failure_reason; } fail_entry; /* KVM_EXIT_EXCEPTION */ 客戶機(jī)因為異常退出 struct { __u32exception; __u32error_code; } ex; /* KVM_EXIT_IO */ 客戶機(jī)因為IO事件退出。 struct kvm_io { #define KVM_EXIT_IO_IN 0 #define KVM_EXIT_IO_OUT 1 __u8 direction; __u8 size; /* bytes */ __u16 port; __u32 count; __u64 data_offset; /* relative to kvm_runstart */ } io; struct { struct kvm_debug_exit_arch arch; } debug; /* KVM_EXIT_MMIO */ 客戶機(jī)因為MMIO退出 struct { __u64 phys_addr; __u8 data[8]; __u32 len; __u8 is_write; } mmio; /* KVM_EXIT_HYPERCALL */ 客戶機(jī)退出的超調(diào)用參數(shù)。 struct { __u64 nr; __u64 args[6]; __u64 ret; __u32 longmode; __u32 pad; } hypercall; /*KVM_EXIT_TPR_ACCESS */ 客戶機(jī)退出訪問TPR參數(shù) struct { __u64rip; __u32is_write; __u32pad; } tpr_access; /* KVM_EXIT_S390_SIEIC */ 和S390相關(guān)數(shù)據(jù) struct { __u8 icptcode; __u64 mask; /* psw upper half */ __u64 addr; /* psw lower half */ __u16 ipa; __u32 ipb; } s390_sieic; /* KVM_EXIT_S390_RESET */ #define KVM_S390_RESET_POR 1 #define KVM_S390_RESET_CLEAR 2 #define KVM_S390_RESET_SUBSYSTEM 4 #define KVM_S390_RESET_CPU_INIT 8 #define KVM_S390_RESET_IPL 16 __u64 s390_reset_flags; /* KVM_EXIT_DCR */ struct { __u32dcrn; __u32data; __u8 is_write; } dcr; /* Fix the size of the union. */ char padding[256]; 2. 函數(shù)kvm_create():該函數(shù)主要用于創(chuàng)建一個虛擬機(jī)內(nèi)核環(huán)境。該函數(shù)原型為: int kvm_create(kvm_context_t kvm,unsignedlong phys_mem_bytes, void **phys_mem); 參數(shù):kvm_context_t 表示傳遞的用戶態(tài)虛擬機(jī)上下文環(huán)境,phys_mem_bytes表示需要創(chuàng)建的物理內(nèi)存的大小,phys_mem表示創(chuàng)建虛擬機(jī)的首地址。這個函數(shù)首先調(diào)用kvm_create_vm()分配IRQ并且初始化為0,設(shè)置vcpu[0]的值為-1,即不允許調(diào)度虛擬機(jī)執(zhí)行。然后調(diào)用ioctl系統(tǒng)調(diào)用ioctl(fd,KVM_CREATE_VM,0)來創(chuàng)建虛擬機(jī)內(nèi)核數(shù)據(jù)結(jié)構(gòu)struct kvm。 3. 系統(tǒng)調(diào)用函數(shù)ioctl(fd,KVM_CREATE_VM,0),用于在內(nèi)核中創(chuàng)建和虛擬機(jī)相關(guān)的數(shù)據(jù)結(jié)構(gòu)。該函數(shù)原型為: Static long kvm_dev_ioctl(struct file *filp,unsigned intioctl, unsignedlong arg);其中ioctl表示命令。這個函數(shù)調(diào)用kvm_dev_ioctl_create_vm()創(chuàng)建虛擬機(jī)實例內(nèi)核相關(guān)數(shù)據(jù)結(jié)構(gòu)。該函數(shù)首先通過內(nèi)核中kvm_create_vm()函數(shù)創(chuàng)建內(nèi)核中kvm上下文struct kvm,然后通過函數(shù) Anno_inode_getfd(“kvm_vm”,&kvm_vm_fops,kvm,0)返回該虛擬機(jī)的文件描述符,返回給用戶調(diào)用函數(shù),由2中描述的函數(shù)賦值給用戶態(tài)虛擬機(jī)上下文變量中的虛擬機(jī)描述符kvm_vm_fd。 4. 內(nèi)核創(chuàng)建虛擬機(jī)kvm對象后,接著調(diào)用kvm_arch_create函數(shù)用于創(chuàng)建一些體系結(jié)構(gòu)相關(guān)的信息,主要包括kvm_init_tss、kvm_create_pit以及kvm_init_coalsced_mmio等信息。然后調(diào)用kvm_create_phys_mem創(chuàng)建物理內(nèi)存,函數(shù)kvm_create_irqchip用于創(chuàng)建內(nèi)核irq信息,通過系統(tǒng)調(diào)用ioctl(kvm->vm_fd,KVM_CREATE_IRQCHIP)。 5,函數(shù)kvm_create_vcpu():用于創(chuàng)建虛擬處理器。該函數(shù)原型為: int kvm_create_vcpu(kvm_context_t kvm, intslot); 參數(shù):kvm表示對應(yīng)用戶態(tài)虛擬機(jī)上下文,slot表示需要創(chuàng)建的虛擬處理器的個數(shù)。 該函數(shù)通過ioctl系統(tǒng)調(diào)用ioctl(kvm->vm_fd,KVM_CREATE_VCPU,slot)創(chuàng)建屬于該虛擬機(jī)的虛擬處理器。該系統(tǒng)調(diào)用函數(shù): Static init kvm_vm_ioctl_create_vcpu(struct*kvm, n) 參數(shù)kvm為內(nèi)核虛擬機(jī)實例數(shù)據(jù)結(jié)構(gòu),n為創(chuàng)建的虛擬CPU的數(shù)目。 6,函數(shù)kvm_create_phys_mem()用于創(chuàng)建虛擬機(jī)內(nèi)存空間,該函數(shù)原型: Void * kvm_create_phys_mem(kvm_context_tkvm,unsigned long phys_start,unsigned len,int log,int writable); 參數(shù):kvm 表示用戶態(tài)虛擬機(jī)上下文信息,phys_start為分配給該虛擬機(jī)的物理起始地址,len表示內(nèi)存大小,log表示是否記錄臟頁面,writable表示該段內(nèi)存對應(yīng)的頁表是否可寫。 該函數(shù)首先申請一個結(jié)構(gòu)體kvm_userspace_memory_region 然后通過系統(tǒng)調(diào)用KVM_SET_USER_MEMORY_REGION來設(shè)置內(nèi)核中對應(yīng)的內(nèi)存的屬性。該系統(tǒng)調(diào)用函數(shù)原型: Ioctl(int kvm->vm_fd,KVM_SET_USER_MEMORY_REGION,&memory); 參數(shù):第一個參數(shù)vm_fd為指向內(nèi)核虛擬機(jī)實例對象的文件描述符,第二個參數(shù)KVM_SET_USER_MEMORY_REGION為系統(tǒng)調(diào)用命令參數(shù),表示該系統(tǒng)調(diào)用為創(chuàng)建內(nèi)核客戶機(jī)映射,即影子頁表。第三個參數(shù)memory表示指向該虛擬機(jī)的內(nèi)存空間地址。系統(tǒng)調(diào)用首先通過參數(shù)memory通過函數(shù)copy_from_user從用戶空間復(fù)制struct_user_momory_region 變量,然后通過kvm_vm_ioctl_set_memory_region函數(shù)設(shè)置內(nèi)核中對應(yīng)的內(nèi)存域。該函數(shù)原型: Int kvm_vm_ioctl_set_memory_region(struct*kvm,struct kvm_usersapce_memory_region *mem,int user_alloc);該函數(shù)再調(diào)用函數(shù)kvm_set_memory_resgion()設(shè)置影子頁表。當(dāng)這一切都準(zhǔn)備完畢后,調(diào)用kvm_run()函數(shù)即可調(diào)度執(zhí)行虛擬處理器。 7,函數(shù)kvm_run():用于調(diào)度運行虛擬處理器。該函數(shù)原型為: Int kvm_run(kvm_context_t kvm,int vcpu,void *env) 該函數(shù)首先得到vcpu的描述符,然后調(diào)用系統(tǒng)調(diào)用ioctl(fd,kvm_run,0)調(diào)度運行虛擬處理器。Kvm_run函數(shù)在正常運行情況下并不返回,除非發(fā)生以下事件之一:一是發(fā)生了I/O事件,I/O事件由用戶態(tài)的QEMU處理;一個是發(fā)生了客戶機(jī)和KVM都無法處理的異常事件。KVM_RUN()中返回截獲的事件,主要是I/O以及停機(jī)等事件。
|
|
來自: 浪子小新 > 《kvm虛擬機(jī)》