最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

linux內(nèi)核雙向鏈表詳解

 更新時間:2025年07月22日 15:52:06   作者:大肥周  
Linux內(nèi)核雙向鏈表通過結(jié)構(gòu)體嵌入node實現(xiàn),使用container_of獲取結(jié)構(gòu)體地址,初始化用LIST_HEAD_INIT或INIT_LIST_HEAD,增刪通過list_add/list_del操作,遍歷用list_for_each_entry正反向宏,注意head節(jié)點不參與遍歷,鏈表元素需為全局或靜態(tài)變量,避免指針越界或內(nèi)存崩潰

介紹下linux內(nèi)核的雙向鏈表的使用,接口定義在include/linux/list.h

結(jié)構(gòu)體

struct list_head {
    struct list_head *next;  // 指向下一個節(jié)點
    struct list_head *prev;  // 指向前一個節(jié)點
};

使用的時候,會把這個結(jié)構(gòu)體放在需要使用鏈表的結(jié)構(gòu)體里,放在結(jié)構(gòu)體里的任意位置都可以,讀取數(shù)據(jù)的時候,是從鏈表里拿到node后,通過container_of拿到node所在結(jié)構(gòu)體的地址,根據(jù)這個地址再來找結(jié)構(gòu)體里的其他成員。

所以,同一個鏈表里,是根據(jù)node把存放的內(nèi)容關(guān)聯(lián)串起來,再根據(jù)node拿到內(nèi)容的,而不關(guān)心這些node所在的內(nèi)容的結(jié)構(gòu)體是不是同一個結(jié)構(gòu)體。

struct zslnode {
    int seq;
    struct list_head node;
};

struct zslnode1 {
    int seq;  
    struct list_head node1;
    int seq1;
};

初始化

初始化有多種方式,常見的有LIST_HEAD_INIT和INIT_LIST_HEAD

作用是常見一個struct list_head變量,把里面的next和prev都指向自己。

增加

常用的增加分為兩種接口,往前增加list_add,往后增加list_add_tail。這倆接口都是調(diào)用的__list_add實現(xiàn)的,往前增加是__list_add(new, head, head->next),往后是__list_add(new, head->prev, head)。

__list_add主要就是如下操作:

       next->prev = new;
       new->next = next;
       new->prev = prev;
       WRITE_ONCE(prev->next, new);

所以,增加節(jié)點其實就是在原來鏈表的的head->prev、head和head->next三個之間去增加。

head本身不存在鏈表上(即在head上去存數(shù)據(jù)遍歷的時候是取不到的),像是把雙向鏈表的首尾連接起來的節(jié)點,head->next永遠(yuǎn)指向鏈表第一個節(jié)點,head->prev指向最后一個節(jié)點。

這樣就容易理解__list_add里的賦值操作,去掉prev和next之間的聯(lián)系,然后把new增加到prev和next中間。

往前增加參數(shù)里prev是head,即首尾相連的點,next是head->next即鏈表第一位,new增加到中間就變成了鏈表第一位。

往后增加prev是鏈表最后一位,next是首尾相連的點,new增加到中間就變成了鏈表最后一位。

刪除

刪除常用的是list_del,最終調(diào)用的是__list_del,主要操作如下:

       next->prev = prev;
       WRITE_ONCE(prev->next, next);

即,后面一個節(jié)點的prev指向前面一個節(jié)點,前面一個節(jié)點的next指向后面。

遍歷

遍歷常用的是list_for_each_entry正向遍歷,list_for_each_entry_reverse反向遍歷,也還有不少別的變種,基本差不多。

       list_for_each_entry定義如下
       #define list_for_each_entry(pos, head, member)                           \
       for (pos = list_first_entry(head, typeof(*pos), member);   \
            !list_entry_is_head(pos, head, member);                 \
            pos = list_next_entry(pos, member))

找到第一個節(jié)點,然后一路next查找。第一個節(jié)點,前面有提到就是head->next,再通過list_entry拿結(jié)構(gòu)體地址,list_entry就是使用的container_of。

反向遍歷就是反過來,查找最后一個節(jié)點,即head->prev,然后一路prev往前查找node。

直到遍歷到list_entry_is_head停止,即發(fā)現(xiàn)自己就是head,&pos->member == (head),這里&pos->member就是存儲的結(jié)構(gòu)體里指向node的地址。

實例

struct zslnode {
    int seq;
    struct list_head node;
};

struct zslnode1 {
    int seq1;	
    struct list_head node1;
    int seq2;
};

static void testlist(void)
{
	struct list_head list;
	struct zslnode next1;
	struct zslnode next2;	
	struct zslnode1 next3;
	struct zslnode pre1;	
	struct zslnode pre2;

	struct zslnode *tmpnode;
	struct zslnode1 *tmpnode1;

	INIT_LIST_HEAD(&list);

	next1.seq = 101;
	list_add_tail(&next1.node, &list);

	next2.seq = 102;
	list_add_tail(&next2.node, &list);

	next3.seq1 = 1000;
	next3.seq2 = 2000;
	list_add_tail(&next3.node1, &list);

	pre1.seq = 99;
	list_add(&pre1.node, &list);

	pre2.seq = 98;
	list_add(&pre2.node, &list);

	list_for_each_entry(tmpnode, &list, node) 
	{
		printk(KERN_INFO "tlist: seq:%d\n",tmpnode->seq);
	}

	list_del(&next2.node);

	list_for_each_entry(tmpnode1, &list, node1) 
	{
		printk(KERN_INFO "tlist1: seq:%d %d\n",tmpnode1->seq1,tmpnode1->seq2);
	}

}

運行結(jié)果如下

注意事項

  • 從掃描的邏輯來看,head的節(jié)點是不能被掃描到的,雖然只有head存在的話,能拿到數(shù)據(jù)(那是因為head->next=head)。
  • 從實例運行結(jié)果tlist1的打印可以看到不同的結(jié)構(gòu)體可以放在一起處理,但是會出現(xiàn)內(nèi)存越界的情況,讀到不可控的內(nèi)容。
  • List的內(nèi)容存放,都是用指針存放的,所以如果鏈表是全局變量的話,里面的節(jié)點也必須是全局或者靜態(tài)變量,不能是棧里的內(nèi)容,不然會有指針踩飛導(dǎo)致崩潰等問題。

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解如何在Linux(CentOS 7)命令行模式安裝VMware Tools

    詳解如何在Linux(CentOS 7)命令行模式安裝VMware Tools

    本篇文章主要介紹了如何在Linux(CentOS 7)命令行模式安裝VMware Tools,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • Linux創(chuàng)建服務(wù)使用systemctl管理詳解

    Linux創(chuàng)建服務(wù)使用systemctl管理詳解

    文章指導(dǎo)在Linux中創(chuàng)建systemd服務(wù),設(shè)置文件權(quán)限為所有者讀寫、其他只讀,重新加載配置,啟動服務(wù)并檢查狀態(tài),確保服務(wù)正常運行,關(guān)鍵步驟包括權(quán)限配置與狀態(tài)驗證
    2025-09-09
  • linux 安裝配置lamp v2

    linux 安裝配置lamp v2

    距離第一個版本已經(jīng)有一年了。修正了幾個錯誤的地方,還有取消了某些lib的安裝,因為centos有,所以相關(guān)的lib安裝我都沒有去查找錯誤。
    2009-02-02
  • Linux之Centos8創(chuàng)建CA證書教程

    Linux之Centos8創(chuàng)建CA證書教程

    大家好,本篇文章主要講的是Linux之Centos8創(chuàng)建CA證書教程,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Linux Shell里面生成隨機數(shù)的一些思路分析

    Linux Shell里面生成隨機數(shù)的一些思路分析

    這篇文章主要介紹了Linux Shell里面生成隨機數(shù)的一些思路分析,需要的朋友可以參考下
    2016-07-07
  • linux 下部署nodejs項目(兩種方式)

    linux 下部署nodejs項目(兩種方式)

    本篇文章主要介紹了linux 下部署nodejs項目(兩種方式),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-01-01
  • Linux下修改hostname的三種實現(xiàn)方式

    Linux下修改hostname的三種實現(xiàn)方式

    這篇文章主要介紹了Linux下修改hostname的三種實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • 基于Ubuntu 16.04設(shè)置固定IP的方法教程

    基于Ubuntu 16.04設(shè)置固定IP的方法教程

    這篇文章主要給大家介紹了基于Ubuntu 16.04設(shè)置固定IP的方法教程,文中通過圖文以及示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • 最新評論

    尼玛县| 武清区| 定安县| 古田县| 琼结县| 绿春县| 巴彦县| 泽州县| 德惠市| 贵南县| 阜城县| 嘉善县| 通榆县| 江川县| 天镇县| 平原县| 台前县| 文登市| 甘谷县| 关岭| 棋牌| 甘泉县| 新疆| 遂川县| 繁昌县| 阿拉善右旗| 胶州市| 金山区| 贵定县| 磐安县| 灵寿县| 丁青县| 桃园市| 山阴县| 河曲县| 镇江市| 新丰县| 开江县| 宁国市| 三门县| 湘潭县|