Linux內核學習總結
@2015.05
幸福來得很突然,這門課就快結束了……
喜歡孟老師的課,是從這個課程《軟件工程(C編碼實踐篇)》開始的,那個是我在網易雲課堂第一個堅持學完的課程。
自己一直在Windows下做開發工作,出於個人興趣,去年就想系統地學習下Linux的相關知識。從《軟件工程(C編碼實踐篇)》課程開始,就跟著孟老師逐步熟悉Linux下的一些開發實踐;那時候就感覺孟老師的講課,深入淺出,循序漸進,實踐性很強,只要跟著堅持學下去,總是收穫不少;然後又看到孟老師開設的這門課《Linux內核分析》,就毫不猶豫的選擇跟上,並開啟了“奮鬥模式”。
是時候,總結下這段時間的堅持了,也給同樣對Linux內核有興趣的你一個指南。
在這門課的學習過程中,按照老師的要求,每次課後都寫一篇博文,這是一個很好的學習方式。每當寫這些文章的時候,總是要多看幾遍視頻,再查查相關的資料,才能勉強湊成一個完整的文檔;同時也把自己學到的東西更好的分享出去,吸引更多的人過來學習Linux內核,一起討論。現把這一系列博文羅列如下,歡迎大家批閱指正。
1. 圖解分析彙編代碼以理解計算機是如何工作的
馮諾依曼體系結構的計算機,又叫存儲程序計算機,從硬件的角度來看,其工作模型是CPU依次讀取內存中的指令來完成工作。但它具體是如何完成程序員編寫的非線性執行的程序呢?本次課的實驗,以一段彙編代碼為例,詳細介紹了CPU計算模塊、寄存器和內存是如何配合工作的!
2. 基於mykernel的一個簡單的時間片輪轉多道程序內核代碼分析
mykernel是由老師建立的一個用於開放您自己的操作系統的內核平臺,它基於Linux Kernel 3.9.4 source code。通過本講的學習和實驗,我們知道操作系統的核心功能就是:進程調度和中斷機制,通過與硬件的配合實現多任務處理,再加上上層應用軟件的支持,最終變成可以使用戶可以很容易操作的計算機系統。
3. 使用gdb跟蹤Linux內核啟動過程
start_kernel()是內核的彙編與C語言的交接點,在該函數以前,內核的代碼都是用匯編寫的,完成一些最基本的初始化與環境設置工作。start_kernel就像是c代碼中的main函數。不管你關注Linux的內核模塊,總是離不開start_kernel函數的,因為大部分模塊的初始化工作都是在start_kernel中完成的。按照這節課的實驗步驟,我們可以跟蹤Linux內核的啟動過程。
4. 使用庫函數API和C代碼中嵌入彙編代碼兩種方式使用同一個系統調用
即便是最簡單的程序,也難免要用到諸如輸入、輸出以及退出等操作,而要進行這些操作則需要調用操作系統所提供的服務,也就是系統調用。除非你的程序只完成加減乘除等數學運算,否則將很難避免使用系統調用。在 Linux 平臺下有兩種方式來使用系統調用:利用封裝後的 C 庫(libc)或者通過彙編直接調用。這篇文章從示例出發,介紹了系統調用的概念,以及如何使用系統調用。
5. 分析system_call中斷處裡過程
通過gdb我們可以給系統調用內核處里程序如sys_write, sys_time設置斷點,並讓程序停在斷點處,進行斷點跟蹤系統調用處裡過程。由於system_call是完全用匯編寫就一個的函數,雖然我們也可以在system_call處設置斷點,但卻無法讓系統停在system_call處,所以也無法通過單步跟蹤學習其處裡流程。但system_call是所有系統調用的入口,也是程序由用戶態轉入內核態執行時無法越過的一個函數,其重要性不言而喻,所以我們跟隨老師簡化的彙編代碼以及源代碼學習其主要的流程。
6. 初學Linux進程的描述和進程的創建
為了管理進程,內核必須對每個進程進行清晰的描述,進程描述符提供了內核所需瞭解的進程信息。進程描述符task_struct的源碼鏈接:http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235。在Linux應用程序的開發中,可以通過fork、vfork和clone等API來創建一個子進程,它們在Linux內核中對應的系統調用分別為sys_fork、sys_vfork和sys_clone函數,而這些函數最終都會調用do_fork完成子進程的創建。do_fork主要是複製了父進程的task_struct,然後修改必要的信息,從而得到子進程的task_struct。
7. 初學《Linux內核如何裝載和啟動一個可執行程序》
Linux系統可以通過execve API啟動一個新進程,該API又呼叫sys_execve系統調用,負責將新的程序代碼和數據替換到新的進程中,打開可執行 文件,載入依賴的庫文件,申請新的內存空間,最後執行 start_thread(regs, elf_entry, bprm->p) ,設置 new_ip, new_sp ,完成新進程的代碼和數據替換,然後返回,接下來就是執行新的進程代碼了。
8. 初學Linux中進程調度與進程切換過程
Linux系統的一般執行過程,最一般的情況是:正在運行的用戶態進程X切換到運行用戶態進程Y的過程要經過以下步驟
1). 正在運行的用戶態進程X
2). 發生中斷:save cs:eip/esp/eflags(current) to kernel stack, then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack).
3). SAVE_ALL //保存現場,這裡是已經進入內核中斷處裡過程
4). 中斷處理過程中或中斷返回前調用了schedule(),其中的switch_to做了關鍵的進程上下文切換
5). 標號1之後開始運行用戶態進程Y(這裡Y曾經通過以上步驟被切換出去過因此可以從標號1繼續執行)
6). restore_all //恢復現場
7). iret - pop cs:eip/ss:esp/eflags from kernel stack
8). 繼續運行用戶態進程Y
總結
通過這門課的學習,加深了我對操作系統理論的理解,知道了Linux系統是如何工作的,如何通過代碼閱讀、調試去跟蹤驗證Linux系統的運行機制。
Linux作為一個極其成功的操作系統,其內核紛繁複雜、博大精深,無疑是很難學習的,雖然在課程中孟老師化繁為簡、抽絲剝繭,我也很努力地學完了本課程所有的視頻,跟著老師的指導完成了全部的練習和測驗,但也只感覺我是剛剛站在這一知識寶庫的大門前,大門剛剛露了一個縫隙,要學習的東西真是太多太多了!
個人覺得,在這門課程中,重要的是不是學習到了多少內核代碼(雖然它也很重要);重要的是學習方法,即從何處著手學習Linux內核,課程中給了我們很多這方面的提示,例如:如何調試內核,如何看懂內核中的彙編代碼,如何分析系統調用,等等。
總之,作為入門,這門課程起到了很好的引導作用;師傅領進門,修行靠自身。所以於我來說,這門課雖結束了,但Linux內核的學習才剛剛開始……