Linux 概念架構的理解

1 贊 回覆發表於2015-12-09

摘要

Linux kernel 成功的兩個原因:

  1. 架構設計支援大量的志願開發者加入到開發過程中;
  2. 每個子系統,尤其是那些需要改進的,都支援很好的擴充套件性。

正是這兩個原因使得 Linux kernel 可以不斷進化。

Linux 概念架構的理解

一、Linux核心在整個計算機系統中的位置

Linux 概念架構的理解
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.

這種子系統之間的依賴性只能是從上到下,也就是圖中頂部的子系統依賴底部的子系統,反之則不行。

二、核心的作用

  1. 虛擬化(抽象),將計算機硬體抽象為一臺虛擬機器,供使用者程式(process)使用;程式執行時完全不需要知道硬體是如何工作的,只要呼叫 Linux kernel 提供的虛擬介面(virtual interface)即可。
  2. 多工處理,實際上是多個任務在並行使用計算機硬體資源,核心的任務是仲裁對資源的使用,製造每個程式都以為自己是獨佔系統的錯覺。

PS:程式上下文切換就是要換掉程式狀態字、換掉頁表基地址暫存器的內容、換掉 current 指向的 task_struct 例項、換掉 PC ——>也就換掉了程式開啟的檔案(通過 task_struct 的 files 可以找到)、換掉了程式記憶體的執行空間(通過 task_struct 的 mem 可以找到);

三、Linux核心的整體架構

Linux 概念架構的理解
Linux核心的整體架構

中心繫統是程式排程器(Process Scheduler,SCHED):所有其餘的子系統都依賴於程式排程器,因為其餘子系統都需要阻塞和恢復程式。當一個程式需要等待一個硬體動作完成時,相應子系統會阻塞這個程式;當這個硬體動作完成時,子系統會將這個程式恢復:這個阻塞和恢復動作都要依賴於程式排程器完成。

上圖中的每一個依賴箭頭都有原因:

  • 程式排程器依賴記憶體管理器(Memory manager):程式恢復執行時,需要依靠記憶體管理器分配供它執行的記憶體。
  • IPC 子系統依賴於記憶體管理器:共享記憶體機制是程式間通訊的一種方法,執行兩個程式利用同一塊共享的記憶體空間進行資訊傳遞。
  • VFS 依賴於網路介面(Network Interface):支援 NFS 網路檔案系統;
  • VFS 依賴於記憶體管理器:支援 ramdisk 裝置
  • 記憶體管理器依賴於 VFS,因為要支援交換(swapping),可以將暫時不執行的程式換出到磁碟上的交換分割槽(swap),進入掛起狀態。

四、高度模組化設計的系統,利於分工合作。

  1. 只有極少數的程式設計師需要橫跨多個模組開展工作,這種情況確實會發生,僅發生在當前系統需要依賴另一個子系統時;
  2. 硬體裝置驅動(hardware device drivers)、檔案系統模組(logical filesystem modules)、網路裝置驅動(network device drivers)和網路協議模組(network protocol modules)這四個模組的可擴充套件性最高。

五、系統中的資料結構

  1. 任務列表(Task List)
    程式排程器針對每個程式維護一個資料結構 task_struct;所有的程式用連結串列管理,形成 task list;程式排程器還維護一個 current 指標指向當前正在佔用 CPU 的程式。
  2. 記憶體對映(Memory Map)
    記憶體管理器儲存每個程式的虛擬地址到實體地址的對映;並且也提供瞭如何換出特定的頁,或者是如何進行缺頁處理。這些資訊存放在資料結構 mm_struct 中。每個程式都有一個 mm_struct 結構,在程式的 task_struct 結構中有一個指標 mm 指向次程式的 mm_struct 結構。
    在 mm_struct 中有一個指標 pgd,指向該程式的頁目錄表(即存放頁目錄首地址)——>當該程式被排程時,此指標被換成實體地址,寫入控制暫存器 CR3(x86體系結構下的頁基址暫存器)
  3. I-nodes
    VFS 通過 inodes 節點表示磁碟上的檔案映象,inodes 用於記錄檔案的物理屬性。每個程式都有一個 files_struct 結構,用於表示該程式開啟的檔案,在 task_struct 中有個 files 指標。使用 inodes 節點可以實現檔案共享。檔案共享有兩種方式:(1)通過同一個系統開啟檔案 file 指向同一個 inodes 節點,這種情況發生於父子程式間;(2)通過不同系統開啟檔案指向同一個 inode 節點,舉例有硬連結;或者是兩個不相關的指標開啟同一個檔案。
  4. 資料連線(Data Connection)
    核心中所有的資料結構的根都在程式排程器維護的任務列表連結串列中。系統中每個程式的的資料結構 task_struct 中有一個指標 mm 指向它的記憶體對映資訊;也有一個指標 files 指向它開啟的檔案(使用者開啟檔案表);還有一個指標指向該程式開啟的網路套接字。

六、子系統架構

1.程式排程器(Process Scheduler)架構

(1)目標

程式排程器是 Linux kernel 中最重要的子系統。系統通過它來控制對 CPU 的訪問——不僅僅是使用者程式對 CPU 的訪問,也包括其餘子系統對 CPU 的訪問。

(2)模組

Linux 概念架構的理解
程式排程器

排程策略模組(scheduling policy module):決定哪個程式獲得對 CPU 的訪問權;排程策略應該讓所有程式儘可能公平得共享 CPU。

  • 體系結構相關模組(architecture-specific module)設計一組統一的抽象介面來遮蔽特定體系介面晶片的硬體細節。這個模組與 CPU 互動以阻塞和恢復程式。這些操作包括獲取每個程式需要儲存的暫存器和狀態資訊、執行彙編程式碼來完成阻塞或者恢復操作。
  • 體系結構無關模組(architecture-independent module)與排程策略模組互動將決定下一個執行的程式,然後呼叫體系結構相關的程式碼去恢復那個程式的執行。不僅如此,這個模組還會呼叫記憶體管理器的介面來確保被阻塞的程式的記憶體對映資訊被正確得儲存起來。
  • 系統呼叫介面模組(system call interface)允許使用者程式訪問 Linux Kernel 明確暴露給使用者程式的資源。通過一組定義合適的基本上不變的介面(POSIX 標準),將使用者應用程式和 Linux 核心解耦,使得使用者程式不會受到核心變化的影響。

(3)資料表示

排程器維護一個資料結構——task list,其中的元素時每個活動的程式 task_struct 例項;這個資料結構不僅僅包含用來阻塞和恢復程式的資訊,也包含額外的計數和狀態資訊。這個資料結構在整個 kernel 層都可以公共訪問。

(4)依賴關係、資料流、控制流

正如前面提到過的,排程器需要呼叫記憶體管理器提供的功能,去為需要恢復執行的程式選擇合適的實體地址,正因為如此,所以 程式排程器子系統依賴於記憶體管理子系統。當其他核心子系統需要等待硬體請求完成時,它們都依賴於程式排程子系統進行程式的阻塞和恢復。這種依賴性通過函式呼叫和訪問共享的 task list 資料結構來體現。所有的核心子系統都要讀或者寫代表當前正在執行程式的資料結構,因此形成了貫穿整個系統的雙向資料流。

除了核心層的資料流和控制流,OS 服務層還給使用者程式提供註冊定時器的介面。這形成了由排程器對使用者程式的控制流。通常喚醒睡眠程式的用例不在正常的控制流範圍,因為使用者程式無法預知何時被喚醒。最後,排程器與 CPU 互動來阻塞和恢復程式,這又形成它們之間的資料流和控制流——CPU 負責打斷當前正在執行的程式,並允許核心排程其他的程式執行。

2.記憶體管理器(Memory Manager)架構

(1)目標

記憶體管理模組負責控制程式如何訪問實體記憶體資源。通過硬體記憶體管理系統(MMU)管理程式虛擬記憶體和機器實體記憶體之間的對映。每一個程式都有自己獨立的虛擬記憶體空間,所以兩個程式可能有相同的虛擬地址,但是它們實際上在不同的實體記憶體區域執行。MMU 提供記憶體保護,讓兩個程式的實體記憶體空間不互相干擾。記憶體管理模組還支援交換——將暫時不用的記憶體頁換出到磁碟上的交換分割槽,這種技術讓程式的虛擬地址空間大於實體記憶體的大小。虛擬地址空間的大小由機器字長決定。

(2)模組

Linux 概念架構的理解
記憶體管理子系統

  • 架構相關模組(architecture specific module)提供訪問實體記憶體的虛擬介面;
  • 架構無關模組(architecture independent module)負責每個程式的地址對映以及虛擬記憶體交換。當發生缺頁錯誤時,由該模組負責決定哪個記憶體頁應該被換出記憶體——因為這個記憶體頁換出選擇演算法幾乎不需要改動,所以這裡沒有建立一個獨立的策略模組。
  • 系統呼叫介面(system call interface)為使用者程式提供嚴格的訪問介面(malloc 和 free;mmap 和 ummap)。這個模組允許用程式分配和釋放記憶體、執行記憶體對映檔案操作。

(3)資料表示

記憶體管理存放每個程式的虛擬記憶體到實體記憶體的對映資訊。這種對映資訊存放在 mm_struct 結構例項中,這個例項的指標又存放在每個程式的 task_struct 中。除了存放對映資訊,資料塊中還應該存放關於記憶體管理器如何獲取和儲存頁的資訊。例如:可執行程式碼能夠將可執行映象作為備份儲存;但是動態申請的資料則必須備份到系統頁中。(這個沒看懂,請高手解惑?)

最後,記憶體管理模組還應該存放訪問和技術資訊,以保證系統的安全。

(4)依賴關係、資料流和控制流

記憶體管理器控制實體記憶體,當頁面失敗(page fault)發生時,接受硬體的通知(缺頁中斷)—— 這意味著在記憶體管理模組和記憶體管理硬體之間存在雙向的資料流和控制流。記憶體管理也依賴檔案系統來支援交換和記憶體對映 I/O——這種需求意味著記憶體管理器需要呼叫對檔案系統提供的函式介面(procedure calls),往磁碟中存放記憶體頁和從磁碟中取記憶體頁。因為檔案系統請求非常慢,所以在等待記憶體頁被換入之前,記憶體管理器要讓程式需要進入休眠——這種需求讓記憶體管理器呼叫程式排程器的介面。由於每個程式的記憶體對映存放在程式排程器的資料結構中,所以在記憶體管理器和程式排程器之間也有雙向的資料流和控制流。使用者程式可以建立新的程式地址空間,並且能夠感知缺頁錯誤——這裡需要來自記憶體管理器的控制流。一般來說沒有使用者程式到記憶體管理器的資料流,但是使用者程式卻可以通過 select 系統呼叫,從記憶體管理器獲取一些資訊。

3.虛擬檔案系統(Virtual File System)架構

(1)目標

虛擬檔案系統為儲存在硬體裝置上資料提供統一的訪問介面。可以相容不同的檔案系統(ext2,ext4,ntf等等)。計算機中幾乎所有的硬體裝置都被表示為一個通用的裝置驅動介面。邏輯檔案系統促進與其他作業系統標準的相容性,並且允許開發者以不同的策略實現檔案系統。虛擬檔案系統更進一步,允許系統管理員在任何裝置上掛載任何邏輯檔案系統。虛擬檔案系統封裝物理裝置和邏輯檔案系統的細節,並且允許使用者程式使用統一的介面訪問檔案。

除了傳統的檔案系統目標,VFS 也負責裝載新的可執行檔案。這個任務由邏輯檔案系統模組完成,使得 Linux 可以支援多種可執行檔案。

(2)模組

Linux 概念架構的理解
虛擬檔案系統模組

  • 裝置驅動模組(device driver module)
  • 裝置獨立介面模組(Device Independent Interface):提供所有裝置的同一檢視
  • 邏輯檔案系統(logical file system):針對每種支援的檔案系統
  • 系統獨立介面(system independent interface)提供硬體資源和邏輯檔案系統都無關的介面,這個模組通過塊裝置節點或者字元裝置節點提供所有的資源。
  • 系統呼叫模組(system call interface)提供使用者程式對檔案系統的統一控制訪問。虛擬檔案系統為使用者程式遮蔽了所有特殊的特性。

(3)資料表示

所有檔案使用 inode 表示。每個 inode 都記錄一個檔案在硬體裝置上的位置資訊。不僅如此,inode 還存放著指向邏輯檔案系統模組和裝置驅動的的函式指標,這些指標能夠執行具體的讀寫操作。通過按照這種形式(就是物件導向中的虛擬函式的思想)存放函式指標,具體的邏輯檔案系統和裝置驅動可以向核心註冊自己而不需要核心依賴具體的模組特性。

(4)依賴關係、資料流和控制流

一個特殊的裝置驅動是 ramdisk,這個裝置在主存中開闢一片區域,並把它當成永續性儲存裝置使用。這個裝置驅動使用記憶體管理模組完成任務,所以在 VFS 與對記憶體管理模組存在依賴關係(圖中的依賴關係反了,應該是 VFS 依賴於記憶體管理模組)、資料流和控制流。

邏輯檔案系統支援網路檔案系統。這個檔案系統像訪問本地檔案一樣,從另一臺機器上訪問檔案。為了實現這個功能,一種邏輯檔案系統通過網路子系統完成它的任務——這引入了 VFS 對網路子系統的一個依賴關係以及它們之間的控制流和資料流。

正如前面提到的,記憶體管理器使用 VFS 完成記憶體交換功能和記憶體對映 I/O。另外,當 VFS 等待硬體請求完成時,VFS 需要使用程式排程器阻塞程式;當請求完成時,VFS 需要通過程式排程器喚醒程式。最後,系統呼叫介面允許使用者程式呼叫來存取資料。不像前面的子系統,VFS 沒有提供給使用者註冊不明確呼叫的機制,所以沒有從VFS到使用者程式的控制流。

4.網路介面(Network Interface)架構

(1)目標

網路子系統讓 Linux 系統能夠通過網路與其他系統相連。這個子系統支援很多硬體裝置,也支援很多網路協議。網路子系統將硬體和協議的實現細節都遮蔽掉,並抽象出簡單易用的介面供使用者程式和其他子系統使用——使用者程式和其餘子系統不需要知道硬體裝置和協議的細節。

(2)模組

Linux 概念架構的理解
網路協議層模組圖

  • 網路裝置驅動模組(network device drivers)
  • 裝置獨立介面模組(device independent interface module)提供所有硬體裝置的一致訪問介面,使得高層子系統不需要知道硬體的細節資訊。
  • 網路協議模組(network protocol modules)負責實現每一個網路傳輸協議,例如:TCP,UDP,IP,HTTP,ARP等等~
  • 協議無關模組(protocol independent interface)提供獨立於具體協議和具體硬體裝置的一致性介面。這使得其餘核心子系統無需依賴特定的協議或者裝置就能訪問網路。
  • 系統呼叫介面模組(system calls interface)規定了使用者程式可以訪問的網路程式設計API

(3)資料表示

每個網路物件都被表示為一個套接字(socket)。套接字與程式關聯的方法和 inode 節點相同。通過兩個 task_struct 指向同一個套接字,套接字可以被多個程式共享。

(4)資料流,控制流和依賴關係

當網路子系統需要等待硬體請求完成時,它需要通過程式排程系統將程式阻塞和喚醒——這形成了網路子系統和程式排程子系統之間的控制流和資料流。不僅如此,虛擬檔案系統通過網路子系統實現網路檔案系統(NFS)——這形成了 VFS 和網路子系統指甲的資料流和控制流。

七、結論

1、Linux 核心是整個 Linux 系統中的一層。核心從概念上由五個主要的子系統構成:程式排程器模組、記憶體管理模組、虛擬檔案系統、網路介面模組和程式間通訊模組。這些模組之間通過函式呼叫和共享資料結構進行資料互動。

2、Linux 核心架構促進了他的成功,這種架構使得大量的志願開發人員可以合適得分工合作,並且使得各個特定的模組便於擴充套件。

  • 可擴充套件性一:Linux 架構通過一項資料抽象技術使得這些子系統成為可擴充套件的——每個具體的硬體裝置驅動都實現為單獨的模組,該模組支援核心提供的統一的介面。通過這種方式,個人開發者只需要和其他核心開發者做最少的互動,就可以為 Linux 核心新增新的裝置驅動。
  • 可擴充套件性二:Linux 核心支援多種不同的體系結構。在每個子系統中,都將體系結構相關的程式碼分割出來,形成單獨的模組。通過這種方法,一些廠家在推出他們自己的晶片時,他們的核心開發小組只需要重新實現核心中機器相關的程式碼,就可以講核心移植到新的晶片上執行。

參考文章:

  1. http://oss.org.cn/ossdocs/linux/kernel/a1/index.html
  2. http://www.cs.cmu.edu/afs/cs/project/able/www/paper_abstracts/intro_softarch.html
  3. http://www.cs.cmu.edu/afs/cs/project/able/www/paper_abstracts/intro_softarch.html
  4. http://www.fceia.unr.edu.ar/ingsoft/monroe00.pdf
  5. 核心原始碼:http://lxr.oss.org.cn/

相關文章