日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux内核链表深度分析【转】

發布時間:2025/3/20 linux 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux内核链表深度分析【转】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文轉載自:http://blog.csdn.net/coding__madman/article/details/51325646

鏈表簡介:

鏈表是一種常用的數據結構,它通過指針將一系列數據節點連接成一條數據鏈。相對于數組,鏈表具有更好的動態性,建立鏈表時無需預先知道數據總量,可以隨機分配空間,可以高效地在鏈表中的任意位置實時插入或者刪除數據。鏈表的開銷主要是訪問的順序性和組織鏈的空間損失。

?

內核鏈表的好主要體現為兩點,1是可擴展性,2是封裝。可擴展性肯定是必須的,內核一直都是在發展中的,所以代碼都不能寫成死代碼,要方便修改和追加。將鏈表常見的操作都進行封裝,使用者只關注接口,不需關注實現。分析內核中的鏈表我們 可以做些什么呢?我覺得可以將其復用到用戶態編程中,以后在用戶態下編程就不需要寫一些關于鏈表的代碼了,直接將內核中list.h中的代碼拷貝過來用。也可以整理出my_list.h,在以后的用戶態編程中直接將其包含到C文件中。

?

?

1. 鏈表對比

傳統鏈表和內核鏈表

傳統鏈表:一般指的是單向鏈表

struct List

{

struct list *next;//鏈表結點指針域

};

內核鏈表:雙向循環鏈表 設計初衷是設計出一個通用統一的雙向鏈表!

struct list_head

{

?struct list_head ? ?*head, *prev;

};

list_head結構包含兩個指向list_head結構體的指針

prev和next,由此可見,內核的鏈表具備雙鏈表功能,實際上,通常它都組織成雙向循環鏈表

2. 內核鏈表使用

1. INIT_LIST_HEAD:創建鏈表

2. list_add:在鏈表頭插入節點

3. list_add_tail:在鏈表尾插入節點

4. list_del:刪除節點

5. list_entry:取出節點

6. list_for_each:遍歷鏈表

(如果我們不知道這些函數的參數以及函數內部實現,學習查閱這些函數的參數或者實現代碼最好的方法還是直接查看內核源碼,結和前面的用sourceInsight工具直接搜索這些函數的名字)

?

?

?

下面舉個例子:比如查閱INIT_LIST_HEAD函數,

這個是先將內核源碼導入sourceInsight工程里面!源碼可以在官網上下載,然后在Linux下解壓(文件名Linux分大小寫,windows不分大小寫),然后通過Samba和映射網絡驅動器功能(前面的sourceInsight博文有講到),點擊R圖標左邊的那個圖標(像一個打開的一本書)

這樣可以很快的查看到代碼實現部分:在內核Mkregtale.c文件中

?

[html]?view plaincopy
  • /*??
  • ?*?This?is?a?simple?doubly?linked?list?implementation?that?matches?the??
  • ?*?way?the?Linux?kernel?doubly?linked?list?implementation?works.??
  • ?*/??
  • ??
  • struct?list_head?{??
  • ????struct?list_head?*next;?/*?next?in?chain?*/??
  • ????struct?list_head?*prev;?/*?previous?in?chain?*/??
  • };??
  • 這個不含數據域的鏈表,可以嵌入到任何數據結構中,例如可按如下方式定義含有數據域的鏈表:

    ?

    [html]?view plaincopy
  • struct?score??
  • {??
  • ????int?num;??
  • ????int?English;??
  • ????int?math;??
  • ????struct?list_head?list;//鏈表鏈接域??
  • };??
  • ??
  • struct?list_head?score_head;//所建立鏈表的鏈表頭??
  • INIT_LIST_HEAD(&score_head);//初始化鏈表頭 完成一個雙向循環鏈表的創建
    上面的紅色部分初始化一個已經存在的list_head對象,score_head為一個結構體的指針,這樣可以初始化堆棧以及全局區定義的score_head對象。調用INIT_LIST_HEAD()宏初始化鏈表節點,將next和prev指針都指向其自身,我們就構造了一個空的雙循環鏈表。

    ?

    初始化一個空鏈表:其實就是鏈表頭,用來指向第一個結點!定義結點并且初始化!然后雙向循環鏈表就誕生了

    ?

    static 加在函數前,表示這個函數是靜態函數,其實際上是對作用域的限制,指該函數作用域僅局限于本文件。所以說,static 具有信息隱蔽的作用。而函數前加?inline?關鍵字的函數,叫內聯函數,表 示編譯程序在調用這個函數時,立即將該函數展開。

    ?

    ?

    [html]?view plaincopy
  • /*?Initialise?a?list?head?to?an?empty?list?*/??
  • static?inline?void?INIT_LIST_HEAD(struct?list_head?*list)??
  • {??
  • ????????list->next?=?list;??
  • ????list->prev?=?list;??
  • }??
  • ?

    ?

    list_add:在鏈表頭插入節點

    ?

    [html]?view plaincopy
  • /**??
  • ?*?list_add?-?add?a?new?entry??
  • ?*?@new:?new?entry?to?be?added??
  • ?*?@head:?list?head?to?add?it?after??
  • ?*??
  • ?*?Insert?a?new?entry?after?the?specified?head.??
  • ?*?This?is?good?for?implementing?stacks.??
  • ?*/??
  • static?inline?void?list_add(struct?list_head?*new,?struct?list_head?*head)??
  • {??
  • ??__list_add(new,?head,?head->next);??
  • }??
  • ?

    [html]?view plaincopy
  • /*??
  • ?*?Insert?a?new?entry?between?two?known?consecutive?entries.??
  • ?*??
  • ?*?This?is?only?for?internal?list?manipulation?where?we?know??
  • ?*?the?prev/next?entries?already!??
  • ?*/??
  • #ifndef?CONFIG_DEBUG_LIST??
  • static?inline?void?__list_add(struct?list_head?*new,??
  • ??????????????????struct?list_head?*prev,??
  • ??????????????????struct?list_head?*next)??
  • {??
  • ????next->prev?=?new;??
  • ????new->next?=?next;??
  • ????new->prev?=?prev;??
  • ????prev->next?=?new;??
  • }??
  • #else??
  • extern?void?__list_add(struct?list_head?*new,??
  • ??????????????????struct?list_head?*prev,??
  • ??????????????????struct?list_head?*next);??
  • #endif??


  • ?

    ?list_add_tail:在鏈表尾插入節點

    ?

    [html]?view plaincopy
  • /**??
  • ?*?list_add_tail?-?add?a?new?entry??
  • ?*?@new:?new?entry?to?be?added??
  • ?*?@head:?list?head?to?add?it?before??
  • ?*??
  • ?*?Insert?a?new?entry?before?the?specified?head.??
  • ?*?This?is?useful?for?implementing?queues.??
  • ?*/??
  • static?inline?void?list_add_tail(struct?list_head?*new,?struct?list_head?*head)??
  • {??
  • ????__list_add(new,?head->prev,?head);??
  • }??
  • ?

    用法示例:

    struct score
    {
    int num;
    int English;
    int math;
    struct list_head list;//鏈表鏈接域
    };

    struct list_head score_head;//所建立鏈表的鏈表頭
    //定義三個節點 然后插入到鏈表中
    struct score stu1, stu2, stu3;

    list_add_tail(&(stu1.list), &score_head);//使用尾插法

    Linux 的每個雙循環鏈表都有一個鏈表頭,鏈表頭也是一個節點,只不過它不嵌入到宿主數據結構中,即不能利用鏈表頭定位到對應的宿主結構,但可以由之獲得虛擬的宿主結構指針。

    ?

    ?

    ?list_del:刪除節點

    ?

    [html]?view plaincopy
  • /*?Take?an?element?out?of?its?current?list,?with?or?without??
  • ?*?reinitialising?the?links.of?the?entry*/??
  • static?inline?void?list_del(struct?list_head?*entry)??
  • {??
  • ????struct?list_head?*list_next?=?entry->next;??
  • ????struct?list_head?*list_prev?=?entry->prev;??
  • ??
  • ????list_next->prev?=?list_prev;??
  • ????list_prev->next?=?list_next;??
  • ??
  • }??


  • ?

    list_entry:取出節點

    ?

    [html]?view plaincopy
  • /**??
  • ?*?list_entry?-?get?the?struct?for?this?entry??
  • ?*?@ptr:the?&struct?list_head?pointer.??
  • ?*?@type:the?type?of?the?struct?this?is?embedded?in.??
  • ?*?@member:the?name?of?the?list_struct?within?the?struct.??
  • ?*/??
  • #define?list_entry(ptr,?type,?member)?\??
  • ????container_of(ptr,?type,?member)??
  • ?

    ?

    [html]?view plaincopy
  • /**??
  • ?*?container_of?-?cast?a?member?of?a?structure?out?to?the?containing?structure??
  • ?*?@ptr:????the?pointer?to?the?member.??
  • ?*?@type:???the?type?of?the?container?struct?this?is?embedded?in.??
  • ?*?@member:?the?name?of?the?member?within?the?struct.??
  • ?*??
  • ?*/??
  • #define?container_of(ptr,?type,?member)?({??????????\??
  • ????const?typeof(((type?*)0)->member)*__mptr?=?(ptr);????\??
  • ?????????????(type?*)((char?*)__mptr?-?offsetof(type,?member));?})??



  • list_for_each:遍歷鏈表

    [html]?view plaincopy
  • #define?list_for_each(pos,?head)?\??
  • ????for?(pos?=?(head)->next;?prefetch(pos->next),?pos?!=?(head);?\??
  • ????pos?=?pos->next)</span></span>??
  • ?

    可以看出,使用了輔助指針pos,pos是從第一節點開始的,并沒有訪問頭節點,直到pos到達頭節點指針head的時候結束。 而且 這種遍歷僅僅是找到一個個結點的當前位置,那如何通過pos獲得起始結點的地址,從而可以引用結點的域? list.h 中定義了 list_entry 宏: #define ? list_entry( ptr, type, member ) ?\ ( (type *) ( (char *) (ptr) ?- (unsigned long) ( &( (type *)0 ) ?-> ?member ) ) ) 分析:(unsigned long) ( &( (type *)0 ) ?-> ?member ) 把 0 地址轉化為 type 結構的指針,然后獲取該 結構中 member 域的指針,也就是獲得了 member 在type 結構中的偏移量。其中??(char *) (ptr) 求 出的是 ptr?的絕對地址,二者相減,于是得到 type 類型結構體的起始地址,即起始結點的地址。使用方法非常的巧妙!

    比如下列用法:

    ?

    struct score stu1, stu2, stu3;
    struct list_head *pos;//定義一個結點指針
    struct score *tmp;//定義一個score結構體變量

    ?

    [html]?view plaincopy
  • //遍歷整個鏈表,每次遍歷將數據打印出來??
  • ????list_for_each(pos,?&score_head)//這里的pos會自動被賦新值??
  • ????{??
  • ????????tmp?=?list_entry(pos,?struct?score,?list);??
  • ????????printk(KERN_WARNING"num:?%d,?English:?%d,?math:?%d\n",?tmp->num,?tmp->English,?tmp->math);??
  • ????}??


  • ?

    ?

    list_for_each_safe: 鏈表的釋放

    ?

    [html]?view plaincopy
  • /**??
  • ?*?list_for_each_safe?-?iterate?over?a?list?safe?against?removal?of?list?entry??
  • ?*?@pos:the?&struct?list_head?to?use?as?a?loop?cursor.??
  • ?*?@n:another?&struct?list_head?to?use?as?temporary?storage??
  • ?*?@head:</span>the?head?for?your?list.??
  • ?*/??
  • #define?list_for_each_safe(pos,?n,?head)?\??
  • ????for?(pos?=?(head)->next,?n?=?pos->next;?pos?!=?(head);?\??
  • ????????pos?=?n,?n?=?pos->next)??


  • ?

    3. 內核鏈表實現分析

    4. 移植內核鏈表(這里先貼出一個使用內核鏈表的內核模塊小例程)

    mylist.c文件

    ?

    [html]?view plaincopy
  • #include<linux/module.h>??
  • #include<linux/init.h>??
  • #include<linux/list.h>//包含內核鏈表頭文件??
  • ??
  • struct?score??
  • {??
  • ????int?num;??
  • ????int?English;??
  • ????int?math;??
  • ????struct?list_head?list;//鏈表鏈接域??
  • };??
  • ??
  • struct?list_head?score_head;//所建立鏈表的鏈表頭??
  • ??
  • //定義三個節點?然后插入到鏈表中??
  • struct?score?stu1,?stu2,?stu3;??
  • struct?list_head?*pos;//定義一個結點指針??
  • struct?score?*tmp;//定義一個score結構體變量??
  • ??
  • int?mylist_init()??
  • {??
  • ????INIT_LIST_HEAD(&score_head);//初始化鏈表頭?完成一個雙向循環鏈表的創建??
  • ??????
  • ????stu1.num?=?1;??
  • ????stu1.English?=?59;??
  • ????stu1.math?=?99;??
  • ??????
  • ????//然后將三個節點插入到鏈表中??
  • ????list_add_tail(&(stu1.list),?&score_head);//使用尾插法??
  • ??????
  • ????stu2.num?=?2;??
  • ????stu2.English?=?69;??
  • ????stu2.math?=?98;??
  • ????list_add_tail(&(stu2.list),?&score_head);??
  • ??????
  • ????stu3.num?=?3;??
  • ????stu3.English?=?89;??
  • ????stu3.math?=?97;??
  • ????list_add_tail(&(stu3.list),?&score_head);??
  • ??????
  • ????//遍歷整個鏈表,每次遍歷將數據打印出來??
  • ????list_for_each(pos,?&score_head)//這里的pos會自動被賦新值??
  • ????{??
  • ????????tmp?=?list_entry(pos,?struct?score,?list);??
  • ????????printk(KERN_WARNING"num:?%d,?English:?%d,?math:?%d\n",?tmp->num,?tmp->English,?tmp->math);??
  • ????}??
  • ??????
  • ????return?0;??
  • }??
  • ??
  • void?mylist_exit()??
  • {??
  • ????//退出時刪除結點??
  • ????list_del(&(stu1.list));??
  • ????list_del(&(stu2.list));??
  • ????printk(KERN_WARNING"mylist?exit!\n");??
  • }??
  • ??
  • module_init(mylist_init);??
  • module_exit(mylist_exit);??

  • Makefile文件

    ?

    ?

    [html]?view plaincopy
  • obj-m?:=?mylist.o??
  • ??
  • KDIR?:=?/home/kernel/linux-ok6410??
  • ??
  • all:??
  • ????make?-C?$(KDIR)?M=$(PWD)?modules?CROSS_COMPILE=arm-linux-?ARCH=arm??
  • ??????
  • clean:??
  • ????rm?-f?*.o?*.ko?*.order?*.symvers??

  • ?

    在終端上加載運行內核模塊:

    這里rmmod 時會有個錯誤!不過沒大事!百度有很多解決方案!

    總結

    以上是生活随笔為你收集整理的Linux内核链表深度分析【转】的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。