linux核心系列(2) linux核心鍊錶

2021-07-31 00:13:16 字數 2638 閱讀 7533

因為高超的設計理念,linux核心中的鍊錶被很多人津津樂道。實際上,鍊錶本身只是核心提供的一組結構體、巨集定義和函式的集合,與linux核心本身沒有直接關係。核心鍊錶的設計思路已經在另一篇部落格中寫了,有興趣的同學可以去看一下,這裡是鏈結。

今天,我們來看一下真實核心鍊錶的基本操作過程。我們的目標是做乙個核心模組兒,在模組兒內使用核心提供的鍊錶,完成構建節點,插入節點,排序節點,輸出節點等一系列操作,最後在退出時釋放鍊錶節點。

在介紹業務之前,先來看一下核心鍊錶的組成

struct list_head 

;

可見,核心提供的是乙個雙向鍊錶,每個節點裡面只有兩個指向自身型別的指標。

為了模擬場景,我們假設有5個名為cat的結構體是我們的鍊錶節點,其基本定義如下:

struct cat

;

為了形成鍊錶,我們將核心鍊錶節點插入到業務結構體中

struct cat

;

我們還是來先看一下**,然後再解釋

#include #include #include #include module_license("gpl");

//業務結構體

struct cat

;struct cat cats = , ,

, , };

static list_head(cat_list); //定義乙個頭結點

static int insert_five_cats(void)

static int sort_five_cats(void)

} }return 0;

}static int print_five_cats(void)

return 0;

}static int lkp_init(void)

static void lkp_exit(void)

module_init(lkp_init);

module_exit(lkp_exit);

首先,為了要使用核心鍊錶,我們必須包含標頭檔案。

#include
list_head巨集是在list.h中定義的,用於定義核心鍊錶的頭節點,加乙個static表示該定義只在當前檔案有效

static list_head(cat_list);
這個巨集的具體定義如下

#define list_head_init(name) 

#define list_head(name) \

struct list_head name = list_head_init(name)

把它展開一下,就可以知道是什麼意思了。

然後,在插入函式中我們遇到了第乙個核心鍊錶函式

list_add(&(cats[i].list), &cat_list); //插入節點
這個函式的作用是將第乙個引數(新的業務節點中的鍊錶節點),插入到第二個引數(已經在鍊錶中的節點)後面。

在講排序函式之前,我們有必要來看一下核心鍊錶的記憶體分布,這樣有利於理解這個函式的執行。直接上圖

圖中,橘色的節點為煉表頭結點,可見,鍊錶是乙個帶頭結點的雙向迴圈鍊錶,在這裡我們用氣泡排序法來完成排序動作,已經排好的節點放在最右邊,這個指標有rig指標指示,而遍歷用的指標用lef指示。

在操作過程中,用到了另乙個巨集

list_entry(lef, struct cat, list)
這個巨集是核心鍊錶經典的巨集之一,它的作用是將乙個鍊錶節點位址轉換為業務節點位址,具體實現是通過另乙個巨集container_of實現的,這個比較經典,我們展開講一下

#define container_of(ptr, type, member) ( )

#define list_entary(ptr, type, member) \

container_of(ptr, type, member)

這個巨集比較複雜,首先,來看一下它的引數:第乙個是乙個指標,在我們的函式中,它就是內嵌的節點指標;第二個是乙個型別名,在這裡,它是我們的業務節點型別;第三個是鍊錶節點在業務節點中的名字。然後,這個巨集它由兩句組成第一句是定義了乙個名為__mptr的指標並將其賦值為巨集引數ptr,第二句是將上面定義的指標減去了乙個引數然後返回為業務型別。先說第一句,分析後可知道定義的型別是鍊錶節點型別,也就是ptr的型別,即__mptr指向了業務節點中的鍊錶節點。後面一句,是將這個位址減去了鍊錶節點中指標節點相對於業務節點首位址的偏移量,因為是減去,所以實際上是向前移了,則減去後的值正好是業務節點的首位址。至於為什麼要用__mptr作為中間變數轉換一下,目前還沒有研究明白,自己揣測可能是因為習慣性寫法,先用臨時變數接收一下,防止改變prt的值。有明白的大神還請多多賜教。後面轉char*比較好理解,就是因為offset返回的是偏移的位元組數,所以要按照位元組數來減。

最後兩句是註冊模組兒用的,和前面一節的用法完全相同。

最最後,用上一節的方法,講這個模組兒插入核心就可以檢視程式的執行結果了。

2 Linux核心目錄概述

arch目錄表示核心所支援的處理器架構,如經典的arm x86,在編譯核心時要指定arch arm 此資料夾包含塊裝置驅動程式的 該目錄用於實現塊裝置的基本框架和塊裝置的i o排程演算法。塊裝置是以資料塊方式接收和傳送的資料的裝置。資料塊都是一塊一塊的資料而不是持續的資料流。這個資料夾包含許多加密演...

android 核心系列

編譯 1,需要jre1.6,64bit的機器。2,錯誤 this attribute must be localized 提示了錯誤 this attribute must be localized 這種問題一般情況是因為在res xml資料夾下的中 或者在res layout下的檔案中出現了沒有多...

Linux核心系列之Block塊層(一)

block塊層入口函式為 genhd device init 先對該函式開始分析 函式實現原始碼 static int init genhd device init void kobj map init struct kobj map probes 255 struct mutex lock 作用 ...