1. 程式人生 > >對linux核心學習的一點感受

對linux核心學習的一點感受

跟著這門課不知不覺已經學了8個星期了,對Linux核心有了一個基本的認識,可以說是入門了。既然是課程總結,我大概描述一下這個課程,供對Linux核心感興趣的同學參考。

這門課沒講什麼

  1. 在學習作業系統的時候,我們知道了作業系統將CPU抽象為程序,將記憶體抽象為虛擬記憶體,學習了程序的排程演算法,記憶體頁面的置換演算法,這門課並沒有關注這些演算法;
  2. 作業系統的主要功能就是為使用者遮蔽硬體的操作細節,幫助使用者管理計算機系統的各種資源,同步機制是我們處理併發任務和進行資源管理的重要手段,關於原子操作訊號量自旋鎖等內容該課程中沒有講解;
  3. 在作業系統原理課程中沒有著重講解的各種裝置驅動程式
    實際上佔據了Linux核心程式碼的大部分比例,這門課並沒有這部分內容;
  4. 沒有講解檔案系統的結構與實現,VFS等

這門課講了什麼

  1. 對於要研究Linux核心的人來說,X86組合語言是你必須要面對的第一關,因為作業系統需要大量對暫存器的操作,這是體系結構相關的操作,所以必須用匯編語言來解決,這門課在一開始就講解了X86彙編,並在後面的課程中不斷鞏固,這點對於閱讀核心原始碼非常有用。
  2. 該課程用一個簡單的演示核心myKernel來說明Linux是如何啟動的,包括一個程序是怎樣描述的(PCB資訊),0號程序(idle)的建立與演化,1號程序init的建立與載入,2號程序kthreadd的建立等等。這可以使我們從高處對Linux核心有個大概的認識,並且課中手把手的原始碼閱讀
    可以讓人對減少對結構複雜的核心程式碼的恐懼。
  3. 我們日常使用核心,其實大部分功能都是使用它的系統呼叫,比如從建立一個新的程序fork,裝載程式execve,到輸入輸出,時間查詢等等。因此我們研究核心,很大一部分都是在研究如何實現這些系統呼叫。這門課在8週中花了兩週時間來講解系統呼叫在核心中是如何進行的。如果把程序建立可執行程式的裝載也算作系統呼叫的講解的話,那實際上佔了課程的一半。所以課程的設定正體現了這些系統呼叫在核心構成中的重要性。課中提供了一個試驗環境MenuOS,該系統實現了一個命令列菜單系統,我們只需要新增我們希望執行的功能函式到選單就OK了,同時利用Qemu和gdb,我們跟蹤了各種系統呼叫的執行過程。
  4. 雖然這門課沒有將具體的排程演算法,比如Linux核心中著名的完全公平佇列CFS,但對於程序排程來說,除了排程演算法,還有兩個重要問題,那就是程序的排程時機與切換過程,該課程花了一節課來講解schedule()函式的實現。
  5. 我們不僅需要學習Linux核心的相關知識,更需要學習正確的人生觀和世界觀,這門課的精髓在於,不僅教會你如何分析Linux核心,更教你做事的方法論:“天下難事必做於易,天下大事必做於細”,對於程式碼量龐大無從下手的核心,我們從小處入手,步步為營,最終掌握全域性。

其實上面的兩部分內容已經包含了我對核心的理解,我認為核心中的重要部分基本上包含在了上面兩部分中,這也是下一步我繼續研究核心的努力方向。

我對Linux的認識的一點補充

前面已經講了很多,我對核心的認識和了解也基本上包含在了上面兩個部分,最後我還是談一下系統呼叫
作業系統的主要功能就是為使用者遮蔽硬體的操作細節,幫助使用者管理計算機系統的各種資源,這樣一來作業系統本身就需要擁有一定的特權來區別於使用者應用程式,這就是核心態使用者態的由來。當然這個機制需要CPU的支援,X86系列處理器定義了R0 - R4四個特權級別,Linux中使用了其中的R0作為核心態,R4作為使用者態。
核心態與使用者態使用了不同的地址空間,他們通過系統呼叫的方式進行通訊。在32位下程序空間是2^32=4GByte,其中0-3G是使用者空間,3G-4G是核心空間。首先,這個4G空間對應的是虛擬記憶體空間的概念,也就是說這個4G並不在實體記憶體中,而是通過頁面置換不斷部分進入或移出實體記憶體(缺頁異常機制),而這個4G中,核心空間的1G空間是所有的程序所共享的,而其餘3G是互不可見的。這樣的地址設定加上中斷機制,對於一個程序來說,就相當於獨佔了這個計算機。
RobertLove的書裡說,CPU在任一時刻都處於下面三種狀態中的一種:
1、核心態,運行於程序上下文,核心代表程序運行於核心空間;
2、核心態,運行於中斷上下文,核心代表硬體運行於核心空間;
3、使用者態,運行於使用者空間。
孟老師在課上將中斷上下文的切換和程序上下文的切換稱為作業系統的“兩把寶劍”,核心態正是運行於這兩種環境中執行對中斷、異常和系統呼叫的處理。我之前已經提到,系統呼叫是這門課的重中之重,從一開始,老師就提到“計算機工作的三個法寶”:儲存程式計算機、函式呼叫堆疊和中斷機制,系統呼叫也可以算是一種特殊的中斷機制。我們日常使用系統呼叫都是通過呼叫封裝好的C標準庫函式來間接呼叫系統呼叫服務例程。這正是老師總結的“系統呼叫的三層皮”:API xyz –> 中斷向量system_call –> 中斷服務程式sys_xyz。這樣就進入核心態,運行於程序上下文。

廣告

我認為這門課程值得一聽,有如下三個原因:
首先這門課的實操性質很強,上完一遍你一定會減少對龐大核心程式碼的恐懼感;
其次孟老師的經驗豐富,講解非常生動有趣,我已經決定繼續跟孟老師的另外一門課了;
其次這門課聽起來很方便,下載在手機上,很適合在碎片時間進行學習。

作業列表

這門課程的作業是通過撰寫部落格來記錄自己對課程的理解和實驗報告,下面就是我這8周的作業:

從mykernel來分析linux系統的啟動過程
教你自己寫簡化的PCB結構,建立0號程序my_process,並利用其進行最簡單的排程,進行程序的切換。

利用gdb分析從start_kernel到init啟動的過程
利用MenuOS來跟蹤並分析Linux Kernel啟動時的最後一步,即從init/start_kernel()開始分析第一個使用者態程序init是如何啟動的這裡給出一張呼叫關係圖,細節可以看博文。
call_graph

使用庫函式API和C程式碼中嵌入彙編程式碼兩種方式使用同一個系統呼叫
我在這節用了4個系統呼叫“開啟檔案open(系統呼叫號5),關閉檔案close(系統呼叫號6),讀檔案read(系統呼叫號3),以及寫檔案write(系統呼叫號4)”,分別通過C庫和嵌入式彙編直接呼叫int 0x80中斷來實現了一個檔案拷貝的操作。

分析system_call中斷處理過程
我們深入系統呼叫的關鍵程式碼片段system_call,來理解一個系統呼叫內部是怎麼完成的,總結的流程圖如下,細節參見博文。
sys_call

分析Linux核心建立一個新程序的過程
這一節,我們深入fork的系統呼叫服務例程sys_clone,來觀察它的實現細節。一個fork的執行過程如下所示:
libc fork() -> system_call -> sys_clone() -> do_fork() -> copy_process() {dup_task_struct; copy_thread } -> wake_up_new_task() -> ret_from_fork

Linux核心如何裝載和啟動一個可執行程式
這節我們分析了目標檔案的格式,動態連結可執行程式的編譯連結方法,如何裝載和啟動一個可執行程式即sys_execve的實現細節。

理解程序排程時機跟蹤分析程序排程與程序切換的過程
這一節我們分析了程序的排程時機和切換過程,主要分析了schedule()函式的實現細節。