“tcp丟包分析”系列文章代碼來自謝寶友老師,由西郵陳莉君教授研一學生進行解析,本文由戴君毅整理,梁金榮編輯,賀東昇校對。

最初開發 /proc 文件系統是爲了提供有關係統中進程的信息。但是這個文件系統非常有用, /proc 文件系統包含了一些目錄(用作組織信息的方式)和虛擬文件。虛擬文件可以向用戶呈現內核中的一些信息,也可以用作一種從用戶空間向內核發送信息的手段。

/proc 文件系統可以爲用戶提供很多信息, 在左邊是一系列數字編號,每個實際上都是一個目錄,表示系統中的一個進程。由於在Linux中創建的第一個進程是 init 進程,因此它的 process-id 爲 1。

右邊的目錄包含特定信息,比如 cpuinfo 包含了CPU的信息, modules 包含了內核模塊的信息。

爲了解決一些實際問題,我們需要在 /proc 下創建條目捕獲信息,使用文件系統通用方法肯定是不行的,需要使用相關API編寫內核模塊來實現。

在做謝寶友老師寫的“TCP丟包分析”實驗裏,首先就會在 /proc 下創建條目,較爲簡單,先來看 initexit

框架還是比較清晰的,需要深入源碼來感受一下,第一部分代碼:

struct proc dir entry *pe;

結構 proc_dir_entry 定義在 <fs/proc/internal.h> 下,可以稱爲一個 pde ,在創建一個文件或目錄時就會創建一個 pde 來管理它們。而在打開它們的時候,則會創建一個 proc_inode 結構:

可以使用 PROC_I 宏,也就是我們熟悉的 container_of ,從虛擬文件系統的 inode 得到 proc_inode ,進而得到 pde

回到 proc_dir_entry 結構,很多信息從字段名字就可以看出, pde 需要指向創建自己的父 pde 結構, subdir 的組織方式是紅黑樹,還需要我們實現操作集以及一些引用計數和命名規則等等。

有意思的是,除了操作集之外還有一個 proc_write_t ,對於一些功能比較簡單的 proc 文件,我們只要實現這個函數即可,而不用設置 inode_operations 結構,在註冊 proc 文件的時候,會自動爲 proc_fops 設置一個缺省的 file_operations 結構。

此時,我們可以想象以下模型:

第二部分代碼是:

proc_mkdir("mooc", NULL);

proc_mkdir("mooc/net", NULL);

remove proc entry("mooc/net", NULL);

remove proc entry("mooc", NULL);

易知其功能是在 /proc 下創建和刪除條目 mooc/net ,以創建操作爲例,看下內核代碼如何實現的:

這裏邏輯很簡單, proc_mkdir 實際上是 proc_mkdir_data 默認了權限爲 S_IRUGO|S_IXUGO ,再調用 __proc_create 初始化一個局部 pde ,如果成功則初始化操作集,並調用 proc_register 註冊這個 pde 到父 pde 下並返回。

__proc_create 調用 kmem_cache_zalloccache 中獲取空間給 pde ,並且對條目名稱進行檢查。如果成功,則對名稱、模式等屬性賦值,設置引用計數並初始化鎖。

__proc_register 接收兩個參數,一個父親 pde ,一個當前 pde ,目的是把當前 pde 掛到父親名下,前面提到 subdir 的組織形式是紅黑樹,那麼肯定涉及相關代碼,來看:

首先判斷當前 pdeid 是否越界,如果沒有打開子目錄鎖,把當前 pdeparent 字段指向父親 pde ,並嘗試在紅黑樹中插入子目錄,成功後重新上鎖並返回當前 pde

紅黑樹的插入操作篇幅所限不再敘述。

下面看第三部分代碼:

proc_create 內部也是調用 proc_create_data ,但還需自行指定權限以及操作集回調,用於創建一個 proc 文件,在3.10內核中取代 create_proc_entry 這個舊的接口。

回到實驗代碼,我們爲加入的條目編寫操作集接口。

一般地,內核通過在 procfs 文件系統下建立文件來向用戶空間提供輸出信息,用戶空間可以通過任何文本閱讀應用查看該文件信息,但是 procfs 有一個缺陷,如果輸出內容大於1個內存頁,需要多次讀,因此處理起來很難,另外,如果輸出太大,速度比較慢,有時會出現一些意想不到的情況, AlexanderViro 實現了一套新的功能,使得內核輸出大文件信息更容易,它們叫做 seq_file ,所以在使用它們的操作集時需要包含 seq_file.h 頭文件。

Drop_packet_open 實際上是調用了 single_open

爲什麼這麼做?內核文檔給出了相關描述:

https://www.kernel.org/doc/Documentation/filesystems/seq_file.txt

你可能發現,內核文檔裏顯示的是 seq_open ,而實驗裏是 single_open ,它們有什麼區別呢?實際上內核文檔的最後給出了答案:

謝寶友老師的實驗中運用 seq_file 的極簡版本(extra-simple version),只需定義一個 show() 函數。完整的情況我們還需要實現 start() , next() 等迭代器來對 seq_file 進行操作。極簡版本中, open 方法需要調用 single_open ,對應的, release 方法調用 single_release

推薦閱讀https://www.ibm.com/developerworks/cn/linux/l-proc.html

相關文章