MIT6.S081 - Lecture3: OS Organization and System Calls

jll133688發表於2024-04-20

為什麼要使用作業系統

  1. 使用作業系統的主要原因是為了實現 CPU 多程序分時複用以及記憶體隔離
  2. 如果沒有作業系統,應用程式會直接與硬體進行互動,這時應用程式會直接使用 CPU,比如假設只有一個 CPU 核,一個應用程式在這個 CPU 核上執行,但是同時其他程式也需要執行,因為沒有作業系統來幫助切換,就需要應用程式時不時釋放 CPU 資源,但是如果這個程式的某個函式有一個死迴圈,那它就永遠也不會釋放 CPU,甚至沒辦法做到執行第三方程式來停止或者殺死這個死迴圈程式,這種情況下就沒辦法實現 CPU 多程序的分時複用
  3. 還有從記憶體的角度來看,如果應用程式直接執行在硬體上,則程式的資料程式碼都直接儲存到實體記憶體中,這樣不同程式的記憶體之間沒有明確邊界,就可能會造成一個程式儲存在本來屬於另外一個程式的記憶體空間,覆蓋另外一個程式中的內容

作業系統的隔離性需要隔離使用者程式和作業系統,也需要隔離不同的程序

系統呼叫與隔離性

  • 可以認為 exec 抽象了記憶體。當我們在執行 exec 系統呼叫的時候,我們會傳入一個檔名,而這個檔名對應了一個應用程式的記憶體映象。記憶體映象裡面包括了程式對應的指令,全域性的資料。應用程式可以逐漸擴充套件自己的記憶體,但是應用程式並沒有直接訪問實體記憶體的許可權,例如應用程式不能直接訪問實體記憶體的 1000-2000 這段地址。不能直接訪問的原因是,作業系統會提供記憶體隔離並控制記憶體,作業系統會在應用程式和硬體資源之間提供一箇中間層。exec 是這樣一種系統呼叫,它表明了應用程式不能直接訪問實體記憶體。
  • files 基本上是抽象了磁碟。應用程式不會直接讀取磁碟,在 Unix 中它與儲存系統互動的唯一方式就是透過 files。作業系統會決定如何將檔案與磁碟中的塊對應,確保一個磁碟塊只出現在一個檔案中,並保證使用者 A 不能操作使用者 B 的檔案。files 實現了不同使用者之間以及同一使用者不同程序之間的檔案隔離

硬體實現強隔離性

實現強隔離性的硬體支援包括了兩部分:

  1. user/kernel mode:處理器有兩種操作模式:user mode 和 kernel mode:

    • 當執行在 kernel model 時,CPU 能執行特定許可權的指令(直接操作硬體的指令和設定保護的指令,如設定 page table 暫存器、關閉時鐘中斷)
    • 當執行在 user mode 時,CPU 只能執行普通許可權的指令

    RISC-V 實際上有三種許可權:user/kernel/machine mode

  2. 在 RISC-V 中,如果你在使用者空間(user space)嘗試執行一條特殊許可權指令,使用者程式會透過系統呼叫來切換到 kernel mode。當使用者程式執行系統呼叫,會透過 ECALL 觸發一個軟中斷(software interrupt),軟中斷會查詢作業系統預先設定的中斷向量表,並執行中斷向量表中包含的中斷處理程式。中斷處理程式在核心中,這樣就完成了 user mode 到 kernel mode 的切換,並執行使用者程式想要執行的特殊許可權指令

  3. 虛擬記憶體:每個程序都有自己獨立的 page table,page table 將虛擬記憶體地址和實體記憶體地址做了對應

ECALL 指令

  • 在 RISC-V 中,ECALL 指令可以讓使用者程式將控制權轉移給核心,並傳入一個數字,這個數字表示了應用程式想要呼叫的 System Call
  • ECALL 會跳轉到核心中一個特定的位置,在核心側,有一個位於 syscall.c 的函式 syscall,每一個從應用程式發起的系統呼叫都會呼叫到這個 syscall 函式,syscall 函式會檢查 ECALL 的引數

核心是如何奪回控制權

  • 核心會透過硬體設定一個定時器,定時器到期之後會將控制許可權從使用者空間轉移到核心空間,之後核心就有了控制能力並可以重新排程 CPU 到另一個程序中

單核心 vs 微核心

單核心

XV6 中,所有的作業系統服務都在 kernel mode 中,這種形式被稱為單核心

  • 從安全的角度來說,這種方式不太好,在一個單核心中,任何一個作業系統的 Bug 都有可能成為漏洞,如果有許多行程式碼執行在核心中,那麼出現嚴重 Bug 的可能性也變得更大
  • 單核心的優勢在於可以將檔案系統、虛擬記憶體、程序管理這些實現特定功能的子模組緊密地整合在一起,這樣可以提供很好的效能

微核心

微核心模式下,在核心中只有非常少的模組,將核心中的其他部分作為普通的使用者程式來執行

  • 這樣做的好處是核心中的程式碼數量較小,降低了 bug 出現的可能
  • 如果使用者程式想要使用核心的功能,但由於核心的程式作為普通的使用者程式,比如說某個系統想要使用檔案系統,需要完成從使用者空間到核心空間,再從核心到使用者空間來訪問檔案系統,檔案系統也需要經過同樣的路徑將訊息返回給使用者系統,使得在微核心從使用者到核心的跳轉是單核心的兩倍,而反覆跳轉帶來了效能的損耗,且微核心的核心程式被隔離開,難以實現共享,降低了系統的效能。

XV6 程式碼結構

程式碼主要由三部分組成:

  1. kernel:包含了基本上所有的核心檔案
  2. user:這基本上是執行在 user mode 的程式
  3. mkfs:會建立一個空的檔案映象,將這個映象存在磁碟上,就可以直接使用一個空的檔案系統

kernel 編譯過程

  1. Makefile 讀取一個 C 檔案,比如 proc.c,然後呼叫 gcc 編譯器,生成一個叫 proc.s 的檔案,這是 RISC-V 組合語言檔案,然後再走到彙編直譯器,生成 proc.o,這是組合語言的二進位制格式,Makefile 會為所有核心檔案做相同的操作
  2. 系統載入器收集所有的.o 檔案,將它們連結在一起並生成核心檔案

相關文章