腳本語言的虛擬機和作業系統的虛擬機

底層技術棧 發佈 2024-01-14T04:10:52.216268+00:00

#頭條創作挑戰賽#虛擬機是個用軟體實現的CPU,而CPU的權限控制分為系統級和用戶級。例如,Linux內核就運行在CPU的最高優先級(ring0),而普通應用程式則運行在最低優先級(ring3)。雖然英特爾把CPU的權限分了4個優先級,但實際只用到了2個。

虛擬機是個用軟體實現的CPU,而CPU的權限控制分為系統級用戶級

例如,Linux內核就運行在CPU的最高優先級(ring0),而普通應用程式則運行在最低優先級(ring3)。

雖然英特爾把CPU的權限分了4個優先級,但實際只用到了2個。

對於虛擬機來說,要想模擬作業系統的運行,也必須進行權限分級。

1,CPU的權限分級,主要是指內存的訪問權限。

intel的CPU分為實模式保護模式,保護模式最主要的作用就是保護內存的訪問權限。

內核代碼可以訪問所有的內存,但是用戶代碼只能訪問進程的用戶空間(內存)。

用戶空間的內存是通過進程的頁表來管理的,而進程的頁表只能通過系統內核來修改。

當使用malloc()分配內存的時候,實際上並不是分配一塊物理內存,而只是把用戶空間的某一個內存範圍設置為可用

只有當進程代碼真去讀寫這個內存範圍的時候,作業系統才會給它分配物理內存,即Linux的寫時複製需求加載機制。

所以虛擬機要想「模擬」作業系統的運行,首先要模擬CPU的保護模式。

2,CPU保護模式的實現,靠的就是幾個控制寄存器。

對於intel CPU來說,跟保護模式下相關的寄存器是cr0, cr1, cr2, cr3。

其中cr0用於控制分段和分頁機制,一旦開啟內存的分段機制就進入了保護模式。

一旦開啟了內存的分頁機制,作業系統可以支持的進程個數就是無限的了。

開啟了分頁之後,作業系統就可以4096位元組的一個頁為單位,為進程分配「必需的」內存空間,非常的靈活。

什麼時候必需?

當然是寫時複製需求加載的時候必需,所以進程剛創建時除了它的task_struct結構之外,只需要給它分配4096位元組做為頁目錄即可,其他的都可以跟父進程共享

對於多進程多任務的作業系統來說,內存的分頁機制是必需的,因為分段機制太死板了。

cr3就是頁目錄基地址寄存器,哪個進程運行時它就指向哪個進程的頁表,內核運行時它就指向內核頁表。

cr2在缺頁中斷時用於保存進程用戶空間的內存地址。在哪個位置出錯了,就保存哪個地址,然後作業系統就會為那個位置(所在的內存頁)分配內存。

獲取一個位置addr所在的內存頁非常的簡單,把它的最低12位清零就行,addr & ~0xfff[呲牙]

3,虛擬機要想模擬作業系統的運行,必須自己實現MMU的功能。

作業系統的運行,首先要依賴這幾個控制寄存器。

這幾個控制寄存器的主要作用,其實就是內存管理。

真實的硬體上,內存管理是通過MMU實現的。MMU可以根據進程的頁表實現用戶空間的內存地址(線性地址)到物理內存的映射。

如果在虛擬機上,這部分功能就只能通過代碼去實現了。

虛擬機要實現三層內存地址的映射:虛擬進程的用戶內存地址 --> 虛擬物理內存的物理地址 --> 虛擬機所在的真實進程的用戶內存地址。

所以像qemu這種能夠直接運行Linux系統的大型虛擬機,是必須要實現CPU的控制寄存器和系統級指令的。

系統級指令,指的是只能在內核代碼(或引導扇區)里運行的指令,例如:

pushfl 把標誌寄存器壓棧,

mov cr2, eax 把導致缺頁的內存地址讀到eax寄存器,

mov ax, cs 加載段選擇符,等等。

4,腳本語言的虛擬機

腳本語言因為是運行在用戶進程中,運行的代碼也是用戶態代碼,所以實現起來比qemu這類虛擬機要簡單的多。

它只需要解釋一些常用指令就行了,不需要處理系統級的指令,也不需要管理複雜的內存映射。

它只需要把編譯之後的字節碼文件根據程序頭的信息加載起來,並且處理動態庫函數的調用(動態連結),就可以實現腳本語言的運行了。

最主要的是,腳本語言的字節碼和編譯器都是腳本語言的作者設計的,作者可以實現字節碼和虛擬機的精確匹配,而不需要去實現CPU的整個指令集。

系統級的虛擬機就不得不實現CPU的整個指令集,因為OS內核被編譯之後有可能用到CPU的所有指令,其中任何一條指令沒被支持都可能導致內核運行失敗。

腳本語言的虛擬機怎麼寫,之前已經說過了,不再細說了。

關鍵字: