

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、<p><b> 目 錄</b></p><p><b> 1 緒論1</b></p><p> 1. 1課題背景及目的1</p><p> 1.1.1為什么要設計虛擬機1</p><p> 1.1.2虛擬機反對派觀點1</p><p> 1.
2、2國內外研究狀況2</p><p> 1.3課題研究方法2</p><p> 1.4論文構成及研究內容2</p><p> 2 SVM 虛擬機設計3</p><p> 2.1運形時系統(tǒng)與虛擬機3</p><p> 2.2 SVM 虛擬機3</p><p> 2.3 SV
3、M虛擬機處理器設計5</p><p> 2.3.1 機器處理器設計5</p><p> 2.3.2 SVM虛擬機處理器設計6</p><p> 2.4 SVM內存分配8</p><p> 2.5 SVM內存尋址8</p><p> 2.6 SVM 多字節(jié)存儲方式8</p><p
4、> 2.7 SVM輸入輸出9</p><p> 2.8 SVM 中斷10</p><p> 2.9 SVM 匯編器16</p><p> 2.10 SVM 反匯編器18</p><p> 2.11 SVM 調試器18</p><p> 2.11.1概述18</p><p
5、> 2.11.2調試技術19</p><p> 2.11.3 SVM 調試器的實現19</p><p> 2.12 建造運行時系統(tǒng)20</p><p> 2.13 SVM 虛擬機的擴展20</p><p> 3 SVM 虛擬機運行時環(huán)境22</p><p> 3.1SVM執(zhí)行方式22<
6、;/p><p> 3.2 SVM調試方式23</p><p> 4 程序設計處理26</p><p> 4.1程序設計中的宏處理26</p><p> 4.2程序設計中的異常處理26</p><p><b> 總 結26</b></p><p>&l
7、t;b> 致 謝26</b></p><p><b> 參考文獻26</b></p><p><b> 1 緒論</b></p><p> 1. 1課題背景及目的</p><p> 1.1.1為什么要設計虛擬機</p><p> 就軟
8、件工業(yè)的發(fā)展趨勢而言,一方面是需要運行在多種計算機平臺之上的實用系統(tǒng)越來越多,另一方面是以網絡為中心的計算情況越來越多,虛擬機也重新成為軟件工業(yè)的一個潮流。</p><p> 基于單一操作系統(tǒng)的軟件開發(fā)工具已經不能滿足軟件工程師的需要。</p><p> 面對雜亂的信息系統(tǒng)和日新月異的技術發(fā)明,軟件工程師們開始重新審視開發(fā)虛擬機的優(yōu)點。</p><p> 編寫
9、虛擬機是軟件開發(fā)項目的一種,它體現了一種使投資價值最大化的思路。為了從資源方面的投資得到最大的回報,企業(yè)都希望自己花錢開發(fā)出來的軟件的使用期限能夠盡可能地長。把軟件包從一種平臺移植到另一種平臺的工作需要花費不小的成本,而且并不是所有的軟件包都能夠移植到所有的平臺上去,軟件包所能支持的平臺種數是有限度的。大量事實表明,軟件移植工作往往會變成一場噩夢。</p><p> 使用虛擬機就可以在一定程度上避免這類事情的發(fā)
10、生。當遇到一種新硬件平臺或者新操作系統(tǒng)的時候,唯一需要移植的應用級軟件就是虛擬機本身[1]。</p><p> 1.1.2虛擬機反對派觀點</p><p> 有不少人反對使用虛擬機,他們的觀點有許多種,其中最主要的就是虛擬機會降低程序的執(zhí)行性能。他們認為,編譯型語言——如C語言——是以計算機自身的機器碼的形式執(zhí)行的,因此會執(zhí)行得更快些。但這種說法并不一定正確。</p>&
11、lt;p> 純粹的C++代碼并不一定比由虛擬機執(zhí)行的字節(jié)碼(byte code)更快。程序的執(zhí)行時間主要消耗在運行時庫以及內核模式中斷處理例程方面,只有當處理的代碼是完全孤立且沒有調用任何用戶庫或系統(tǒng)調用時(因為程序要等待系統(tǒng)的響應)才能百分之百肯定其機器碼會執(zhí)行得更快。</p><p> 對于企業(yè)應用級系統(tǒng),虛擬級在可移植方面的優(yōu)勢足以彌補這些其實并慢不了多少的性能損失[1]。</p>
12、<p> 1.2國內外研究狀況</p><p> 一個商業(yè)級的虛擬機實現是極其復雜的,一個開源虛擬機bochs 做得很好,其2.0.2版在Win32平臺下的有13萬行源代碼(C/C++)。能在其上運行minux(linux 前身),FreeDSB, Windows95,Windows NT 4.0等操作系統(tǒng)。</p><p> 另一種虛擬機有自己的指令集,它是針對一種語言來
13、設計和實現的。比如JVM(JAVA virtual machine JAVA 虛擬機)。.NET平臺。而由JVM所支持的JAVA語言,目前正得到廣泛的應用。</p><p><b> 1.3課題研究方法</b></p><p> 一臺虛擬機與一臺真實存在的計算機的不同之處在于前者只是一個技術規(guī)范。這類技術規(guī)范由一系列規(guī)則構成,而軟件工程師可以采用任何他自己認為適當
14、的手段來實現這些規(guī)則。這就使虛擬機能夠做到與具體的計算機平臺無關。對一臺虛擬機來說,只要它能夠遵從其技術規(guī)范里的各項規(guī)則,就可以存在于任何一種計算機平臺上,就可以用任何一種計算機語言來編寫[1]。</p><p> 我的畢業(yè)設計所做的虛擬機采用標準C/C++語言編寫,在結構上模仿了8086計算機體系結構。</p><p> 1.4論文構成及研究內容</p><p&g
15、t; 本論文作為對虛擬機開發(fā)的一個嘗試,實現的部分偏重于CPU也即指令執(zhí)行部件。而對于虛擬機這個運行時系統(tǒng)的另一個很重要的部分——中斷處理,由于涉及面太廣,故只是象征性的實現了INT 10H 0EH中斷(向屏幕輸出),INT 20H中斷(退出程序)。</p><p> 我做的虛擬機取名為SVM(simple virual machine)——簡單的虛擬機。</p><p> 論文中將
16、主要說明整個SVM虛擬機的設計實現過程,由于涉及到編程語言C/C++,也參考了一些程序設計書上的內容,在文中一并寫出來。</p><p><b> (以后略……)</b></p><p> 2 SVM 虛擬機設計</p><p> 2.1運形時系統(tǒng)與虛擬機</p><p> 運形時系統(tǒng)(run-time sys
17、tem)是各種計算機程序在其中得以執(zhí)行的一個環(huán)境。運行時系統(tǒng)提供了程序在執(zhí)行時所需要的一切東西。例如,運行時系統(tǒng)要負責為應用程序分配內存,把該應用程序加載到分配好的內存里,然后開始執(zhí)行該程序中的指令。</p><p> 如果該程序通過調用系統(tǒng)調用要求位于底層的操作系統(tǒng)提供服務,該運行時系統(tǒng)還必須負責處理有關的服務請求。例如,如果應用程序需要進行文件I/O操作,運行時系統(tǒng)就必須向它提供一種與磁盤控制器進行通信并提
18、供讀寫訪問的機制。</p><p> 運行時系統(tǒng)的種類有很多。對運行時系統(tǒng)進行分類的一個辦法是把它們按執(zhí)行程序指令的基本方式進行劃分。對那些以處理其本身的機器碼為指令的程序而言,某計算機的處理器和操作系統(tǒng)就構成了與之對應的運行時系統(tǒng),處理器提供了一種執(zhí)行指令的機制。CPU把編碼為數值形式的指令從內存里取出并根據那些指令完成相應的動作,操作系統(tǒng)則實現了(由處理器和操作系統(tǒng)構成的)這個運行時系統(tǒng)的策略部分。CPU負
19、責執(zhí)行指令, 操作系統(tǒng)負責決定事情何時,何地發(fā)生。</p><p> 對那些用機器指令編寫的程序來說,計算機本身就是一個運行時系統(tǒng)。程序的指令由物理CPU在機器級上執(zhí)行,指令執(zhí)行的具體過程由操作系統(tǒng)管理。這類運行時系涉及計算機硬件和軟件。</p><p> 那些指令不由物理處理器來直接執(zhí)行的程序需要一個完全由軟件構成的運行時系統(tǒng)。在這種情況下,程序的指令將由一臺虛擬機來執(zhí)行。虛擬機是類
20、似于計算機的一個軟件程序,它會像真正的處理器那樣取出并執(zhí)行程序指令,但兩者的區(qū)別在于虛擬機的指令執(zhí)行過程發(fā)生在軟件級而不是硬件級,即指令是由軟件而不是硬件執(zhí)行的[1]。</p><p> 2.2 SVM 虛擬機</p><p> SVM虛擬機是通過模擬硬件平臺而實現的。 SVM采用8086指令集。這樣可以省去一些建造虛擬機的重要步驟——比如,指令集不用重建(事實上,我也沒有能力去定義一
21、個完備的指令集)。SVM虛擬機采用單任務方式,每次只運行一個程序。</p><p> 虛擬機總體結構如下:</p><p> 圖2.1 虛擬機總體結構</p><p> SVM 虛擬機源文件組成:</p><p> c8086.h 聲明8086處理器類;</p><p> c8086.cpp
22、 實現8086處理器類;</p><p> ram.h 聲明內存類;</p><p> ram .cpp 實現內存類;</p><p> disasm.h 反匯編函數聲明;</p><p> disasm .cpp 反匯編函數定義;</p><p> debug.h
23、 調試器函數聲明;</p><p> debug.cpp 調試器函數定義;</p><p> fileLoader.h com文件加載函數聲明;</p><p> fileLoader.cpp com文件加載函數定義;</p><p> global.h 全局數據類型定義;</p>&
24、lt;p> test_CPU.cpp SVM測試程序主函數。</p><p><b> 編譯執(zhí)行環(huán)境:</b></p><p> 運行,開發(fā)平臺:Windows 98 SE , Windows 2000 professional SP4</p><p> 編譯器:Visual C++ 6.0/7.0(VC是對標準C/C++支持
25、很好的一個編譯器)</p><p> 2.3 SVM虛擬機處理器設計</p><p> 2.3.1 機器處理器設計</p><p> 中央處理器可以實現為基于寄存器或者基于堆棧的機器。一個基于寄存器的處理器,如Intel 公司的Pentium芯片,有8個用來完成基本運算的32位寄存器。一個基于堆棧的處理器,如Harris半導體公司的RTX32P芯片,有兩個用來
26、完成基本運算的片上堆棧。</p><p> 基于堆棧的處理器在嵌入式系統(tǒng)中比較流行,這是因為這類處理器支持比較短小的程序,在資源有限的場合也能工作得很好。同時函數調用在基于堆棧的機器上完成得也更有效率,因為函數參數都已經被放到堆棧里去了。而在一臺基于寄存器的機器上,函數參數必須逐個收集并壓入堆棧,這就需要做更多的工作。上下文切換在基于堆棧的機器上的開銷也比較小。如果是在一臺基于寄存器的機器上進行上下文切換,就必
27、須把它所有的寄存器的狀態(tài)都保存起來。對擁有大量寄存器的RISC體系結構來說,上下文切換是一個消耗內存的操作?;诙褩5臋C器就不存在這種問題。基于堆棧的計算機可以為每個進程分別準備一個堆棧,切換上下文時只需變一下堆棧就行了。</p><p> 既然有這么多的優(yōu)點,基于堆棧的機器為什么沒有成為計算機體系結構的主流呢?這是因為基于寄存器的處理器有一個極其重要的優(yōu)勢:速度快。寄存器就在CPU的內部,如果是對保存在寄存器
28、里的數據進行運算,那它的運算速度將是非??斓?。Intel 公司在它新推出的64位處理器Itanium里安排了好幾百個片上寄存器,目的就是為了讓程序操作盡可能多地在芯片上執(zhí)行。再看基于堆棧地處理器,它們的片上堆棧幾乎總是會延伸到內存里去。這就造成了這樣一種后果:即使執(zhí)行的是一個面向堆棧的基本操作,處理器也不得不到內存里去讀取數據。這就大大降低了基于堆棧的處理器的執(zhí)行速度。并且基于寄存器的處理器比較容易調試(debug),因為指令的執(zhí)行過程
29、更清晰(一部分操作數在寄存器中,在運算完后還可以檢查參與運算的數值是多少)[1]。</p><p> 表2.1 基于寄存器的處理器和基于寄存器的處理器的優(yōu)缺點</p><p> 2.3.2 SVM虛擬機處理器設計</p><p> 由于虛擬機完全由軟件構成,沒有硬件設備,所以它不存在剛才提到的某些缺陷。</p><p> 與硬件處理器
30、的分類相似,虛擬機也有基于堆棧和基于寄存器之分。JVM就是基于堆棧的。這可以使JAVA字節(jié)碼文件很短。</p><p> 而我將SVM虛擬機的中央處理器實現為基于寄存器的機器。直接采用8086指令集。</p><p> 要知到,設計一個完備的指令集可不是一件簡單的事,至少我還沒有那種實力。SVM虛擬機的設計目標是能夠運行原8086平臺下的部分16位com程序(程序只能使用除去HLT,W
31、AIT,IN,OUT,LOCK,ESC的88條指令,并限制使用INT指令)。</p><p> SVM虛擬機處理器的組成:</p><p><b> 寄存器</b></p><p> SVM虛擬機有8個通用整數寄存器,4個段寄存器,一個指令指針,一個指令指針,一個標志寄存器(它有16位,但只使用其中的9位)。這些寄存器及其用途如圖所示:&
32、lt;/p><p> 表2.2寄存器及其用圖</p><p> 其中,AX,BX,CX,DX 都能劃分為兩個8位寄存器。一個用來保存低字節(jié),一個用來保存高字節(jié)(如AX寄存器可以劃分為AH 和 AL)。</p><p><b> 運算單元</b></p><p> 由C8086類(在C8086.C中)的成員函數實現。&
33、lt;/p><p> 2.4 SVM內存分配</p><p> SVM 虛擬機在啟動時會請求分配1M字節(jié)的內存空間,內存空間有可能會到虛擬內存中去,因為沒有對可用物理內存進行檢查。但由于只能加載com程序,實際上只使用了其中的64KB字節(jié)。</p><p> 2.5 SVM內存尋址</p><p> SVM 模擬8086處理器,選用20位
34、的實模式的地址空間,內存中某個字節(jié)的地址時由兩位16位數值指定的,這兩個數值分別叫做“段地址”(segment address)和“偏移地址”(offset address)。一個給定字節(jié)的內存地址是這樣計算出來的:把16位段地址乘以16(即0x10),然后把結果與偏移地址相加。</p><p> 2.6 SVM 多字節(jié)存儲方式</p><p> 多字節(jié)數據在內存中有兩種存放方式:降序
35、格式(big-endian)和升序格式(little-endian)。如果一個多字節(jié)數據的最高位字節(jié)存放在內存中的最低位地址,我們就說它采用的是降序記號。升序記號方式正好相反——多字節(jié)數據的最低位字節(jié)降存放在內存中的最低地址。</p><p> 請看下面這個例子。假設有一個多字節(jié)值“0Xabcdef12”存放在內存中的某個地方(我們不妨假設從地址24處開始存放)。這個數據的降序和升序表示法如圖所示:</p
36、><p><b> 降序存儲方式:</b></p><p> 24 25 26 27</p><p><b> 升序存儲方式:</b></p><p> 24 25 26 27 </p><p> 圖:多字節(jié)數據值在內存中的
37、兩種存放方式:降序和升序</p><p> 以降序方式表示的數據也稱為“網絡順序”(network order)。這是因為TCP/IP等網絡協(xié)議要求通過網絡傳輸的信息都必須采用降序格式[1]。</p><p> SVM虛擬機的設計目標是執(zhí)行DOS下的使用8086指令集的程序,故對多字節(jié)排列采用升序方式。</p><p> 2.7 SVM輸入輸出</p&g
38、t;<p> 對于8086平臺,它有兩個指令可以用來把寄存器長度的數據讀寫到外設:IN 和OUT</p><p> IN指令用來從某個I/O端口讀入數據。OUT用來把某個數據寫到某個I/O端口。I/O端口是被映射到某個外設或者外設的某個部件的一個數字。端口號的范圍是0到65535。</p><p> 由于對計算機里的I/O硬件進行設置需要考慮太多的細節(jié),需要很長的時間去
39、收集齊全外設的信息(工作原理,功能號,端口號等),所以,在實現SVM虛擬機的I/O時,我只能用軟件來模擬實現I/O(還只能實現向屏幕輸出單個字符)。</p><p> 輸出 INT 0X10 功能號 0X0E,模擬實現。</p><p> 雖然在Win32下,用軟件實現SVM虛擬機的I/O時,使用底層的系統(tǒng)調用(system call)能夠獲得更好的性能,這些操作更接近硬件,并且一般不
40、提供緩沖功能(bufferring)或額外的格式化(formatting)。C語言標準的函數就是建立在系統(tǒng)調用層之上的(即封裝具體平臺下的底層系統(tǒng)調用),它更側重于功能而不是執(zhí)行速度。但倚賴與C語言標準API對我來說好入手一些,同時,也利于對SVM虛擬機本身的移植。</p><p> 2.8 SVM 中斷</p><p><b> 實模式下的中斷處理</b><
41、;/p><p> 通過置位/清零,FLAG的第10個比特位(如果下標從0開始計算,就是第9位),我們就能激活/禁止實模式下的軟件中斷。人們把這個標志位稱為IF(interrupt flag中斷標志)。</p><p> IF標志可用兩條指令進行置位或清除。STI指令用來對IF標志進行置位,從而使處理器能夠對中斷做出響應。CLI指令用來清除IF標志,從而使(大多數)中斷都被屏蔽。</p
42、><p> 軟件中斷是用INT指令產生的。每執(zhí)行一條INT指令,就會產生一個軟件中斷。INT指令的操作數是一個單字節(jié)的整數,人們把它稱為“中斷向量”(interrupt vector)。例如,用來處理第12個中斷向量的指令就是“INT 12”。</p><p> 中斷向量可以是0到255之間的任意整數。中斷向量其實只是“中斷向量表”(interrupt vector table ,IVT)
43、的下標,IVT中的元素都是雙字(4字節(jié))數值。既然中斷向量有256個可能的取值,IVT表的長度就是1024個字節(jié)。中斷向量表從處理器地址空間的最底端開始,因此它將占據內存的第一個KB。IVT表中的每一個雙字數據項包含著某個中斷服務程序的段地址和偏移地址。偏移地址保存在第一個字節(jié)里,段地址保存在隨后的第二個字里。中斷下向量表的構造情況如圖所示:</p><p> 圖2.2 中斷下向量表的構造圖</p>
44、<p> 處理器在執(zhí)行中斷指令時將按如下步驟進行:</p><p> 把FLAG寄存器壓入堆棧</p><p> 把CS寄存器壓入堆棧</p><p> 把IP寄存器壓入堆棧(向量中斷處理結束后將要執(zhí)行的下一條指令)。</p><p> 清除IF和TF標志位</p><p> 根據中斷向量找到
45、對應的IVT數據項</p><p> 把IVT中的段地址和偏移地址分別加載到CS 和 IP 寄存器里去。</p><p> 從效果上講,這等于是讓程序控制條轉到中斷服務程序,中斷服務程序去做自己該做的事。為了讓處理器回到一種“清醒”的狀態(tài),放到中斷服務程序里的第一條指令應該是STI指令,</p><p> 這將喚醒處理器并使它能夠再次接受中斷請求。中斷服務程序
46、必須用IRET指令返回,IRET指令將把以上步驟反過來執(zhí)行。使程序路徑能夠正確地回到緊跟再剛才這條中斷指令的下一條指令上去。具體來說,IRET指令將按以下步驟進行:1)把堆棧頂部的16位數值彈出到IP寄存器里去。</p><p> 2)把堆棧頂部的16位數值彈出到CS寄存器里去。</p><p> 3)把堆棧頂部的16位數值彈出到FLAG寄存器里去。</p><p
47、> SVM虛擬機將簡化以上硬件中斷處理的過程。它使用軟件模擬的方式實現BIOS中斷。</p><p> SVM虛擬既需要依賴宿主操作系統(tǒng)提供諸如輸入輸出之類的基本服務。讓自己游離于硬件通信細節(jié)之外。SVM虛擬即讓宿主操作系統(tǒng)作為之給予精簡設備之間的中間人,不直接與硬件發(fā)生糾纏(實事上在Win32下,如果不是驅動程序,也不可能有ring0級權限去訪問硬件)。也就是說,宿主操作系統(tǒng)將代表虛擬機來請求各種底層
48、的操作細節(jié)。</p><p> 每一中操作系統(tǒng)都會提供一些人們稱之為“系統(tǒng)調用“(system call)的基本函數,這些系統(tǒng)調用將負責管理機算計的可用資源調用。系統(tǒng)調用就像是一些原子元素,它們的各種組合構成了操作系統(tǒng)這個宇宙中的一切事務。任何一個操作系統(tǒng)命令或任何一個用戶程序都可以分解為一系列系統(tǒng)調用。</p><p> 系統(tǒng)調用一般都處于非常底層的位置。它們的操作只能用特定機器的硬
49、件語言來描述。換句話說,系統(tǒng)調用大都是用匯編語言寫的。以匯編語言來編寫系統(tǒng)調用的原因并不是出于速度方面的考慮——執(zhí)行速度方面的邊界效應與成千上萬行匯編代碼所形成的思維復雜性并不成比例。一會便于演變歇息同調用的真正原因是有些事情你只能用匯編語言來做。</p><p> 系統(tǒng)調用的輸入輸出參數還必須通過特定的機器寄存器來指定。</p><p> 為了降低復雜性,系統(tǒng)工程師會盡量把與硬件直接
50、相關的匯編代碼的操作系統(tǒng)的底層操作隔離開來。軟件開發(fā)人員再用C/C++對匯編代碼進行打包以使之更容易使用。</p><p> 把機器操作歸結為一整套系統(tǒng)調用的做法是非常有遠見的。但這絕不是對機器操作的抽象至少有兩個層次來隔離核心級匯編語言例程的系統(tǒng)調用以及來封裝系統(tǒng)調用的函數庫調用。如圖所示:</p><p><b> 圖2.3系統(tǒng)層次圖</b></p>
51、;<p> C程序設計語言的標準函數庫是這種抽象歸納的經典示例。比如putchar()函數。</p><p> 各種版本的標準函數庫實現大都以更通用的putc()函數定義putchar()函數,putc()用來把一個字符寫到一個給定的輸出流去。就putchar()函數而言,它的輸出流被規(guī)定為標準輸出(stdout)。</p><p> #define putchar(c
52、) putc(c, stdout)</p><p> 因此,要想了解putchar(),必須先把putc()搞清楚:</p><p> int putc(int ch, FILE *stream)</p><p><b> {</b></p><p><b> int ret;</b>&l
53、t;/p><p> ret = write(stream, &ch, 1);</p><p> if (ret != 1) {return (EOF);}else{return (ch);}</p><p><b> }</b></p><p> putc()函數又用到了一個名為write()的系統(tǒng)調用,這種
54、嵌套結構的特點是:越接近硬件,函數或例程的功能就越通用,越基本。</p><p><b> /*</b></p><p> stream = output stream to write to </p><p> buffer = buffer of bytes to write to stream</p><p>
55、; nbytes = number of bytes to write</p><p> returns = number of bytes written to stream</p><p><b> */</b></p><p> int write(FILE *stream, void *buffer, int nbytes)&l
56、t;/p><p><b> {</b></p><p> struct call_struct;</p><p> call_struct.type = FILE_SYSTEM;</p><p> call_struct.subtype = BUFF_OUTPUT;</p><p> cal
57、l_struct.param1 = (long)stream;</p><p> call_struct.param2 =(long)buffer;</p><p> call_struct.param3=nbytes;</p><p><b> asm</b></p><p><b> {</b
58、></p><p> MOV ECX,USE_LIBRARY</p><p> LEA EAX,call_struct</p><p> INT SYSTEM_CALL</p><p><b> }</b></p><p><b> }</b></p&g
59、t;<p> write()函數實際上是一個二傳手,它將把球在傳給一個名為system_call的系統(tǒng)調用通道,一般地,操作系統(tǒng)只有一個對系統(tǒng)調用請求進行集中分配的機制,而這是很有必要的。這是因為,系統(tǒng)調用通常都是用軟件中斷實現的,而產生軟件中斷的辦法只有一種(如在Intel平臺上使用的INT指令)。換句話說,系統(tǒng)調用其實就是一系列相同的基本指令編寫出不同的變化組合。</p><p> 從上圖中
60、可以看出,系統(tǒng)調用通道是用戶級函數庫與各種系統(tǒng)調用之間的一條必經之路。在具備內存保護機制的操作機制的操作系統(tǒng)里,系統(tǒng)調用通道將是用戶執(zhí)行各種系統(tǒng)調用的唯一途徑,除此之外,沒有第二條路可走。這就使計算機的運行狀態(tài)產生了“內河模式”(kernel mode)和“用戶模式”(user mode)之分。當CPU正在執(zhí)行的指令屬于某個系統(tǒng)調用時,我們就說它運行在內核模式;當計算機正在執(zhí)行的指令屬于某個庫函數或者屬于用戶編寫出來的某個函數時,我們就
61、說它運行在用戶模式。</p><p> 在某種程度上,我們可以把操作系統(tǒng)看作是由它的全體系統(tǒng)調用所構成的一個集合,那些系統(tǒng)調用就好比是操作系統(tǒng)的簽名。但系統(tǒng)調用接口摒不能把操作系統(tǒng)完全的定義下來,而是由其系統(tǒng)調用接口以及那些系統(tǒng)調用的具體實現方法所定義的[1]。</p><p> SVM虛擬機的中斷調用結構如下圖:</p><p> 圖2.4 SVM虛擬機中斷
62、調用結構:</p><p> 2.9 SVM 匯編器</p><p><b> 匯編語言</b></p><p> 在4月份對C8086類的測試中(單元測試,即單獨測試一個個的指令執(zhí)行單元),我是以手工方式建立機器碼文件。但是,如果是面對大量的機器碼編程,需要考慮周全的瑣碎細節(jié),這只能使人感到力不從心。</p><p&
63、gt; 不用手工方式來建立機器碼可執(zhí)行文件的辦法是存在的。具體地說,用一種匯編語言來編寫可執(zhí)行文件是完全能夠做到的。匯編語言是一種底層程序設計語言。為了更好的理解這句話的含義,我們必須先搞清楚幾個概念。</p><p> “程序設計語言”是一種能夠用來準確地寫出程序的符號系統(tǒng)。程序設計語言通過他們的語法(syntax,該語言所能使用的符號以及這些符號的使用規(guī)則)和語義(semantics,語言符號所代表的含義
64、)得到定義[1] 。</p><p> 程序設計語言的符號化語句不會產生歧義,這是他們與數學語句的相似之處。但程序設計語言的符號化語句卻不必非得解析成“真”或“假”,這是它們與數學語句的不同之處[1]。</p><p> 語言的語法可以通過一套名為“上下文無關文法”(context-free grammar)的規(guī)則集合做出正規(guī)定義。程序設計去眼大都采用一種名為“巴科斯·諾爾范
65、式”(Backas-Naur form,BNF)的記號來表示它們的語法規(guī)則。語言的寓意很難用含義精確的邏輯符號來描述[1]。</p><p> “匯編語言”是一種程序設計語言,它直接把符號化的助記符映射為機器指令,機器指令和助記符之間的映射結果幾乎是一對一的關系[1]。</p><p> 1110 1001 0000 1111 0000 0000 直接映射為:JMP 0012</
66、p><p> 匯編語言里還提供有“宏指令”(macro directivec)。洪指令用來經以各種符號,這些符號可以代表內存地址,數值常數或一組機器指令。匯編語言中的宏指令的用法與C程序設計語言的情況基本相同[1]。</p><p> 一般來說,用匯編語言寫成的源文件可以被一種人們稱為“匯編器”(assembler)的開發(fā)工具翻譯——或者匯編——為機器碼可執(zhí)行文件。匯編器能夠體程序員完成內
67、存地址,偏移量和其他瑣碎的安排記錄工作[1]。</p><p> 程序設計語言是對處理器指令的抽象,我們可以根據這種抽象的高低層次來對它們進行分類。低級語言——如匯編語言——的特征與機器指令有相當直接的對應關系。匯編語言中的一條語句通常直接對應一條機器指令。而高級語言——如BASIC——與機器操作細節(jié)的距離則相當遙遠,BASIC語言中的一條語句往往相當于很多條機器指令。中級語言——如C語言——則用使具備執(zhí)行低級
68、操作和高級操作的能力[1]。</p><p> 匯編器的設計將要涉及一些將當復雜的數據結構。好在有NASM,MASM等匯編器存在。因為匯編器比虛擬機要復雜很多。</p><p> SVM 虛擬機采用的是8086指令集,這樣就可以用已有的支持8086的匯編器,如NASM , TASM, MASM等。我使用的是NASM,用來編譯com文件的格式是:</p><p>
69、 C:\program files>nasm test.asm –f bin –o test.com</p><p> -f 用來生成com文件;</p><p> -o 用來指定生成的文件明。</p><p> 2.10 SVM 反匯編器</p><p> 作為SVM虛擬機調試器的組件之一,為便于管理代碼,從調試器代碼中
70、獨立了出來。SVM反匯編器簡單地將機器碼翻譯為對應的匯編源程序,其結構和C8086類很相似,只是在處理機器碼時不執(zhí)行罷了。</p><p> 2.11 SVM 調試器</p><p><b> 2.11.1概述</b></p><p> “調試器”(debugger)是一種軟件開發(fā)工具, 它能讓一個程序的執(zhí)行路徑暫停下來以便人們查看和修改
71、概進程的機器狀態(tài)。調試器就像是一個特殊的實驗室,你可以在里面運行和分析程序,看它在執(zhí)行時到底在干些什么。例如,在某程序執(zhí)行路徑上的某個特定地點,你可以把一切都凍結起來,然后查看一下寄存器或內存區(qū)間的情況以檢查某個給定變量的值。有些不易發(fā)現的程序漏洞只有在這類環(huán)境下才有可能被捕獲和分析[1]。</p><p> 調試器又分為兩個基本類型</p><p><b> 機器級調試器&
72、lt;/b></p><p><b> 源碼級調試器</b></p><p> 劃分這兩類調試器的標準是它們所管理的”指令粒度”(granularity of instructions)。</p><p> 機器級調試器處理的指令是二進制編碼形式的底層機器指令。它使你能夠觀察到計算機最底層,最基本的操作情況。機器級調試器是最后一道防
73、線,一般用來分析已經成品化了的程序[1]。</p><p> 源代碼級的調試器處理的指令是高級程序設計語言的語句。源代碼級調試器使我們能夠在程序設計語言的框架里追蹤程序的執(zhí)行路徑。一般來說,源代碼級調試器要比機器級調試器更容易使用,因為工程師們用不著再把精力浪費在大量的機器細節(jié)</p><p> 上,源代碼級調試器都已經替他們照顧到了[1]。</p><p>
74、 2.11.2調試技術</p><p> 為了提供基本調試功能,各種調試器都使用了兩種基本技術:</p><p><b> 斷點</b></p><p><b> 單步執(zhí)行</b></p><p><b> ?。?)斷點</b></p><p>
75、 “斷點”(bread point )是一種特殊類型的機器指令, 在使用中,它們將插入到程序的正文段(text segment)里。斷點既可以在程序運行時被插入——既把斷點放到某個進程的內存映像里去,也可以在編譯時插入——即把斷點放到可執(zhí)行文件里去。不論采用的是哪種方法,斷點的作用都是一樣的:讓處理器暫停某個任務的執(zhí)行并把程序控制轉交給調試器,以便用戶查看和修改該任務的機器狀態(tài)[1]。</p><p><b
76、> ?。?)單步執(zhí)行</b></p><p> “單步執(zhí)行”(simle-step excution)是處理器的一種執(zhí)行模式,在這種模式里,處理器每執(zhí)行完一條語句,就會把程序控制轉交給調試器。在單步執(zhí)行模式下,調試器能夠一條指令一條指令地追蹤進程的執(zhí)行路徑。</p><p> 一般情況下,用在在接近某個預定代碼區(qū)域的某個地方設置一個斷點。當執(zhí)行路徑到達這個斷點并把程序
77、控制轉交給調試器之后,用戶再以單步執(zhí)行方式通過這個預定代碼區(qū)域,看該代碼區(qū)域里會發(fā)生什么事情[1]。</p><p> 2.11.3 SVM 調試器的實現</p><p> SVM 調試器是一個機器級調試器。僅僅作為一個嘗試,SVM 調試器很簡陋,只提供了幾個服務。不支持斷點,寄存器修改等功能。</p><p> 該調試器的功能見SVM虛擬機調試器的使用。&l
78、t;/p><p> 2.12 建造運行時系統(tǒng)</p><p> 建造一個虛擬機運行時系統(tǒng)的步驟大體上如下圖:</p><p> 圖2.5 建造虛擬機網絡圖</p><p> 2.13 SVM 虛擬機的擴展</p><p> 到此,SVM 虛擬機的功能仍然是很不完善的,由于初始設計的原因,要對其進行擴展有可能要重新
79、設計整個程序的結構,尤其是處理器對調試器的支持部分。</p><p> SVM 虛擬機可以擴展的方面還有很多,還可以做虛擬設備,擴展指令集,加載NE 文件,實現IN,OUT 等涉及硬件的指令,實現更多的BIOS中斷。其中BIOS中斷又有兩種實現方式——用軟件模擬實現,或在建好虛設備的基礎上載入一段BIOS程序。</p><p> 關于調試器的擴展,原本調試器的功能可以更強的,比如指定反
80、匯編的位置,設置斷點等。但對于命令行的處理太過繁瑣,需要對參數進行嚴格的檢查。時間不夠,我也就沒有做了。而調試器中的相關接口函數確實是提供了指令定位的功能。</p><p> 還可以加入更多的防錯處理使得SVM虛擬機更健壯。</p><p> 當然,擴展是在16位數據的基礎上的,若是要建造32位的虛擬機,SVM整個都要改,不只是表面上指令的操作數位數不同了,IA32相對于16體系結構變
81、化很大,出現了3種指令執(zhí)行模式:實模式,保護模式,虛擬86模式。技術的發(fā)展是趨于完善,復雜的,SVM只是一個嘗試。對于SVM,要改成32位虛擬機,那就不叫擴展,而是重構了。目前,BOCHS虛擬機(一個開源的虛擬機)在模擬32位硬件平臺上就做得很好。</p><p> ?。?SVM 虛擬機運行時環(huán)境</p><p> 3.1SVM執(zhí)行方式</p><p><
82、b> SVM虛擬機生命期</b></p><p> 圖3.1 SVM 虛擬機生命期</p><p><b> 啟動虛擬機</b></p><p> C:\Program files>svm test2.com</p><p><b> 運行示例:</b></p
83、><p> 圖3.2 SVM運行圖</p><p> 3.2 SVM調試方式</p><p><b> 進入調試方式</b></p><p> c:\program files>svm test2.com –d</p><p><b> -q 退出調試器</b>&
84、lt;/p><p> 顯示存儲單元內容——D命令(Dump Command)</p><p><b> -d</b></p><p> 從即將執(zhí)行的地址開始, 顯示80H個字節(jié)單元內容</p><p> 圖3.3 顯示內存單元</p><p> 上述顯示內容分為三部分:左邊是每一行存儲單元的
85、起始地址(段基值:偏移量);</p><p> 中間是16個字節(jié)單元,以兩位十六進制數顯示其現行內容;右邊是中間各字節(jié)單元用相應的ASCII碼字符顯示,如該單元數據是不可顯示字符,使用“.“表示。</p><p> (2)顯示寄存器內容——R命令(Register Command)</p><p><b> -r</b></p>
86、;<p> 圖3.4 顯示寄存器內容</p><p> 鍵入R后, CPU各寄存器內容全部顯示出來。第二行后半部顯示標志寄存器各標志位狀態(tài),且各標志位的復位(“0”狀態(tài))和置位(“1”狀態(tài))是用兩個字符來表示的,如表所示;顯示的第三行表示現在CS:IP指向的下面將要執(zhí)行的指令。</p><p> 表3.1 標志寄存器個標志位的狀態(tài)字符</p><p&
87、gt; 顯示匯編語言指令(反匯編)——U命令(Unassemble Command)</p><p> 在調試狀態(tài)下運行程序是執(zhí)行某內存區(qū)域的目標代碼,為了知道執(zhí)行的是什么指令,操作數的地址在哪里等,希望把目標代碼“還原”為匯編語言指令(即源程序中指令),這個操作叫做反匯編。U命令從當前地址開始,顯示32個字節(jié)目標代碼的匯編語言指令。</p><p> 執(zhí)行U命令后,屏幕顯示示例如下
88、:</p><p> 圖3.5 反匯編輸出</p><p> ?。?)單步運行方式——T命令(Trap Command)</p><p> 每次執(zhí)行T命令,僅執(zhí)行一條指令。每執(zhí)行完一條指令后,就自動顯示CPU中各寄存器和標志寄存器內容。并顯示下一條即將執(zhí)行的指令。T命令執(zhí)行的顯示示例如下:</p><p> 圖3.6 跟蹤方式輸出<
89、;/p><p><b> 4 程序設計處理</b></p><p> 4.1程序設計中的宏處理</p><p> C8086類由一個譯指令成員函數和執(zhí)行成員函數以及一些輔助成員函數組成。</p><p> 當一個指令執(zhí)行時,譯碼成員函數對其第一個字節(jié)進行翻譯,并選取相應的執(zhí)行函數對指令進行處理。為了盡可能的提高指令
90、的執(zhí)行速度,我將執(zhí)行成員函數及輔助成員函數全部聲明為內聯(lián)函數,以省去函數調用的開銷。當然,最后的可執(zhí)行文件要比基于函數的大很多。</p><p> 注:在各種類型的C 編譯器里,函數調用總是會有一定的開銷。調用一個函數通常需要把一些數值壓入堆棧,把程序執(zhí)行點切換到內存里的另一個地點,最后在從堆棧里彈出一些數值。避免這類開銷的辦法之一是把函數定義為宏(macro)。例如有一個函數,如果它會被頻繁地調用,為了節(jié)省函
91、數調用過程中系統(tǒng)開銷,我們可以把函數寫成宏:</p><p> #define abs(x) ( (x) >= 0) ? (x) : -(x)</p><p> 這樣,(通過預處理程序展開宏)我們每一次調用abs(x),譬如:</p><p> int a = abs(-5);</p><p> 我們就相當于直接寫:</p
92、><p> int a = ( (5) >= 0) ? (5) : -(5);</p><p> 效率明顯比編寫一個函數然后調用它要好。</p><p> 但宏也不是萬能的,它有很多不盡人意的方面,例如:</p><p> int a = abs(5) + 1;/* 結果應該等于6 */</p><p>
93、 然而,預處理程序把宏展開:</p><p> int a = ( (5) >= 0) ? (5) : -(5) + 1;</p><p> “+”運算符的優(yōu)先級比“?”要高,所以上面的式子其實是:</p><p> int a = ( (5) >= 0) ? (5) : (-(5) + 1);/* a = 5 !*/</p>
94、<p> 為了避免這樣的漏洞,我們要小心翼翼地提方,盡可能采用適當的措施:</p><p> #define abs(x) ( ( (x) >= 0) ? (x) : -(x))</p><p> 雖然這里“加括號”可以解決問題,但由于宏有不少“副作用”,某些場合就頗為棘手。</p><p> 因為宏僅僅是一種記號替換,不像函數,它沒有辦法知
95、道真正的參數時什么,從而無法對參數進行檢查,也不能像函數那樣把某些運算局限在內部。</p><p> 因此由C++提出和實現了“內聯(lián)涵數”,它本身的確是函數,但必要時編譯器可以把它的代碼像宏那樣“嵌入”到調用它的地方,從而既節(jié)省系統(tǒng)開銷又沒有宏的缺點。</p><p> 今天,C99也擁有了這項語言特性[2]。</p><p> 4.2程序設計中的異常處理&l
96、t;/p><p> 我曾經考慮過學習C++中的try catch throw 異常處理機制,然后用在SVM虛擬機的實現中。但最終未采用該機制,SVM虛擬機的執(zhí)行效率已經很難讓人恭維了,加入該機制只會使虛擬機的運行更慢。不過在此,仍簡要介紹一下該機制。</p><p> 在異常處理技術得到廣泛應用之前,即使應用程序理出現了奇怪的事情,你也只能依靠全局變量和函數的返回值來判斷到底發(fā)生了什么事情
97、。</p><p> 然而,你也不能期望一個孤零零的出錯值總是能向你提供足夠的信息,函數出錯并不可怕,可要是函數反回了一個“-1”這樣的整數值,那即使是再高明的程序員也很難找出函數失敗的真正原因,這種做法的最終結果將是丟失程序的狀態(tài)信息。</p><p><b> 給出一段代碼:</b></p><p> FILE *fptr;</
98、p><p><b> int ch;</b></p><p> fptr = fopen(“myfile.txt”, “rb”);</p><p><b> /*</b></p><p> forget to check for fptr == NULL</p><p>
99、 sends everything to the big room if the file is missing.</p><p><b> */</b></p><p> ch = fgetc(fptr);</p><p> while(ch != EOF)</p><p><b> {</b
100、></p><p> printf(“%c”, (char)ch);</p><p> ch = fgetc(fptr);</p><p><b> }</b></p><p> fclose(fptr);</p><p> 在沒有異常處理功能的平臺上,上面這段代碼在myfile.
101、txt文件真的不存在時將會造成一場可怕的混亂,而具備異常處理功能的平臺則會把這場返回值混亂消除。</p><p> 異常是程序在試圖進行某種非正常操作時生成的一個結構。這并不意味著每個異常都代表著一個因某些基本的系統(tǒng)服務沒有執(zhí)行成功而導致的程序出錯。只要是偏離程序員設計意圖的情況都可以用異常來表示。</p><p> 當異常出現時,程序員執(zhí)行路徑將從產生這個異常的代碼位置跳轉到預先安排
102、好的異常處理代碼位置去,這種跳轉大多是一些“長跳轉”(non-local jump,從一個函數跳轉到另一個函數里去)。人們經常用“拋出”(throw)和“捕獲”(catch)這兩個詞來描述異常處理中的這個跳轉動作。</p><p> 生成異常的代碼拋出一個異常,而將要對之進行處理的代碼則捕獲了它。整個過程如圖所示:</p><p> 圖 4.1 異常處理示意圖</p>&
103、lt;p> 由于執(zhí)行效率的原因,SVM虛擬機不易采用C++的try/throw/catch 異常處理機制[1]。</p><p><b> 總 結</b></p><p> SVM虛擬機的模塊化設計并不好,沒有能夠將指令集的定義獨立出來。變量,函數的命名不是很規(guī)范。整個的編碼工作的關鍵部分是在4月份完成的,五月初加入了調試器代碼。</p>
104、<p> 整個源文件的擴展過程是:</p><p> 先建好8086模擬器,分布在C8086.H C8086.CPP中,一些全局數據類型定義在GLOBAL.H中,同時主函數在test_CPU.cpp中。</p><p> C8086 基本通過測試后(實際上只測試了ADD,OR,JMP,LODSB,AND等指令,測試太耗時間了!然后接著是巨痛苦的DEBUG階段?。┌裄
105、am的功能從C8086類中分離出來,建了Ram類,在文件Ram.h ,Ram.cpp中。</p><p> 為了便于調試程序又編寫了簡單的調試器debug.h,debug.cpp。</p><p> 5月初,在老師的建議下,給調試器件入了反匯編的功能。實現在disasm.h,disasm.cpp中。</p><p> 由于沒有異常處理,SVM虛擬機并不健壯,例
106、如數組越界的問題還未能很好的解決。所以為了防止代碼長度超出1 MB而溢出,目前只能加載只有64KB的.com程序。(這也不能保證不出錯)。</p><p> SVM虛擬機的擴展部分主要集中在BIOS,DOS等調用部分,建立虛擬設備。中斷部分的可擴展部分實在是太多了。但我目前對此無能為力,涉及面太寬。所以稱為SVM是蠻合適的。</p><p> 另外,擴展fileLoader部
107、分,以使虛擬機可以執(zhí)行16位DOS NE 文件。</p><p> 事實上,SVM虛擬機的設計,主要目的是了解計算機的硬件體系結構。學會在熟悉了一個硬件平臺的基礎上(主要包括 指令集,CPU與硬件的通信接口,中斷等),用軟件來搭建虛擬的平臺。SVM虛擬機的難點在于了解CPU的工作機制,其是所涉及的算法和數據機構很簡單。如果是做編譯器,或者是操作系統(tǒng)的畢業(yè)設計,我很可能要推遲畢業(yè)了,編譯器涉及大量復雜的數據結構,
108、什么二叉檢索樹,哈希表,向量等等。而操作系統(tǒng)則更難,它要對底層的硬件資源進行管理,由其是內存,再有就是文件處理,當然,如果是針對一臺虛擬機來設計操作系統(tǒng)可能要容易一些。好在只是做虛擬機——很簡單的虛擬機——設計實現。</p><p> 商業(yè)上級虛擬機肯定是很復雜的,要在上面模擬一個完整的硬件平臺應該是有很多困難的,隨著虛擬機受到系統(tǒng)工程師的關注,現在虛擬機的設計也成為了一門新興的研究領域,它在防病毒檢測,開發(fā)跨
109、平臺軟件,開發(fā)操作系統(tǒng)等項目上得到了廣泛應用。</p><p> 正如《虛擬機設計與實現》上所說的:</p><p> “如今,計算機硬件的體系結構越來越復雜,要想根上摩爾定律,工程師就不得不面對更大的挑戰(zhàn)。例如,對負責開發(fā)編譯器的的系統(tǒng)程序員們來說,與80年代的處理器相比,如今的EPIC(Explicitly Parallel Instruction Computing 并行指令計算
110、)處理器架構絕對是一個嚴峻得多的挑戰(zhàn)。為了支持諸如指令級并行處理(instruction-level parallelism)之類的功能,提高執(zhí)行效率的大部分責任都已經從處理器芯片轉移到了系統(tǒng)工程師(他們可真了不起)肩上,要想提高執(zhí)行效率,就必須預測計算機指令的執(zhí)行情況并盡可能地讓它們并行執(zhí)行。這類預測必須滿足兩項要求:一要盡量避免因預測錯誤而降低執(zhí)行效率,二是生成能夠通過預測而提高效率的代碼。因此,實現這類預測功能的工作量是相當大的。
111、不夸張地說,這足以把任何一位工程師逼得跳樓。</p><p> 更糟糕的是,計算機的處理器芯片的這些芯片的指令集總是處于變化和發(fā)展當中。當一個穩(wěn)定,高效的編譯器被推向市場并被接納為一種標準化工局的時候,計算機硬件肯定又有了新的進展,這就迫使設計該編譯器的工程師們不得不埋頭于改進該編譯器的后端(back end)。為了跟上硬件開發(fā)人員的步伐,像我這樣的系統(tǒng)開發(fā)人員就不得不一刻不停地跑下去,根本沒有喘息的機會——這
112、是一種永遠的痛?!?lt;/p><p> “在不斷改進開發(fā)工具后端的辦法之外并不是沒有其他的道路。具體地說,你可以選擇一臺虛擬機(virtual machine)為目標?!薄?lt;/p><p> 這也是虛擬機開發(fā)的源動力。</p><p><b> 致 謝</b></p><p> 我的畢業(yè)設計課題原來是DL
113、X模擬器設計,后來,我想以8086處理器的指令集為基準建一個小型虛擬機會更有意思一些。在和導師商量之后,導師同意我換一個類似的課題。我的整個畢業(yè)設計從3月中旬正式開始。</p><p> 在我的整個畢業(yè)設計過程中,感謝***老師的積極建議,比如鑒于時間的關系,SVM虛擬機首先實現對無格式com文件的加載執(zhí)行;完善SVM虛擬機的DEBUG部分,加入反匯編的功能。整個DEBUG程序模仿DOS下的調試器;程序可以省去
114、不必要的GUI編程,這使得我能把精力集中在虛擬機的內部實現上。</p><p><b> 參考文獻</b></p><p> [1] 【美】B.Blunden,楊濤,楊曉云,王建橋,高文雅,張玉亭 譯.虛擬機的設計與實現-C/C++ 機械工業(yè)出版社,2003,1.</p><p> [2] 姚新顏.C/C++深層探索.人民郵電出版社, 2
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 虛擬機命令
- java虛擬機
- virtualpc虛擬機教程
- 基于虛擬機和解釋器的游戲腳本系統(tǒng) ——虛擬機模塊設計與實現---畢業(yè)論文
- 虛擬機管理平臺中的虛擬機代理服務機制研究.pdf
- 怎樣安裝vmware虛擬機
- 虛擬機機制研究.pdf
- 硬件虛擬機的設計與實現.pdf
- 結合虛擬機間性能互擾度量的虛擬機遷移方法研究.pdf
- 虛擬機檢測技術剖析
- Xen虛擬機研究.pdf
- 基于節(jié)能的虛擬機部署與虛擬機整合技術研究.pdf
- Java虛擬機衰退分析.pdf
- 虛擬機管理平臺的設計及實現.pdf
- 虛擬機安裝安卓4.0
- virtualbox虛擬機使用教程圖解
- kvm虛擬機遷移技術分析
- 虛擬機應用系統(tǒng)的設計與實現.pdf
- vmware虛擬機的虛擬網卡使用說明
- 虛擬機遷移技術研究
評論
0/150
提交評論