使用KVM API實現Emulator Demo

2021-07-16 01:27:17 字數 4250 閱讀 6758

完整的code在這裡: 

這個code其實是很久以前寫的,以前在team內部分享過,用來幫助大家理解kvm工作原理。現在既然要開始寫code了,就用這個先來個開端。

當然我不可能寫乙個完整的qemu,只是寫出qemu中最基本的那些code。這個虛擬機器只有乙個vcpu和512000000位元組記憶體(其實富裕了) 可以進行一些i/o,當然這些i/o的結果只能導致一些print,沒有實際模擬任何裝置。所以所能執行的guest也很簡單。

首先來看看guest有多簡單。23

4567

89

.globl

_start

.code16

_start:

xorw

%ax,

%axloop1:

out%ax

,$0x10

inc%ax

jmploop1

不熟悉彙編也沒關係,這code很簡單,基本也能猜到幹啥了。對,guest只是基於at&t彙編寫的乙個在8086模式下的死迴圈,不停的向埠0x10寫東西。目標就是讓這個guest跑起來了。

我們的目標就是讓這個guest能執行起來。下面開始看我們虛擬機器的code了。

我們先來看看main函式:23

4567

891011

1213

1415

1617

1819

2021

2223

242526

int

main

(int

argc

,char

**argv)if

(kvm_create_vm

(kvm

,ram_size

)<0)

load_binary

(kvm

);// only support one vcpu now

kvm->

vcpu_number=1

;kvm

->

vcpus

=kvm_init_vcpu

(kvm,0

,kvm_cpu_thread

);kvm_run_vm

(kvm

);kvm_clean_vm

(kvm

);kvm_clean_vcpu

(kvm

->

vcpus

);kvm_clean

(kvm);}

這裡正是第乙個kvm基本原理: 乙個虛擬機器就是乙個程序,我們的虛擬機器從這個main函式開始

讓我先來看看kvm_init。這裡很簡單,就是開啟了/dev/kvm裝置,這是kvm的入口,對kvm的所有操作都是通過對檔案描述符上執行ioctl來完成。 這裡很簡單,就是開啟kvm裝置,然後將檔案描述符返回到我自己建立的乙個結構體當中。

然後我們就開始建立乙個vm,然後為其分配記憶體。

kvm

->

vm_fd

=ioctl

(kvm

->

dev_fd

,kvm_create_vm,0

);

建立乙個虛擬機器很簡單,在kvm裝置上執行這麼乙個ioctl即可,然後會得到新建的vm的檔案描述,用來操作這個vm。

然後我們來分配記憶體,這裡最重要的是struct kvm_userspace_memory_region這個資料結構。23

4567

8

/* for kvm_set_user_memory_region */

struct

kvm_userspace_memory_region

;

memory_size是guest的記憶體的大小。userspace_addr是你為其份分配的記憶體的起始位址,而guest_phys_addr則是這段記憶體對映到guest的什麼物理記憶體位址。

這裡用mmap建立了一段匿名對映,並將位址置入userspace_addr。隨後來告訴我們的vm這些資訊:

ioctl

(kvm

->

vm_fd

,kvm_set_user_memory_region,&

(kvm

->

mem));

這裡是來操作我們的vm了,不是kvm裝置檔案了。

我們有了記憶體了,現在可以把我們的guest code載入的進來了,這個實現很簡單就是開啟編譯後的二進位制檔案將其寫入我們分配的記憶體空間當中。 這裡所要注意的就是如何編譯guest code,這裡我們編譯出來的是flat binary,不需要什麼elf的封裝。

有了記憶體,下一步就是vcpu了,建立vcpu是在kvm_init_vcpu函式裡。 這裡最重要的操作只有這個:23

vcpu

->

kvm_run_mmap_size

=ioctl

(kvm

->

dev_fd

,kvm_get_vcpu_mmap_size,0

);...

vcpu

->

kvm_run

=mmap

(null

,vcpu

->

kvm_run_mmap_size

,prot_read

|prot_write

,map_shared

,vcpu

->

vcpu_fd,0

);

struct kvm_run是儲存vcpu狀態的乙個資料結構,稍後我們可以看到我們可以從這裡得到當陷入後具體陷入原因。

有了記憶體和vcpu就可以執行了:

pthread_create(&

(kvm

->

vcpus

->

vcpu_thread),(

const

pthread_attr_t*)

null

,kvm

->

vcpus[i

].vcpu_thread_func

,kvm

)

這裡是另乙個kvm基本概念了,乙個vcpu就是乙個執行緒。這裡讓我們為vcpu建立乙個執行緒。

最終我們到了最關鍵的部分了,就是這個vcpu執行緒。其實他就是乙個迴圈。 當迴圈開始的時候,我們讓他執行guest code:

ret

=ioctl

(kvm

->

vcpus

->

vcpu_fd

,kvm_run,0

)

到這裡這就是這個virtualizer的全部了. 如果你想體驗一下,只需要執行make。23

45

:~/code/kvmsample$ make

cc -c -o main.o main.c

gcc main.c -o kvmsample -lpthread

as -32 test.s -o test.o

ld -m elf_i386 --oformat binary -n -e _start -ttext 0x10000 -o test.bin test.o

然後執行kvmsample23

4567

891011

1213

1415

$ ./kvmsample

read size: 712288

kvm start run

kvm_exit_io

out port: 16, data: 0

kvm start run

kvm_exit_io

out port: 16, data: 1

kvm start run

kvm_exit_io

out port: 16, data: 2

kvm start run

kvm_exit_io

out port: 16, data: 3

....

其實qemu裡面的code也就是這樣,你也可以在其中找到這個loop,只不過它被qemu內部的各種裝置框架所隱藏起來了。

使用Apache HTTPServer實現負載均衡

採用http 模式配置方法 修改conf目錄下的httpd.conf檔案 第一步 載入module loadmodule proxy module modules mod proxy.so loadmodule proxy balancer module modules mod proxy bala...

使用陣列實現棧,使用單向鍊錶實現佇列

棧 源 使用陣列實現棧的 出棧 入棧 返回棧頂元素 取得棧的大小 及棧的複製 最重要的是定義結構體,結構體內部兩個屬性,乙個 int 型陣列,乙個 top 表示陣列內的有效資料為 top 個。include include include typedef int datetype define ma...

使用 Mahout 實現集群

mahout 支援一些集群演算法實現 都是使用 map reduce 編寫的 它們都有一組各自的目標和標準 從實際的角度來說,名稱和實現並不如它們生成的結果重要。了解了這一點之後,我將展示 k means 的執行原理,而其餘內容將由您自己去研究。請記住,要有效執行每個演算法,您需要滿足它們各自的的需...