作為現代操作系統(tǒng)的代表之一,Linux操作系統(tǒng)非常復雜,內部有多得令人眼花繚亂的各種組件在同步運行和相互通信。對于初學者來說,我認為理解操作系統(tǒng)工作原理最好的方法是利用抽象的思維去理解,也就是說,你可以暫時忽略大部分細節(jié)。就像坐車一樣,通常你不會去在意車內固定發(fā)動機的裝配螺栓,也不會關心你走的路是誰修筑的。如果你是一名乘客,可能只會關心如何打開或關閉車門、如何系好安全帶以及車要把你帶到哪兒去。如果你是一名司機,就需要了解更多的細節(jié),比如如何控制油門、剎車和換擋,以及如何處理意外情況。如果你是一名維修工程師或汽車設計師,則需要更深入地了解汽車構造及其工作原理了。
我們試一下運用“抽象思維”來理解開車這件事情,首先我們可以將“一輛汽車在路上行駛”抽象為三個部分:汽車、道路和駕駛操作。這樣一來,開車這件事情就簡單多了,我們幾乎只需要知道駕駛操作即可。如果道路顛簸,也不會去埋怨汽車本身和自己的駕駛技術,反而我們會問這條路為什么這么爛,而我們是不是一定要走這條路。同樣,在軟件開發(fā)過程中,開發(fā)人員通常不用太關心他們需要使用的組件的內部結構,他們只關心能使用哪些組件,以及這些組件該怎么用。跟汽車零部件一樣,每一個組件都可能包含著復雜的技術細節(jié),但我們可以暫時忽略這些細節(jié),而專注于這些組件在系統(tǒng)中發(fā)揮的功能。實際上,抽象思維形成的這種“分層思想”無論在計算機技術還是其他社會生產活動中都是適用的。
Linux操作系統(tǒng)的層次
下面我們來看一下,通過抽象可以將系統(tǒng)分解為哪些組件,以及這些組件在用戶和硬件系統(tǒng)之間所處的位置。
簡單來說,Linux操作系統(tǒng)可以大體分為三層,如下圖所示,最底層是硬件系統(tǒng),包括CPU、內存、硬盤、網卡等;硬件系統(tǒng)之上是內核,這是操作系統(tǒng)的核心,內核負責管理硬件系統(tǒng),同時為應用程序提供操作接口;用戶進程在這里表示計算機中運行的所有程序,它們運行于用戶空間,由內核統(tǒng)一管理。
內核和用戶進程之間最大的區(qū)別在于:內核運行于內核模式(kernel mode,也稱內核態(tài)),用戶進程運行于用戶模式(user mode,也稱用戶態(tài))。在內核模式中運行的代碼可以不受限制地訪問中央處理器和內存,也就是說內核可以為所欲為,那這就非常危險了,因為內核進程可以輕而易舉地使整個系統(tǒng)崩潰。所以為了提高系統(tǒng)穩(wěn)定性,限制進程對中央處理器和內存的訪問權限,提出了用戶模式的概念。
一般我們將只有內核可以訪問的空間稱為內核空間,而將用戶進程能夠訪問的空間稱為用戶空間。通過這種限制,即使某個用戶進程運行時崩潰了,也不會對整個系統(tǒng)造成嚴重的影響。
內核模式和用戶模式
實際上,內核模式和用戶模式是需要處理器支持的。內核程序和用戶程序的本質區(qū)別在于:除了可以執(zhí)行大部分通用指令,內核程序還可以執(zhí)行特權指令。說到計算機指令,就不得不提到RISC(Reduced Instruction Set Computer,精簡指令集)和CISC(Complex Instruction Set Computer,復雜指令集),我們知道Intel的x86架構芯片采用的是CISC,而ARM架構芯片則采用RISC。也就是說,內核模式和用戶模式之間的切換以及模式的實現依托于CPU指令集架構。
Intel的x86處理器通過Ring級別來進行訪問控制,共分為4個級別,即Ring0~Ring3。Ring0層擁有的權限最高,Ring3層擁有的權限最低。按照Intel原來的設想,應用程序工作在Ring3層,只能訪問Ring3層的空間;操作系統(tǒng)工作在Ring0層,可以訪問所有層的空間;而其他驅動程序工作與Ring1和Ring2層,每一層只能訪問本層和權限更低層的數據。這種設計可以有效保障操作系統(tǒng)的穩(wěn)定性和安全性。但是現代操作系統(tǒng),包括Windows和Linux都沒有采用4層權限,只使用了Ring0和Ring3層,對應于內核空間和用戶空間。因此,驅動一旦加載,就運行在Ring0層,擁有與操作系統(tǒng)內核一樣的權限。
和x86架構不同,ARM沒有Ring0~Ring3,也不存在Root模式和非Root模式。眾所周知,ARM有7種工作模式,即usr(用戶模式,User)、fiq(快速中斷模式,FIQ)、irq(外部中斷模式,IRQ)、svc(管理模式,Supervisor)、abt(數據訪問中止模式,Abort)、und(未定義指令中止模式,Undef)和sys(系統(tǒng)模式,System)。除了用戶模式以外的其他6種處理器模式都稱為特權模式(Privileged Modes)。在特權模式下,程序可以訪問所有的系統(tǒng)資源,也可以任意地進行處理器模式切換。
除此之外,還有在ARM v6中引入的Security Extensions帶來的Monitor模式,以及在ARM v7中引入的Virtualization Extensions帶來的Hyp模式。對于ARM v8架構則更為復雜一些,它定義了兩種執(zhí)行狀態(tài)(Execution State),分別是AArch32狀態(tài)和 AArch64狀態(tài)。同時定義了4個異常等級(Exception Level)來進行權限控制,分別是EL0~EL3。對于AArch32,ARMv8定義了9種PE模式(也就是上面提到的9種工作模式)來確定執(zhí)行權限,而不使用EL;而對于AArch64,則不支持PE模式。(更多關于處理器架構的信息,請查閱相關手冊)。
內存的作用
除了CPU,內存可以說是是硬件系統(tǒng)中最為重要的部分。內存中存儲的是0或1這樣的比特數據,內核和進程也都是運行在內存里面的,它們在內存中就是一系列的比特數據集合,所有外圍設備的數據輸入和輸出都通過內存完成。而CPU就像一個操作員一樣處理內存中的數據,它從內存讀取指令和數據,然后將運算結果寫回內存。Linux內核幾乎所有的操作都和內存有關,例如:將內存劃分為很多區(qū)塊,并且一直維護著這些區(qū)塊的狀態(tài)信息;每一個進程擁有自己的內存區(qū)塊,并且由內核保證每個進程只使用它自己的內存區(qū)塊。
Linux內核
Linux內核采用的是整體式結構(Monolithic),整個內核是一個單獨的、非常大的程序,這樣雖然能夠使系統(tǒng)的各個部分直接溝通,提高系統(tǒng)相應速度,但與嵌入式系統(tǒng)存儲容量小、資源有限的特點不相符合。因此,在嵌入式系統(tǒng)中經常采用的是另一種稱為微內核(Microkernel)的體系結構,即內核本身只提供一些最基本的操作系統(tǒng)功能,如任務調度、內存管理、中斷處理等,而類似于文件系統(tǒng)和網絡協議等附加功能則運行在用戶空間中,并且可以根據實際需要進行取舍。這樣可以大大減小內核的體積,便于維護和移植。
對于Linux這樣一個宏內核操作系統(tǒng)來說,一個完整的Linux內核主要由五個子系統(tǒng)組成:進程調度,內存管理,虛擬文件系統(tǒng),網絡接口,進程間通信。
·進程調度(SCHED)控制進程對CPU的訪問。當需要選擇下一個進程運行時,由調度程序選擇最值得運行的進程??蛇\行進程實際上是僅等待CPU資源的進程,如果某個進程在等待其它資源,則該進程是不可運行進程。Linux使用了比較簡單的基于優(yōu)先級的進程調度算法選擇新的進程。
·內存管理(MM)允許多個進程安全的共享主內存區(qū)域。Linux 的內存管理支持虛擬內存,即在計算機中運行的程序,其代碼,數據,堆棧的總量可以超過實際內存的大小,操作系統(tǒng)只是把當前使用的程序塊保留在內存中,其余的程序塊則保留在磁盤中。必要時,操作系統(tǒng)負責在磁盤和內存間交換程序塊。內存管理從邏輯上分為硬件無關部分和硬件有關部分。硬件無關部分提供了進程的映射和邏輯內存的對換;硬件相關的部分為內存管理硬件提供了虛擬接口。
·虛擬文件系統(tǒng)(Virtual File System,VFS)隱藏了各種硬件的具體細節(jié),為所有的設備提供了統(tǒng)一的接口,VFS提供了多達數十種不同的文件系統(tǒng)。虛擬文件系統(tǒng)可以分為邏輯文件系統(tǒng)和設備驅動程序。邏輯文件系統(tǒng)指Linux所支持的文件系統(tǒng),如ext2,fat等,設備驅動程序指為每一種硬件控制器所編寫的設備驅動程序模塊。
·網絡接口(NET)提供了對各種網絡標準的存取和各種網絡硬件的支持。網絡接口可分為網絡協議和網絡驅動程序。網絡協議部分負責實現每一種可能的網絡傳輸協議。網絡設備驅動程序負責與硬件設備通訊,每一種可能的硬件設備都有相應的設備驅動程序。
·進程間通訊(IPC)支持進程間各種通信機制。進程間通信主要用于控制不同進程之間在用戶空間的同步、數據共享和交換。由于不用的用戶進程擁有不同的進程空間,因此進程間的通信要借助于內核的中轉來實現。一般情況下,當一個進程等待硬件操作完成時,會被掛起;當硬件操作完成,進程被恢復執(zhí)行,而協調這個過程的就是進程間的通信機制。
Linux內核子系統(tǒng)的結構如下圖所示,處于中心位置的進程調度,所有其它的子系統(tǒng)都依賴它,因為每個子系統(tǒng)都需要掛起或恢復進程。一般情況下,當一個進程等待硬件操作完成時,它被掛起;當操作真正完成時,進程被恢復執(zhí)行。例如,當一個進程通過網絡發(fā)送一條消息時,網絡接口需要掛起發(fā)送進程,直到硬件成功成功地完成消息的發(fā)送,當消息被成功的發(fā)送出去以后,網絡接口給進程返回一個代碼,表示操作的成功或失敗。其他子系統(tǒng)以相似的理由依賴于進程調度。
各個子系統(tǒng)之間的依賴關系如下:進程調度與內存管理之間的關系:這兩個子系統(tǒng)互相依賴。在多道程序環(huán)境下,程序要運行必須為之創(chuàng)建進程,而創(chuàng)建進程的第一件事情,就是將程序和數據裝入內存。
進程間通信與內存管理的關系:進程間通信子系統(tǒng)要依賴內存管理支持共享內存通信機制,這種機制允許兩個進程除了擁有自己的私有空間,還可以存取共同的內存區(qū)域。
虛擬文件系統(tǒng)與網絡接口之間的關系:虛擬文件系統(tǒng)利用網絡接口支持網絡文件系統(tǒng)(NFS),也利用內存管理支持RAMDISK設備。
內存管理與虛擬文件系統(tǒng)之間的關系:內存管理利用虛擬文件系統(tǒng)支持交換,交換進程(swapd)定期由調度程序調度,這也是內存管理依賴于進程調度的唯一原因。當一個進程存取的內存映射被換出時,內存管理向文件系統(tǒng)發(fā)出請求,同時,掛起當前正在運行的進程。
除了這些依賴關系外,內核中的所有子系統(tǒng)還要依賴于一些共同的資源。這些資源包括所有子系統(tǒng)都用到的過程。例如:分配和釋放內存空間的過程,打印警告或錯誤信息的過程,還有系統(tǒng)的調試例程等等。
內核管理的四個方面
Linux內核負責管理以下四個方面:
·進程:內核決定哪個進程可以使用CPU。
·內存:內核管理所有的內存,為進程分配內存,管理進程間的共享內存以及空閑內存。
·設備驅動程序:作為硬件系統(tǒng)(如磁盤)和進程之間的接口,內核負責操控硬件設備。
·系統(tǒng)調用和支持:進程通常使用系統(tǒng)調用和內核進行通信。
進程管理
進程管理設計進程的啟動、暫停、恢復和終止。啟動和終止比較直觀,但是要解釋清楚進程在執(zhí)行過程中如何使用CPU則相對復雜一些。
在現代操作系統(tǒng)中,很多進程貌似都是同時運行的。例如,你可以同時在桌面打開Web瀏覽器和電子表格應用程序。然而,雖然它們表面上看是同時運行,但實際上這些應用程序背后的進程并不完全是同時運行的。
我們設想一下,在只有一個CPU的計算機系統(tǒng)中,可能會有很多進程可以使用CPU,但是在任何一個特定的時間段內只能有一個進程可以使用CPU。所以實際上是多個進程輪流使用CPU,每個進程使用一段時間后就暫停,然后讓另一個進程使用,依次輪流,時間單位是毫秒級。一個進程讓出CPU使用權給另一個進程稱為上下文切換(context switch)。
進程在其時間段內有足夠的時間完成主要的計算工作(實際上,進程通常在單個時間段內就能完成它的工作)。由于時間段非常短,短到我們根本察覺不到,所以在我們看來,系統(tǒng)是在同時運行多個進程(我們稱之為多任務執(zhí)行)。
內核負責上下文切換。我們來看看下面的場景,以便理解它的工作原理。
(1)CPU為每個進程計時,到時即停止進程,并切換至內核模式,由內核接管CPU控制權。(2)內核記錄下當前CPU和內存的狀態(tài)信息,這些信息在恢復被停止的進程時需要用到。(3)內核執(zhí)行上一個時間段內的任務(如從輸入輸出設備獲得數據,磁盤讀寫操作等)。(4)內核準備執(zhí)行下一個進程,從準備就緒的進程中選擇一個執(zhí)行。(5)內核為新進程準備CPU和內存。(6)內核將新進程執(zhí)行的時間段通知CPU。(7)內核將CPU切換至用戶模式,將CPU控制權交給新進程。
上下文切換回答了一個十分重要的問題,即內核是在什么時候運行的。答案就是:內核是在上下文切換時的時間段間隙中運行的。
在多CPU系統(tǒng)中,情況要稍微復雜一些。如果新進程將在另一個CPU上運行,內核就不需要讓出當前CPU的使用權。不過為了將所有CPU的使用效率最大化,內核會使用一些其他的方式來獲取CPU控制權。
內存管理
內核在上下文切換過程中管理內存,這是一項十分復雜的工作,因為內核要保證以下所有條件:
(1)內核需要自己的專有內存空間,其他的用戶進程無法訪問;(2)每個用戶進程有自己的專有內存空間;(3)一個進程不能訪問另一個進程的專有內存空間;(4)用戶進程之間可以共享內存;(5)用戶進程的某些內存空間可以是只讀的;(6)通過使用磁盤交換,系統(tǒng)可以使用比實際內存容量更多的內存空間。
新型的CPU提供MMU(Memory Management Unit,內存管理單元),MMU使用了一種叫作虛擬內存的內存訪問機制,即進程不是直接訪問內存的實際物理地址,而是通過內核使得進程看起來可以使用整個系統(tǒng)的內存。當進程訪問內存的時候,MMU截獲訪問請求,然后通過內存映射表(或稱為內存頁面表,page table)將要訪問的內存地址轉換為實際的物理地址。內核需要初始化、維護和更新這個地址映射表。例如,在上下文切換時,內核將內存映射表從被移出進程轉給被移入進程使用。
設備驅動程序和設備管理
對于設備來說,內核的角色比較簡單。通常設備只能在內核模式中被訪問(例如用戶進程請求內核關閉系統(tǒng)電源),因為設備訪問不當有可能會讓系統(tǒng)崩潰。另一個原因是不同設備之間沒有一個統(tǒng)一的編程接口,即使同類設備也是如此(比如兩個不同的網卡)。所以設備驅動程序傳統(tǒng)意義上來說是內核的一部分,它們盡可能為用戶進程提供統(tǒng)一的接口,以簡化開發(fā)人員的工作。
系統(tǒng)調用和系統(tǒng)支持
內核還對用戶進程提供其他功能。例如,系統(tǒng)調用(system call或syscall)為進程執(zhí)行一些它們不擅長或無法完成的工作。打開、讀取和寫文件這些操作都涉及系統(tǒng)調用。
fork()和exec()這兩個系統(tǒng)調用對于我們了解進程如何啟動很重要。
(1)fork():當進程調用fork()時,內核創(chuàng)建一個和該進程幾乎一模一樣的副本。(2)exec():當進程調用exec(program)時,內核啟動program來替換當前的進程。
除了init以外,Linux中的所有用戶進程都是通過fork()來啟動的。除了創(chuàng)建現有進程的副本以外,大多數情況下你還可以使用exec()來啟動新的進程。一個簡單的例子是你在命令行運行l(wèi)s命令來顯示目錄內容。當你在終端窗口中輸入ls時,終端窗口中的shell調用fork()創(chuàng)建一個shell的副本,然后該副本調用exec(ls)來運行l(wèi)s。
除了傳統(tǒng)的系統(tǒng)調用,內核還為用戶進程提供其他很多功能,最為常見的是虛擬設備。虛擬設備對于用戶進程而言是物理設備,但其實它們都是通過軟件實現的。因此從技術角度來說,它們并不需要存在于內核中,但是實際上它們很多都存在于內核中。例如:內核的隨機數生成器(/dev/random)這樣的虛擬設備,如果由用戶進程來實現,難度要大很多。
用戶空間
前面提到過,內核分配給用戶進程的內存我們稱之為用戶空間。因為一個進程簡單來說就是內存中的一個狀態(tài)。(用戶空間也可以指所有用戶進程占用的所有內存)
Linux中大部分的操作都發(fā)生在用戶空間中。雖然從內核的角度來說所有進程都是一樣的,但是實際上它們執(zhí)行的是不同的任務。相對于系統(tǒng)組件,用戶進程位于一個基礎服務層中。底層的基礎服務層中提供了上層應用程序所需的工具服務(也稱為中間件),比如郵件、打印和數據庫服務。頂層組件則可專注于完成用戶交互和復雜的功能。當然,組件之間也是可以相互調用的。
雖然這里提到底層、頂層、中間層等概念,但實際上它們在用戶空間里并沒有明顯的界限。其實很多用戶空間的組件也比較難分類,比如Web服務器和數據庫,你可以把它們歸為上層組件,也可以歸為中間層組件。
另外,從技術上來說,用戶進程還是需要通過使用系統(tǒng)調用打開設備的方式來訪問虛擬設備,所以進程總是避免不了要和系統(tǒng)調用打交道。
-
Linux
+關注
關注
87文章
11208瀏覽量
208721
原文標題:如何學習Linux?一位老司機總結每個Linux開發(fā)者都應該知道的一些知識
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論