腳本語言的虛擬機和操作系統(tǒng)的虛擬機
虛擬機是個用軟件實現(xiàn)的CPU,而CPU的權(quán)限控制分為系統(tǒng)級和用戶級。
例如,Linux內(nèi)核就運行在CPU的最高優(yōu)先級(ring0),而普通應(yīng)用程序則運行在最低優(yōu)先級(ring3)。
雖然英特爾把CPU的權(quán)限分了4個優(yōu)先級,但實際只用到了2個。
對于虛擬機來說,要想模擬操作系統(tǒng)的運行,也必須進行權(quán)限分級。
1,CPU的權(quán)限分級,主要是指內(nèi)存的訪問權(quán)限。
intel的CPU分為實模式和保護模式,保護模式最主要的作用就是保護內(nèi)存的訪問權(quán)限。
內(nèi)核代碼可以訪問所有的內(nèi)存,但是用戶代碼只能訪問進程的用戶空間(內(nèi)存)。
用戶空間的內(nèi)存是通過進程的頁表來管理的,而進程的頁表只能通過系統(tǒng)內(nèi)核來修改。
當使用malloc()分配內(nèi)存的時候,實際上并不是分配一塊物理內(nèi)存,而只是把用戶空間的某一個內(nèi)存范圍設(shè)置為可用。
只有當進程代碼真去讀寫這個內(nèi)存范圍的時候,操作系統(tǒng)才會給它分配物理內(nèi)存,即Linux的寫時復制和需求加載機制。
所以虛擬機要想“模擬”操作系統(tǒng)的運行,首先要模擬CPU的保護模式。
2,CPU保護模式的實現(xiàn),靠的就是幾個控制寄存器。
對于intel CPU來說,跟保護模式下相關(guān)的寄存器是cr0, cr1, cr2, cr3。
其中cr0用于控制分段和分頁機制,一旦開啟內(nèi)存的分段機制就進入了保護模式。
一旦開啟了內(nèi)存的分頁機制,操作系統(tǒng)可以支持的進程個數(shù)就是無限的了。
開啟了分頁之后,操作系統(tǒng)就可以4096字節(jié)的一個頁為單位,為進程分配“必需的”內(nèi)存空間,非常的靈活。
什么時候必需?
當然是寫時復制和需求加載的時候必需,所以進程剛創(chuàng)建時除了它的task_struct結(jié)構(gòu)之外,只需要給它分配4096字節(jié)做為頁目錄即可,其他的都可以跟父進程共享。
對于多進程多任務(wù)的操作系統(tǒng)來說,內(nèi)存的分頁機制是必需的,因為分段機制太死板了。
cr3就是頁目錄基地址寄存器,哪個進程運行時它就指向哪個進程的頁表,內(nèi)核運行時它就指向內(nèi)核頁表。
cr2在缺頁中斷時用于保存進程用戶空間的內(nèi)存地址。在哪個位置出錯了,就保存哪個地址,然后操作系統(tǒng)就會為那個位置(所在的內(nèi)存頁)分配內(nèi)存。
獲取一個位置addr所在的內(nèi)存頁非常的簡單,把它的最低12位清零就行,addr & ~0xfff
3,虛擬機要想模擬操作系統(tǒng)的運行,必須自己實現(xiàn)MMU的功能。
操作系統(tǒng)的運行,首先要依賴這幾個控制寄存器。
這幾個控制寄存器的主要作用,其實就是內(nèi)存管理。
在真實的硬件上,內(nèi)存管理是通過MMU實現(xiàn)的。MMU可以根據(jù)進程的頁表實現(xiàn)用戶空間的內(nèi)存地址(線性地址)到物理內(nèi)存的映射。
如果在虛擬機上,這部分功能就只能通過代碼去實現(xiàn)了。
虛擬機要實現(xiàn)三層內(nèi)存地址的映射:虛擬進程的用戶內(nèi)存地址 --> 虛擬物理內(nèi)存的物理地址 --> 虛擬機所在的真實進程的用戶內(nèi)存地址。
OS虛擬機的內(nèi)存映射過程
所以像qemu這種能夠直接運行Linux系統(tǒng)的大型虛擬機,是必須要實現(xiàn)CPU的控制寄存器和系統(tǒng)級指令的。
系統(tǒng)級指令,指的是只能在內(nèi)核代碼(或引導扇區(qū))里運行的指令,例如:
pushfl 把標志寄存器壓棧,
mov cr2, eax 把導致缺頁的內(nèi)存地址讀到eax寄存器,
mov ax, cs 加載段選擇符,等等。
4,腳本語言的虛擬機
腳本語言因為是運行在用戶進程中,運行的代碼也是用戶態(tài)代碼,所以實現(xiàn)起來比qemu這類虛擬機要簡單的多。
它只需要解釋一些常用指令就行了,不需要處理系統(tǒng)級的指令,也不需要管理復雜的內(nèi)存映射。
它只需要把編譯之后的字節(jié)碼文件根據(jù)程序頭的信息加載起來,并且處理動態(tài)庫函數(shù)的調(diào)用(動態(tài)鏈接),就可以實現(xiàn)腳本語言的運行了。
最主要的是,腳本語言的字節(jié)碼和編譯器都是腳本語言的作者設(shè)計的,作者可以實現(xiàn)字節(jié)碼和虛擬機的精確匹配,而不需要去實現(xiàn)CPU的整個指令集。
系統(tǒng)級的虛擬機就不得不實現(xiàn)CPU的整個指令集,因為OS內(nèi)核被編譯之后有可能用到CPU的所有指令,其中任何一條指令沒被支持都可能導致內(nèi)核運行失敗。
腳本語言的虛擬機怎么寫,之前已經(jīng)說過了,不再細說了。