linux內(nèi)核源碼 -- list鏈表

  • linux kernel中的list估計(jì)已經(jīng)被各位前輩們寫爛了,但是我還是想在這里記錄一下;
  • linux kernel里的很多數(shù)據(jù)結(jié)構(gòu)都很經(jīng)典, list鏈表就是其中之一
  • 本篇要介紹的內(nèi)容:
    1. list的定義
    2. list提供的操作方法
    3. 注意事項(xiàng)
    4. 使用實(shí)例

List 所在文件:
  • List的所有操作可以在 include/linux/list.h找到;
  • List head的定義可以在 include/linux/types.h找到;
定義
  • 實(shí)際上這就是一個(gè)雙向循環(huán)鏈表, 且有一個(gè)頭指針
  • list head的定義:
struct list_head {
    struct list_head *next, *prev;
};
  • 這個(gè)定義中只有前向和后向指針,沒任何的數(shù)據(jù)部分, 那我們基本上就知道了, 它不是被單獨(dú)使用的,而是把它嵌入到用戶定義的struct中, 將用戶定義的數(shù)據(jù)結(jié)構(gòu)串起來(lái),作成list;
  • 思想很巧妙, 對(duì)用戶定義的數(shù)據(jù)結(jié)構(gòu)侵入性很小, 實(shí)現(xiàn)了c++中std::List模板的功能;
  • 雖然這個(gè)定義是叫head, 但其實(shí)嵌入到用戶定義的數(shù)據(jù)結(jié)構(gòu)中的也是這個(gè).
初始化
// 靜態(tài)初始化
#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
    struct list_head name = LIST_HEAD_INIT(name)

// 調(diào)用INIT_LIST_HEAD來(lái)初始化, **WRITE_ONCE**這個(gè)后面我們專門介紹
static inline void INIT_LIST_HEAD(struct list_head *list)
{
    WRITE_ONCE(list->next, list);
    list->prev = list;
}

插入操作
  • 將一個(gè)元素插入到兩個(gè)元素之間, 即將 new插入到prevnext中, 這個(gè)函數(shù)是下面在頭部和尾部插入的實(shí)現(xiàn)基礎(chǔ)
static inline void __list_add(struct list_head *new,
                  struct list_head *prev,
                  struct list_head *next)
{
    if (!__list_add_valid(new, prev, next))
        return;

       // 前后向指針的改寫賦值
    next->prev = new;
    new->next = next;
    new->prev = prev;
    WRITE_ONCE(prev->next, new);
}
  • 在頭部插入, 在頭指針和第一個(gè)元素間插入
static inline void list_add(struct list_head *new, struct list_head *head)
{
    __list_add(new, head, head->next);
}
  • 在尾部插入,在最后一個(gè)元素間和頭指針間插入, 因?yàn)槭茄h(huán)鏈表嘛~
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
}
刪除操作
  • 刪除兩個(gè)元素之間的元素
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
    next->prev = prev;
    WRITE_ONCE(prev->next, next);
}
  • 刪除一個(gè)已知元素entry
static inline void __list_del_entry(struct list_head *entry)
{
    if (!__list_del_entry_valid(entry))
        return;

    __list_del(entry->prev, entry->next);
}
替換操作

都是指針的變換

static inline void list_replace(struct list_head *old,
                struct list_head *new)
{
    new->next = old->next;
    new->next->prev = new;
    new->prev = old->prev;
    new->prev->next = new;
}
移動(dòng)操作
  • 將一個(gè)元素移動(dòng)到另一個(gè)list的頭部
static inline void list_move(struct list_head *list, struct list_head *head)
{
       // 從原處的list后摘掉
    __list_del_entry(list);
       // 添加到新鏈表的頭部
    list_add(list, head);
}
  • 將一個(gè)元素移動(dòng)到另一個(gè)list的隊(duì)尾
static inline void list_move_tail(struct list_head *list,
                  struct list_head *head)
{
       // 從原處的list后摘掉
    __list_del_entry(list);
       // 添加到新鏈表的隊(duì)尾
    list_add_tail(list, head);
}
拆分操作, 將一個(gè)隊(duì)列由指定的位置拆成兩個(gè)隊(duì)列

list是新隊(duì)列的head指針, 包括的元素從原h(huán)ead隊(duì)列的第一個(gè)元素到entry, head隊(duì)列僅包括余下的元素

static inline void __list_cut_position(struct list_head *list,
        struct list_head *head, struct list_head *entry)
{
    struct list_head *new_first = entry->next;
    list->next = head->next;
    list->next->prev = list;
    list->prev = entry;
    entry->next = list;
    head->next = new_first;
    new_first->prev = head;
}
合并操作:
  • 將list列表中除了list本身插入到prev和next之間
static inline void __list_splice(const struct list_head *list,
                 struct list_head *prev,
                 struct list_head *next)
{
    struct list_head *first = list->next;
    struct list_head *last = list->prev;

    first->prev = prev;
    prev->next = first;

    last->next = next;
    next->prev = last;
}
  • 將一個(gè)列表插入到另一個(gè)列表的頭部
static inline void list_splice(const struct list_head *list,
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head, head->next);
}
  • 將一個(gè)列表插入到另一個(gè)列表的尾部
static inline void list_splice_tail(struct list_head *list,
                struct list_head *head)
{
    if (!list_empty(list))
        __list_splice(list, head->prev, head);
}
list_entry

按之前說(shuō)的, 這個(gè)list_head都有要嵌入到用戶定義的struct中,這個(gè)宏就是由這個(gè)list_head ptr來(lái)獲取當(dāng)前所處的struct對(duì)象的指針, 用了linux的經(jīng)典宏定義 container_of

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)
一堆宏定義, 用來(lái)各種遍歷, 獲取entry
注意事項(xiàng)
  • 只說(shuō)一個(gè),就是多線程操作同一個(gè)list, 還是需要加鎖
使用實(shí)例
typedef struct _Foo {                              
    int data_;                                     
    struct list_head link;                         
} Foo;                                             
                                                   
int main(int argn, char* argv[]) {                 
    LIST_HEAD(test_link);                          
                                                   
    Foo f1;                                        
    f1.data_ = 1;                                  
    LIST_HEAD_INIT(f1.link);                       
                                                   
    Foo f2;                                        
    f1.data_ = 2;                                  
    LIST_HEAD_INIT(f2.link);                       
                                                   
    list_add(&f1.link, &test_link)                 
    list_add(&f2.link, &test_link)                 
                                                   
    struct Foo* pos;                               
    list_for_each_entry(pos, &test_link, link) {   
        printf("%d\n", pos->data_);                
    }                                              
                                                   
    return 0;                                      
}                                                  
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市类腮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌写穴,老刑警劉巖篷扩,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡祷舀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)裳扯,“玉大人抛丽,你說(shuō)我怎么就攤上這事∈尾颍” “怎么了亿鲜?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)冤吨。 經(jīng)常有香客問我蒿柳,道長(zhǎng),這世上最難降的妖魔是什么漩蟆? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任垒探,我火速辦了婚禮,結(jié)果婚禮上怠李,老公的妹妹穿的比我還像新娘圾叼。我一直安慰自己,他們只是感情好扔仓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布褐奥。 她就那樣靜靜地躺著,像睡著了一般翘簇。 火紅的嫁衣襯著肌膚如雪撬码。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天版保,我揣著相機(jī)與錄音呜笑,去河邊找鬼。 笑死彻犁,一個(gè)胖子當(dāng)著我的面吹牛叫胁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汞幢,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼驼鹅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了森篷?” 一聲冷哼從身側(cè)響起输钩,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仲智,沒想到半個(gè)月后买乃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钓辆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年剪验,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肴焊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡功戚,死狀恐怖娶眷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啸臀,我是刑警寧澤茂浮,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站壳咕,受9級(jí)特大地震影響席揽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谓厘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一幌羞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧竟稳,春花似錦属桦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至诊笤,卻和暖如春系谐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背讨跟。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工纪他, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晾匠。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓茶袒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親凉馆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子薪寓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容