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

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

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

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

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

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

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

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

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

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

初始化

初始化有多種方式,常見(jiàn)的有LIST_HEAD_INIT和INIT_LIST_HEAD

作用是常見(jiàn)一個(gè)struct list_head變量,把里面的next和prev都指向自己。

增加

常用的增加分為兩種接口,往前增加list_add,往后增加list_add_tail。這倆接口都是調(diào)用的__list_add實(shí)現(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é)點(diǎn)其實(shí)就是在原來(lái)鏈表的的head->prev、head和head->next三個(gè)之間去增加。

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

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

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

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

刪除

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

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

即,后面一個(gè)節(jié)點(diǎn)的prev指向前面一個(gè)節(jié)點(diǎn),前面一個(gè)節(jié)點(diǎn)的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))

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

反向遍歷就是反過(guò)來(lái),查找最后一個(gè)節(jié)點(diǎn),即head->prev,然后一路prev往前查找node。

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

實(shí)例

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);
	}

}

運(yùn)行結(jié)果如下

注意事項(xiàng)

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

總結(jié)

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

相關(guān)文章

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

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

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

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

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

    linux 安裝配置lamp v2

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

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

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

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

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

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

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

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

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

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

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

    三河市| 错那县| 疏勒县| 清新县| 宝清县| 门头沟区| 莱阳市| 锡林浩特市| 信阳市| 若尔盖县| 克拉玛依市| 临安市| 论坛| 资兴市| 沂水县| 铜陵市| 水富县| 五峰| 富阳市| 罗甸县| 合阳县| 图木舒克市| 兴城市| 鹰潭市| 汉寿县| 崇州市| 武定县| 阿坝县| 麻栗坡县| 银川市| 普陀区| 漾濞| 绥化市| 沙雅县| 迁西县| 石柱| 叶城县| 高邑县| 沐川县| 霍林郭勒市| 龙江县|