Linux kernel 成功的兩個原因:
正是這兩個原因使得 Linux kernel 可以不斷進化。
Fig 1 - 計算機系統分層結構
分層結構的原則:
the dependencies between subsystems are from the top down: layers pictured near the top depend on lower layers, but subsystems nearer the bottom do not depend on higher layers.
這種子系統之間的依賴性只能是從上到下,也就是圖中頂部的子系統依賴底部的子系統,反之則不行。
PS:進程上下文切換就是要換掉程序狀態字、換掉頁表基地址寄存器的內容、換掉 current 指向的 task_struct 實例、換掉 PC ——>也就換掉了進程打開的文件(通過 task_struct 的 files 可以找到)、換掉了進程內存的執行空間(通過 task_struct 的 mem 可以找到);
Linux內核的整體架構
中心系統是進程調度器(Process Scheduler,SCHED):所有其余的子系統都依賴於進程調度器,因為其余子系統都需要阻塞和恢復進程。當一個進程需要等待一個硬件動作完成時,相應子系統會阻塞這個進程;當這個硬件動作完成時,子系統會將這個進程恢復:這個阻塞和恢復動作都要依賴於進程調度器完成。
上圖中的每一個依賴箭頭都有原因:
進程調度器是 Linux kernel 中最重要的子系統。系統通過它來控制對 CPU 的訪問——不僅僅是用戶進程對 CPU 的訪問,也包括其余子系統對 CPU 的訪問。
進程調度器
調度策略模塊(scheduling policy module):決定哪個進程獲得對 CPU 的訪問權;調度策略應該讓所有進程盡可能公平得共享 CPU。
調度器維護一個數據結構——task list,其中的元素時每個活動的進程 task_struct 實例;這個數據結構不僅僅包含用來阻塞和恢復進程的信息,也包含額外的計數和狀態信息。這個數據結構在整個 kernel 層都可以公共訪問。
正如前面提到過的,調度器需要調用內存管理器提供的功能,去為需要恢復執行的進程選擇合適的物理地址,正因為如此,所以 進程調度器子系統依賴於內存管理子系統。當其他內核子系統需要等待硬件請求完成時,它們都依賴於進程調度子系統進行進程的阻塞和恢復。這種依賴性通過函數調用和訪問共享的 task list 數據結構來體現。所有的內核子系統都要讀或者寫代表當前正在運行進程的數據結構,因此形成了貫穿整個系統的雙向數據流。
除了內核層的數據流和控制流,OS 服務層還給用戶進程提供注冊定時器的接口。這形成了由調度器對用戶進程的控制流。通常喚醒睡眠進程的用例不在正常的控制流范圍,因為用戶進程無法預知何時被喚醒。最後,調度器與 CPU 交互來阻塞和恢復進程,這又形成它們之間的數據流和控制流——CPU 負責打斷當前正在運行的進程,並允許內核調度其他的進程運行。
內存管理模塊負責控制進程如何訪問物理內存資源。通過硬件內存管理系統(MMU)管理進程虛擬內存和機器物理內存之間的映射。每一個進程都有自己獨立的虛擬內存空間,所以兩個進程可能有相同的虛擬地址,但是它們實際上在不同的物理內存區域運行。MMU 提供內存保護,讓兩個進程的物理內存空間不互相干擾。內存管理模塊還支持交換——將暫時不用的內存頁換出到磁盤上的交換分區,這種技術讓進程的虛擬地址空間大於物理內存的大小。虛擬地址空間的大小由機器字長決定。
內存管理子系統
架構相關模塊(architecture specific module)提供訪問物理內存的虛擬接口;
架構無關模塊(architecture independent module)負責每個進程的地址映射以及虛擬內存交換。當發生缺頁錯誤時,由該模塊負責決定哪個內存頁應該被換出內存——因為這個內存頁換出選擇算法幾乎不需要改動,所以這裡沒有建立一個獨立的策略模塊。
系統調用接口(system call interface)為用戶進程提供嚴格的訪問接口(malloc 和 free;mmap 和 ummap)。這個模塊允許用進程分配和釋放內存、執行內存映射文件操作。
內存管理存放每個進程的虛擬內存到物理內存的映射信息。這種映射信息存放在 mm_struct 結構實例中,這個實例的指針又存放在每個進程的 task_struct 中。除了存放映射信息,數據塊中還應該存放關於內存管理器如何獲取和存儲頁的信息。例如:可執行代碼能夠將可執行鏡像作為備份存儲;但是動態申請的數據則必須備份到系統頁中。(這個沒看懂,請高手解惑?)
最後,內存管理模塊還應該存放訪問和技術信息,以保證系統的安全。
內存管理器控制物理內存,當頁面失敗( page fault )發生時,接受硬件的通知(缺頁中斷)—— 這意味著在內存管理模塊和內存管理硬件之間存在雙向的數據流和控制流。內存管理也依賴文件系統來支持交換和內存映射 I/O——這種需求意味著內存管理器需要調用對文件系統提供的函數接口(procedure calls),往磁盤中存放內存頁和從磁盤中取內存頁。因為文件系統請求非常慢,所以在等待內存頁被換入之前,內存管理器要讓進程需要進入休眠——這種需求讓內存管理器調用進程調度器的接口。由於每個進程的內存映射存放在進程調度器的數據結構中,所以在內存管理器和進程調度器之間也有雙向的數據流和控制流。用戶進程可以建立新的進程地址空間,並且能夠感知缺頁錯誤——這裡需要來自內存管理器的控制流。一般來說沒有用戶進程到內存管理器的數據流,但是用戶進程卻可以通過 select 系統調用,從內存管理器獲取一些信息。
虛擬文件系統為存儲在硬件設備上數據提供統一的訪問接口。可以兼容不同的文件系統(ext2,ext4,ntf等等)。計算機中幾乎所有的硬件設備都被表示為一個通用的設備驅動接口。邏輯文件系統促進與其他操作系統標准的兼容性,並且允許開發者以不同的策略實現文件系統。虛擬文件系統更進一步,允許系統管理員在任何設備上掛載任何邏輯文件系統。虛擬文件系統封裝物理設備和邏輯文件系統的細節,並且允許用戶進程使用統一的接口訪問文件。
除了傳統的文件系統目標,VFS 也負責裝載新的可執行文件。這個任務由邏輯文件系統模塊完成,使得 Linux 可以支持多種可執行文件。
虛擬文件系統模塊
所有文件使用 inode 表示。每個 inode 都記錄一個文件在硬件設備上的位置信息。不僅如此,inode 還存放著指向邏輯文件系統模塊和設備驅動的的函數指針,這些指針能夠執行具體的讀寫操作。通過按照這種形式(就是面向對象中的虛函數的思想)存放函數指針,具體的邏輯文件系統和設備驅動可以向內核注冊自己而不需要內核依賴具體的模塊特性。
一個特殊的設備驅動是 ramdisk,這個設備在主存中開辟一片區域,並把它當成持久性存儲設備使用。這個設備驅動使用內存管理模塊完成任務,所以在 VFS 與對內存管理模塊存在依賴關系(圖中的依賴關系反了,應該是 VFS 依賴於內存管理模塊)、數據流和控制流。
邏輯文件系統支持網絡文件系統。這個文件系統像訪問本地文件一樣,從另一台機器上訪問文件。為了實現這個功能,一種邏輯文件系統通過網絡子系統完成它的任務——這引入了 VFS 對網絡子系統的一個依賴關系以及它們之間的控制流和數據流。
正如前面提到的,內存管理器使用 VFS 完成內存交換功能和內存映射 I/O。另外,當 VFS 等待硬件請求完成時,VFS 需要使用進程調度器阻塞進程;當請求完成時,VFS 需要通過進程調度器喚醒進程。最後,系統調用接口允許用戶進程調用來存取數據。不像前面的子系統,VFS 沒有提供給用戶注冊不明確調用的機制,所以沒有從VFS到用戶進程的控制流。
網絡子系統讓 Linux 系統能夠通過網絡與其他系統相連。這個子系統支持很多硬件設備,也支持很多網絡協議。網絡子系統將硬件和協議的實現細節都屏蔽掉,並抽象出簡單易用的接口供用戶進程和其他子系統使用——用戶進程和其余子系統不需要知道硬件設備和協議的細節。
網絡協議層模塊圖
每個網絡對象都被表示為一個套接字(socket)。套接字與進程關聯的方法和 inode 節點相同。通過兩個 task_struct 指向同一個套接字,套接字可以被多個進程共享。
當網絡子系統需要等待硬件請求完成時,它需要通過進程調度系統將進程阻塞和喚醒——這形成了網絡子系統和進程調度子系統之間的控制流和數據流。不僅如此,虛擬文件系統通過網絡子系統實現網絡文件系統(NFS)——這形成了 VFS 和網絡子系統指甲的數據流和控制流。
1、Linux 內核是整個 Linux 系統中的一層。內核從概念上由五個主要的子系統構成:進程調度器模塊、內存管理模塊、虛擬文件系統、網絡接口模塊和進程間通信模塊。這些模塊之間通過函數調用和共享數據結構進行數據交互。
2、Linux 內核架構促進了他的成功,這種架構使得大量的志願開發人員可以合適得分工合作,並且使得各個特定的模塊便於擴展。