您的總體想法大部分都是正確的,但要做出調整:整個機器只有一個「內核空間」,所有進程都共享它。
當進程處於活動狀態時,它可以在「用戶模式」或「內核模式」下運行。
在用戶模式下,CPU執行的指令位於內存映射的用戶空間一側。該程序正在運行自己的代碼或來自用戶空間庫的代碼。在用戶模式下,一個進程的能力有限。 CPU中有一個標誌,告訴它不允許使用特權指令,而內核內存雖然存在於進程的內存映射中,但是無法訪問。 (你不希望讓任何程序只讀寫內核的內存 - 所有的安全性都將消失。)
當一個進程想要執行某些操作而不是在自己的(用戶空間)虛擬內存中移動數據時例如打開一個文件,它必須進行系統調用。每個CPU體系結構都有自己獨特的使系統調用的獨特方法,但它們都歸結爲:一個神奇的指令被執行,CPU打開「特權模式」標誌,並跳轉到內核空間中的特殊地址,「系統調用入口點」。
現在該進程正在內核模式下運行。正在執行的指令位於內核內存中,並且可以讀取和寫入他們想要的任何內存。內核檢查進程剛剛做出的請求並決定如何處理它。
在open
例如,內核接收到對應於的int open(const char *filename, int flags[, int mode])
參數2個或3個參數。第一個參數提供了內核空間何時需要訪問用戶空間的示例。你說open("foo", O_RDONLY)
所以字符串"foo"
是用戶空間中程序的一部分。系統調用機制只傳遞一個指針,而不是一個字符串,所以內核必須從用戶內存中讀取字符串。
要查找請求的文件,內核可以諮詢文件系統驅動程序(找出文件的位置)並阻止設備驅動程序(從磁盤加載必要的塊)或網絡設備驅動程序和協議(加載文件來自遠程源)。所有這些東西都是內核的一部分,即在內核空間中,而不管它們是內置還是作爲模塊加載。
如果請求不能立即滿足,內核可以將進程睡眠。這意味着該進程將從CPU中取出,直到從磁盤或網絡收到響應。另一個流程現在可能有機會運行。稍後,當響應進入時,您的進程再次開始運行(仍處於內核模式)。現在找到該文件,系統調用可以完成(檢查權限,創建文件描述符)並返回到用戶空間。
返回用戶空間是一件簡單的事情:將CPU置回非特權模式,並將寄存器恢復爲用戶 - >內核轉換之前的狀態,指令指針指向magic syscall指令之後的指令。
另外的系統調用,還有其他的東西,可以從用戶模式導致過渡到內核模式,包括:
- 頁面錯誤 - 如果您的進程訪問沒有物理地址的虛擬內存地址分配給它,CPU進入內核模式並跳轉到頁錯誤處理程序。然後內核決定虛擬地址是否有效,它會創建一個物理頁面並在其停止的用戶空間中恢復該進程,或者發送一個SIGSEGV。
- 中斷 - 一些硬件(網絡,硬盤,串行端口等)通知需要注意它的CPU。 CPU進入內核模式並跳轉到一個處理程序,內核對它作出響應,然後恢復中斷之前正在運行的用戶空間進程。
加載模塊是通過系統調用來完成的,該系統調用會要求內核將模塊的代碼和數據複製到內核空間並在內核模式下運行其初始化代碼。
這是很長,所以我停止。我希望以用戶內核轉換爲重點的演練已經提供了足夠的例子來鞏固這個想法。