l 帶著課題
分析任何代碼都要都要帶著課題,如果只是走馬觀花很難有具體的收獲。“課題”可大、可小,大課題有大收獲閱讀分析時間也比較長。比如“搞清楚Linux是如何收發(fā)數(shù)據(jù)的”,“Linux是如何分配內(nèi)存的”,這些都是比較大的題目;再比如“IP數(shù)據(jù)包如何被重組的”這就是比較具體的問題,屬于“小課題”。
“大課題”一般是由多個彼此關(guān)聯(lián)的“小課題”組成的,所以最終我們還是會去在內(nèi)核中挨個尋找某個具體問題的具體答案,然后再回頭來看整個問題。“小課題”是指某個具體問題,我們通常需要先找到一個切入點,然后順藤摸瓜理出一個頭緒來。
源代碼的分析工具比較簡單,一個編輯器(語法加亮)一個快捷的查找工具(比如grep)就可以開始干活了。通過查找工具找到切入點,然后分析代碼的邏輯。如果代碼量比較大,一般我們會選擇一個IDE工具或者專門的代碼閱讀工具(Source Insight、Understand)來分析、閱讀代碼。
l 觀察數(shù)據(jù)流向
成熟的代碼通常都很復雜,考慮的事情也比較全面,所以一個函數(shù)可能有幾十行代碼。閱讀代碼的時候我們要把握數(shù)據(jù)流向,比如我們知道函數(shù)的返回值是我們關(guān)注的數(shù)據(jù),那么我們觀察它在哪里執(zhí)行了賦值語句,這樣就可以理出個主脈絡來。
l 分析總結(jié)
我喜歡用兩幅圖來表示分析代碼之后的收獲,函數(shù)調(diào)用關(guān)系圖、數(shù)據(jù)結(jié)構(gòu)圖。調(diào)用關(guān)系可以從宏觀上告訴我們整個過程分成哪些步驟,步驟里面分為哪些子步驟;數(shù)據(jù)結(jié)構(gòu)輔助說明了這些過程涉及到的數(shù)據(jù)操作。
l分析實戰(zhàn)
代碼分析
“萬物皆文件(everything is a file)”,Unix/Linux的一條著名的設計哲學。在Unix/Linux中很多硬件設備、進程運行信息、系統(tǒng)狀態(tài)都被映射成文件系統(tǒng)中的某個文件,這種設計極大的簡化了系統(tǒng)模型。
在Linux中每個文件都由“struct file”和“一個int類型的變量——file descriptor(文件描述符)”組成。下面通過分析Kernel代碼來剖析file descriptor的分配過程。
我們是要探究file descriptor的分配過程,問題非常明確,切入點也比較好找——什么時候執(zhí)行fd的分配?答案是執(zhí)行`open`函數(shù)的時候。所以我們通過查找工具定位到`open`系統(tǒng)調(diào)用的代碼(fs/open.c)
Linux的代碼非常清晰,open函數(shù)實際上調(diào)用的是do_sys_open。“打開文件”的過程分為:
1.分配文件描述符(fd);
2.分配struct file;
3.綁定fd和structfile。
Linux中資源分配的對象是進程,用struct task表示進程的數(shù)據(jù)結(jié)構(gòu)。“文件句柄”(內(nèi)核中指向某個打開文件的指針數(shù)據(jù)結(jié)構(gòu)是struct file)屬于資源申請,所以按道理說Linux的struct task中應該定義一個struct file類型的數(shù)組,文件描述符則表示struct file數(shù)組中的索引。
實際上Linux2.1之前就是這么干的,但是這種實現(xiàn)方式有一個很明顯的缺陷——files的大小是受限的,2.1之前它是一個固定的值——256。如果要突破限制那就不能使用“固定大小數(shù)組”的數(shù)字定義files,所以在后續(xù)的版本中就把“文件句柄”拆分成立兩種內(nèi)核資源——文件描述符(fd)、文件對象(struct file)。(后面會放上我們分析后的數(shù)據(jù)結(jié)構(gòu)圖——也是Linux正在用的數(shù)據(jù)結(jié)構(gòu))
回到我們的代碼,分配文件描述符的代碼是get_unused_fd_flags,我們跟蹤下去發(fā)現(xiàn)它其實是__alloc_fd函數(shù)的封裝,直接看__alloc_fd。
直接讀這么一大段代碼很難理清楚頭緒,這里有個技巧推薦給大家。直接看它的返回值,它的返回值就是文件描述符,所以我們只要注意在哪里給它賦值就能理出關(guān)鍵頭緒。
__alloc_fd函數(shù)的start,end參數(shù)是指文件描述符的**可用**范圍,get_unused_fd_flags在傳遞start參數(shù)的時候是0,所以不設置下標范圍。Linux用一個位圖記錄fd的分配狀態(tài),需要注意的是next_fd并不能直接作為fd返回,它僅僅是標識“未使用的fd中最小值”,這是為了防止位圖中“空隙”(位圖中1、2、4、5、6都是空閑的,3已經(jīng)被使用了,我們搜索未使用fd的時候很顯然應該從1開始搜索。所以一定要保存這個“下標”)。
fdtable是內(nèi)核中用來表示文件描述符表格的數(shù)據(jù)結(jié)構(gòu),表示fd分配狀態(tài)的位圖就是它的成員變量(full_fds_bits),max_fds記錄的是當前表格可用的最大文件描述符,這個值是可以通過expand_files增加的(如果你打開`expand_files`會發(fā)現(xiàn)fd最大值是不能超過`sysctl_nr_open`的,這個就是fs.nr_open的值)。
上面的代碼只是尋找可用fd而沒有修改位圖,所以代碼最后通過__set_open_fd來修改位圖。
l總結(jié)經(jīng)驗
“一圖勝千言”,代碼分析是一件非常難“表達出來”的事情,如果是像上面的文字估計沒有多少人會有興趣看。所以我一般分析完代碼后畫兩張圖,一張表示數(shù)據(jù)結(jié)構(gòu)關(guān)系的圖,一張表示函數(shù)調(diào)用關(guān)系的圖。
do_sys_open所做的都是為了最后執(zhí)行fd_install(成功打開文件),而fd_install可以被簡化為一個簡單的賦值語句(圖中的那句賦值語句)。所以前面的get_unused_fd_flags其實是為了返回合適的fd、do_filp_open則是為struct file分配一塊內(nèi)存空間。
結(jié)合數(shù)據(jù)結(jié)構(gòu)圖來看
get_unused_fd_flags的主要操作對象其實就是struct files_struct。
審核編輯:劉清
-
Linux系統(tǒng)
+關(guān)注
關(guān)注
4文章
595瀏覽量
27442 -
UNIX
+關(guān)注
關(guān)注
0文章
296瀏覽量
41507 -
sys
+關(guān)注
關(guān)注
0文章
9瀏覽量
9170 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21675
原文標題:邢森: 淺析Linux kernel的閱讀方法
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論