KVM虛擬機創建功能詳細講解
一.KVM虛擬機創建的用戶操作
對于用戶或者管理員來說,虛擬機的創建有著很多的方法,例如:kvm自帶命令行工
具、使用virsh命令來創建、使用具有圖形界面的virt-manager等等。但是它們底層實現的原理都是一樣的,而且它們基本上都是通過開源的虛擬化庫Libvirt來開發的。下面就講一講三種用戶可以創建虛擬機的方式。
1.1 利用kvm自帶命令行工具進行創建
kvm常用命令如下:
參數 |
示例 |
說明 |
-hda |
-hda /data/windows.img |
指定windows.img作為硬盤鏡像 |
-cdrom |
-cdrom /data/windows.iso |
指定windows.iso作為光盤鏡像 |
-boot |
-boot c |
從硬盤啟動 |
|
-boot d |
從光盤啟動 |
-m |
-m 512 |
分配512M內存給虛擬系統 |
-vnc |
-vnc :0 |
作為vnc服務器 |
-cpu |
-cpu ? |
列出支持的CPU |
|
-cpu core2duo |
指定CPU為core2duo |
-smp |
-smp 2 |
指定虛擬機有2個CPU |
-net |
-net nic |
為虛擬機網卡(默認為tap0) |
|
-net tap |
系統分配tap設備(默認為tap0)1 |
|
-net nic -net tap |
將虛擬機的網卡eth0連接真機里的tap0 |
具體創建一個虛擬機的步驟如下:
(1)生成硬盤鏡像文件
root@host:kvm-img create -f rawtest.img 10G
其中“-f raw”指定鏡像文件的格式為“raw”,“10G”指定鏡像文件大小。
(2)從光盤啟動虛擬機來安裝操作系統
root@host:kvm -boot d -hda test.img-cdrom test.iso -m 512
其中“-boot d”指定虛擬機從光盤啟動,“-hda test.img”指定硬盤鏡像的位置,“-cdrom test.iso”指定光盤鏡像的位置,“-m 512”指定虛擬機的內存為512M。
(3)安裝操作系統后便可直接從硬盤啟動虛擬機
root@host:kvm -boot c -hda test.img-m 512
1.2 利用virsh命令行工具進行創建
1.2.1 virsh工具簡介
Virsh是由一個名叫libvirt的軟件提供的管理工具,提供管理虛擬機比較高級的能力。Virsh可以管理KVM以及xen等虛擬機。
下面是virsh的一些常見的命令行選項:
命?令? |
Description |
help |
打?印?基?本?幫?助?信?息?。? |
list |
列?出?所?有?客?戶?端?。? |
dumpxml |
輸?出?客?戶?端? XML 配?置?文?件?。? |
create |
從? XML 配?置?文?件?生?成?客?戶?端?并?啟?動?新?客?戶?端?。? |
start |
啟?動?未?激?活?的?客?戶?端?。? |
destroy |
強?制?客?戶?端?停?止?。? |
define |
為?客?戶?端?輸?出? XML 配?置?文?件?。? |
domid |
顯?示?客?戶?端? ID。? |
domuuid |
顯?示?客?戶?端? UUID。? |
dominfo |
顯?示?客?戶?端?信?息?。? |
domname |
顯?示?客?戶?端?名?稱?。? |
domstate |
顯?示?客?戶?端?狀?態?。? |
quit |
退?出?這?個?互?動?終?端?。? |
reboot |
重?新?啟?動?客?戶?端?。? |
restore |
恢?復?以?前?保?存?在?文?件?中?的?客?戶?端?。? |
resume |
恢?復?暫?停?的?客?戶?端?。? |
save |
將?客?戶?端?當?前?狀?態?保?存?到?某?個?文?件?中?。? |
shutdown |
關?閉?某?個?域?。? |
suspend |
暫?停?客?戶?端?。? |
undefine |
刪?除?與?客?戶?端?關?聯?的?所?有?文?件?。? |
migrate |
將?客?戶?端?遷?移?到?另?一?臺?主?機?中?。? |
命?令? |
Description |
setmem |
為?客?戶?端?設?定?分?配?的?內?存?。? |
setmaxmem |
為?管?理?程?序?設?定?內?存?上?限?。? |
setvcpus |
修?改?為?客?戶?端?分?配?的?虛?擬? CPU 數?目?。? |
vcpuinfo |
顯?示?客?戶?端?的?虛?擬? CPU 信?息?。? |
vcpupin |
控?制?客?戶?端?的?虛?擬? CPU 親?和?性?。? |
domblkstat |
顯?示?正?在?運?行?的?客?戶?端?的?塊?設?備?統?計?。? |
domifstat |
顯?示?正?在?運?行?的?客?戶?端?的?網?絡?接?口?統?計?。? |
attach-device |
使?用? XML 文?件?中?的?設?備?定?義?在?客?戶?端?中?添?加?設?備?。? |
attach-disk |
在?客?戶?端?中?附?加?新?磁?盤?設?備?。? |
attach-interface |
在?客?戶?端?中?附?加?新?網?絡?接?口?。? |
detach-device |
從?客?戶?端?中?分?離?設?備?,使?用?同?樣?的? XML 描?述?作?為?命?令?attach-device。? |
detach-disk |
從?客?戶?端?中?分?離?磁?盤?設?備?。? |
detach-interface |
從?客?戶?端?中?分?離?網?絡?接?口?。? |
命?令? |
Description |
version |
顯?示? virsh 版?本? |
nodeinfo |
有?關?管?理?程?序?的?輸?出?信?息? |
1.2.2 virsh命令來創建虛擬機步驟
(1)生成硬盤鏡像文件
root@host:kvm-img create -f rawtest.img 10G
(2)編寫xml配置文件,這一步在1.2.3節具體介紹
(3)創建并運行虛擬機
root@host:virsh create test.xml
其中“test.xml”指定步驟(2)中創建的xml文件
這樣一個虛擬機便創建起來了。
1.2.3 xml配置文件的編寫
利用virsh工具創建虛擬機必須編寫xml配置文件,該文件指定虛擬機的各項參數,比如虛擬機名稱、磁盤鏡像的位置、內存大小、顯示配置等等。下面給出一個簡單的配置文件的例子。
#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屬性指定運行域的虛擬機管理器,針對kvm應當選擇“qemu”。
(2)<name>元素的內容指定域的名字。
(3)<memory>元素和<currentMemory>元素的內容非別指定啟動時為域分配的最大內存和實際分配的內存。
(4)<os></os>元素之間的內容用來指定操作系統啟動的一些信息。其中重復的<boot>元素形成了一個啟動順序表,比如例子中先從磁盤啟動,磁盤無法啟動再從光盤啟動。
(5)<disk>元素的device屬性指明不同的設備,<source>標簽的file屬性指明這些設備的位置。
1.3 如何通過圖形化界面virt-manager來創建虛擬機
Virt-manger既虛擬機管理器,是創建和管理虛擬客戶端的圖形工具。具體的操作步驟為:
① 從控制臺窗口啟動這個工具,從root身份輸入virt-manager命令,點擊file菜單
的”新建”選項。
② virt-manager顯示兩種虛擬化方法:Qemu/KVM或者Xen,這里選擇Qemu/KVM作
為hypervisor。
③ 選擇虛擬機名稱和指定一種安裝方法,通過網絡安裝服務器或者本地CD/DVD驅動包括本地ISO文件,在此我用本地ISO的安裝方法。
④ 輸入本地ISO文件路徑和文件名(假設本地ISO的路徑就在根目錄下,名稱為Mini-BT3.6.1.iso)
⑤ 設置虛擬機使用的內存容量和處理器數量。
⑥ 配置虛擬機的存儲方法。對于存儲后端有兩種選擇:物理存儲設備或者使用之前建立的磁盤文件。如果處于簡單測試,創建文件作為存儲后端。當創建虛擬磁盤時,默認為10GB。
⑦ 網絡配置,在這里選擇NAT方式。
這樣一個虛擬機就開始啟動起來了,將會出現啟動界面,最后出現虛擬機中操作系統的界面。
二.libvirt函數庫如何實現虛擬機創建
2.1 virsh工具”create”命令源碼
在libvirt軟件包安裝完成之后,就可以看到libvirt的源碼,這個源碼實現了很多的開發虛擬化軟件的用戶接口,也就是開發的API。里面也實現了工具virsh,這個工具也實現了很多的功能。在/tools下面有一個virsh.c,這個文件里面實現virsh的功能,這里就具體把創建這部分代碼選取出來。
/*
* "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;
上面這個結構體是關于命令的鍵值對信息,命令一般包含兩個名稱:命令的名字和命令的描述信息。
⑵ typedef struct{
const char *name;
vshCmdOptType type;
unsigned int flags;
const char *help;
}vshCmdOptDef;
上面這個結構體是關于命令選項的定義,該結構體一般包括四個字段:選項的名稱,選項類型,標志,幫助信息。其中選項類型包括:bool類型,字符串類型,整型,字符數據,剩余的參數。
⑶ 在cmdCreate主程序中有一個特別重要的函數:virDomainCreateXML(),這個函數的最初原型是: virDomainPtr virDomainCreateXML (virConnectPtr conn,const char*xmlDesc,unsigned int flags),這個函數是基于一個指定的XML文件來創建一個虛擬機,其中conn表示一個指向hypervisor的連接,xmlDesc表示一個 XML文件,flags表示命令選項的標志。
2.2 通過libvirt創建虛擬機的關鍵API
通過分析2.1中的virsh源碼我們可以看出,使用libvirt進行虛擬機創建要調用兩個關鍵的API-- virFileReadAll和virDomainCreateXML,下面分別進行說明。
2.2.1 virFileReadAll
該函數原型為intvirFileReadAll(const char *path, int maxlen, char **buf),功能是將參數“path”指定路徑的文件內容讀到一個緩沖區中,并將緩沖區地址記錄在參數“*buf”中,而參數“maxlen”指定文件的最大長度。利用該API,我們可以將xml配置文件都到一個緩沖區中,以方便接下來的使用。
2.2.2virDomainCreateXML
該函數原型為virDomainPtr virDomainCreateXML (virConnectPtrconn, const char * xmlDesc, unsigned int flags),功能是根據參數“xmlDesc”定義的配置方式創建一個域并返回該域的指針。參數“conn”是指向虛擬機管理器的指針,而通過設置不同的“flags”標志,可以使創建的域具有不同的屬性。
三. 利用libvirt庫編寫自己的虛擬機創建程序
Virsh命令用來創建虛擬機的命令是:virsh create,這個命令主要是從給定的XML文件生成客戶端并啟動客戶端。
下面用一個測試例子來說明如何通過virsh命令來創建虛擬機的。
具體的操作實踐步驟是:
- 首先需要創建虛擬硬盤,為了放置操作系統的地方,命令是:kvm-img create
701.img10G,也就是創建一個大小為10G的虛擬硬盤。
2. 編寫一個xml文件,這個文件里面包含啟動操作系統的一些特征,比如:內存容量,操作系統位置,虛擬硬盤位置等等,其實有很多的字段,可以簡寫一個xml 文件,如果有些字段沒有定義,那么系統就會默認,下面給出一個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這個文件主要實現的功能就是調用這個xml文件來創建并啟動虛擬機。這個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. 在命令窗口中先執行gcc -lvirt -o 701 701.c ,然后執行./701 701.xml,就可以看到這個虛擬機被創建并啟動起來了。
四.KVM內核如何實現底層虛擬機創建功能
4.1 KVM虛擬機創建和運行虛擬機的流程
開源的Lbvirt庫實現了很多的虛擬化API,這些API的實現還是要靠底層的KVM內核的實現,下面重點講講KVM內核中是如何實現虛擬機創建和運行功能的操作系統層的實現。
KVM虛擬機創建和運行虛擬機分為用戶態和核心態兩個部分,用戶態主要提供應用程序接口,為虛擬機創建虛擬機上下文環境,在libkvm中提供訪問內核字符設備/dev/kvm的接口;內核態為添加到內核中的字符設備/dev/kvm,模塊加載進內核后,即可進行接口用戶空間調用創建虛擬機。在創建虛擬機過程中,kvm字符設備主要為客戶機創建kvm數據結構,創建該虛擬機的虛擬機文件描述符及其相應的數據結構以及創建虛擬機處理器及其相應的數據結構。 kvm創建虛擬機的流程如下圖:
根據上圖就可以大致知道虛擬機創建和運行的流程了。首先申明一個kvm_context_t變量用以描述用戶態虛擬機上下文信息,然后調用 kvm_init()函數初始化虛擬機上下文信息;函數kvm_create()創建虛擬機實例,該函數通過ioctl系統調用創建虛擬機相關的內核數據結構并且返回文件描述符給用戶態kvm_context_t數據結構;創建完內核虛擬機數據結構后,再創建內核pit以及mmio等外設模擬設備,然后調用kvm_create_vcpu()函數來創建虛擬處理器,kvm_create_vcpu()函數通過系統調用向由vm_fd文件描述符指向的虛擬文件調用創建虛擬處理器,并將虛擬處理器的文件描述符返回給用戶態程序,供以后的調度使用;創建完虛擬處理器后,由用戶態的QEMU程序申請客戶機用戶空間,用以加載和運行客戶機代碼;為了使得客戶虛擬機正確執行,必須要在內核中為客戶機建立正確的內存映射關系,即影子頁表信息。因此,申請客戶機內存地址空間之后,調用函數kvm_create_phys_mem()創建客戶機內存映射關系,該函數主要通過ioctl系統調用向vm_fd指向隊的虛擬文件調用設置內核數據結構中客戶機內存映射關系,主要建立影子頁表信息;當創建好虛擬處理器和影子頁表后,即可讀取客戶機到指定分配的空間中,然后調度虛擬處理器運行。調度虛擬機的函數為kvm_run(),該函數通過ioctl系統調用調用由虛擬處理器文件描述符指向的虛擬文件調度處理函數 kvm_run()調度虛擬處理器的執行,該系統調用將虛擬處理器vcpu信息加載到物理處理器中,通過vm_entry執行進入客戶機執行。在客戶機正常運行期間kvm_run()函數不返回,只有發生以下兩種情況時,函數返回:1,發生了I/O事件,如客戶機發出讀寫I/O的指令;2,產生了客戶機和內核KVM都無法處理的異常。I/O事件處理完畢后,通過重新調用KVM_RUN()函數繼續調度客戶機的執行。
4.2 KVM虛擬機創建和運行虛擬機的主要函數分析以及流程
1.函數kvm_init():該函數在用戶態創建一個虛擬機上下文,用以在用戶態保存基本的虛擬機信息,這個函數是創建虛擬機的第一個需要調用的函數,函數返回一個kvm_context_t結構體。該函數原型的實現在libkvm.c中,該函數原型是:
kvm_context_t kvm_init(struct kvm_callbacks*callbacks,void *opaque);
參數:callbacks為結構體kvm_callbacks變量,該結構體包含指向函數的一組指針,用于在客戶機執行過程中因為I/O事件退出到用戶態的時候處理的回調函數。參數opaque一般未使用。
函數執行基本過程:打開字符設備dev/kvm,申請虛擬機上下文變量kvm_context_t空間,初始化上下文的基本信息:設置fd文件描述符指向 /dev/kvm,禁止虛擬機文件描述符vm_fd(-1),設置I/O事件回調函數結構體,設置IRQ和PIT的標志位以及內存頁面記錄的標志位。
用戶態數據結構kvm_context_t用以描述虛擬機實例的用戶態上下文信息。在kvm_common.h文件里面有kvm_context的結構體定義。
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;
};
各個數據域的解釋為:
int fd :指向內核標準字符設備/dev/kvm的文件描述符。
int vm_fd:指向所創建的內核虛擬機數據結構相關文件的文件描述符。
intvcpu_fd[MAX_VCPUS]:指向虛擬機所有的虛擬處理器的文件描述符數組。
struct kvm_run*run[MAX_VCPUS]:指向虛擬機運行環境上下文的指針數組。
struct kvm_callbacks*call_backs: 回調函數結構體指針,該結構體用于處理用戶態I/O事件。
void *opaque:指針(還未弄清楚)
int dirty_page_log_all:設置是否記錄臟頁面的標志。
int no_ira_creation: 用于設置是否再kernel里設置irq芯片。
int_irqchip_in_kernel:內核中irqchip的狀態
structkvm_callbacks:該結構體用于在用戶態中處理I/O事件,在KVM中調用KVM_QEMU實現,主要包含的數據域為:
int (*inb)(void *opaque, uint16_t addr,uint8_t *data):用于模擬客戶機執行8位的inb指令。
int (*inw)(void *opaque, uint16_t addr,uint16_t *data):用于模擬客戶機執行16位的inw指令。
int (*inl)(void *opaque, uint16_t addr,uint32_t *data):用于模擬客戶機執行32位的inl指令。
int (*outb)(void *opaque, uint16_t addr,uint8_t data):用于模擬客戶機執行8位的outb指令。
int (*outw)(void *opaque, uint16_t addr,uint16_t data):用于模擬客戶機執行16位的outw指令。
int (*outl)(void *opaque, uint16_t addr,uint32_t data):用于模擬客戶機執行32位的outl指令。
int (*mmio_read)(void *opaque, uint64_taddr, uint8_t *data,int len):用于模擬客戶機執行mmio讀指令。
int (*mmio_write)(void *opaque, uint64_taddr, uint8_t *data,int len):用于模擬客戶機執行mmio寫指令。
int (*debug)(void *opaque, void *env,struct kvm_debug_exit_arch *arch_info):用戶客戶機調試的回調函數。
int (*halt)(void *opaque, int vcpu):用于客戶機執行halt指令的響應。
int (*shutdown)(void *opaque, void *env):用于客戶機執行shutdown指令的響應。
int (*io_window)(void *opaque):用于獲得客戶機io_windows。
int (*try_push_interrupts)(void *opaque):用于注入中斷的回調函數。
void (*push_nmi)(void *opaque):用于注入nmi中斷的函數。
void (*post_kvm_run)(void *opaque, void*env);用戶得到kvm運行狀態函數。
int (*pre_kvm_run)(void *opaque, void*env);用于獲得kvm之前運行狀態的函數
int (*tpr_access)(void *opaque, int vcpu,uint64_t rip, int is_write);獲得tpr訪問處理函數
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的重設處理。
}
當客戶機執行I/O事件或者停機操作等事件時,KVM會交給用戶態的QEMU模擬外部I/O事件,調用這個結構體指向的相關的函數進行處理。
Struct kvm_run: 用于KVM運行時一些的一些狀態信息。主要包含的數據域為:
__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 */ 客戶機執行過程中執行VM_ENTRY失敗。
struct {
__u64hardware_entry_failure_reason;
} fail_entry;
/* KVM_EXIT_EXCEPTION */ 客戶機因為異常退出
struct {
__u32exception;
__u32error_code;
} ex;
/* KVM_EXIT_IO */ 客戶機因為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 */ 客戶機因為MMIO退出
struct {
__u64 phys_addr;
__u8 data[8];
__u32 len;
__u8 is_write;
} mmio;
/* KVM_EXIT_HYPERCALL */ 客戶機退出的超調用參數。
struct {
__u64 nr;
__u64 args[6];
__u64 ret;
__u32 longmode;
__u32 pad;
} hypercall;
/*KVM_EXIT_TPR_ACCESS */ 客戶機退出訪問TPR參數
struct {
__u64rip;
__u32is_write;
__u32pad;
} tpr_access;
/* KVM_EXIT_S390_SIEIC */ 和S390相關數據
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. 函數kvm_create():該函數主要用于創建一個虛擬機內核環境。該函數原型為:
int kvm_create(kvm_context_t kvm,unsignedlong phys_mem_bytes, void **phys_mem);
參數:kvm_context_t 表示傳遞的用戶態虛擬機上下文環境,phys_mem_bytes表示需要創建的物理內存的大小,phys_mem表示創建虛擬機的首地址。這個函數首先調用kvm_create_vm()分配IRQ并且初始化為0,設置vcpu[0]的值為-1,即不允許調度虛擬機執行。然后調用ioctl系統調用 ioctl(fd,KVM_CREATE_VM,0)來創建虛擬機內核數據結構struct kvm。
3. 系統調用函數ioctl(fd,KVM_CREATE_VM,0),用于在內核中創建和虛擬機相關的數據結構。該函數原型為:
Static long kvm_dev_ioctl(struct file *filp,unsigned intioctl, unsignedlong arg);其中ioctl表示命令。這個函數調用kvm_dev_ioctl_create_vm()創建虛擬機實例內核相關數據結構。該函數首先通過內核中kvm_create_vm()函數創建內核中kvm上下文struct kvm,然后通過函數
Anno_inode_getfd(“kvm_vm”,&kvm_vm_fops,kvm,0)返回該虛擬機的文件描述符,返回給用戶調用函數,由2中描述的函數賦值給用戶態虛擬機上下文變量中的虛擬機描述符kvm_vm_fd。
4. 內核創建虛擬機kvm對象后,接著調用kvm_arch_create函數用于創建一些體系結構相關的信息,主要包括kvm_init_tss、 kvm_create_pit以及kvm_init_coalsced_mmio等信息。然后調用kvm_create_phys_mem創建物理內存,函數kvm_create_irqchip用于創建內核irq信息,通過系統調用 ioctl(kvm->vm_fd,KVM_CREATE_IRQCHIP)。
5,函數kvm_create_vcpu():用于創建虛擬處理器。該函數原型為:
int kvm_create_vcpu(kvm_context_t kvm, intslot);
參數:kvm表示對應用戶態虛擬機上下文,slot表示需要創建的虛擬處理器的個數。
該函數通過ioctl系統調用ioctl(kvm->vm_fd,KVM_CREATE_VCPU,slot)創建屬于該虛擬機的虛擬處理器。該系統調用函數:
Static init kvm_vm_ioctl_create_vcpu(struct*kvm, n) 參數kvm為內核虛擬機實例數據結構,n為創建的虛擬CPU的數目。
6,函數kvm_create_phys_mem()用于創建虛擬機內存空間,該函數原型:
Void * kvm_create_phys_mem(kvm_context_tkvm,unsigned long phys_start,unsigned len,int log,int writable);
參數:kvm 表示用戶態虛擬機上下文信息,phys_start為分配給該虛擬機的物理起始地址,len表示內存大小,log表示是否記錄臟頁面,writable表示該段內存對應的頁表是否可寫。
該函數首先申請一個結構體kvm_userspace_memory_region 然后通過系統調用KVM_SET_USER_MEMORY_REGION來設置內核中對應的內存的屬性。該系統調用函數原型:
Ioctl(int kvm->vm_fd,KVM_SET_USER_MEMORY_REGION,&memory);
參數:第一個參數vm_fd為指向內核虛擬機實例對象的文件描述符,第二個參數KVM_SET_USER_MEMORY_REGION為系統調用命令參數,表示該系統調用為創建內核客戶機映射,即影子頁表。第三個參數memory表示指向該虛擬機的內存空間地址。系統調用首先通過參數memory通過函數copy_from_user從用戶空間復制struct_user_momory_region 變量,然后通過kvm_vm_ioctl_set_memory_region函數設置內核中對應的內存域。該函數原型:
Int kvm_vm_ioctl_set_memory_region(struct*kvm,struct kvm_usersapce_memory_region *mem,int user_alloc);該函數再調用函數kvm_set_memory_resgion()設置影子頁表。當這一切都準備完畢后,調用 kvm_run()函數即可調度執行虛擬處理器。
7,函數kvm_run():用于調度運行虛擬處理器。該函數原型為:
Int kvm_run(kvm_context_t kvm,int vcpu,void *env) 該函數首先得到vcpu的描述符,然后調用系統調用ioctl(fd,kvm_run,0)調度運行虛擬處理器。Kvm_run函數在正常運行情況下并不返回,除非發生以下事件之一:一是發生了I/O事件,I/O事件由用戶態的QEMU處理;一個是發生了客戶機和KVM都無法處理的異常事件。 KVM_RUN()中返回截獲的事件,主要是I/O以及停機等事件。
轉自:http://blog.csdn.net/chenglinhust/article/details/7931717