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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux内核重要结构体,Linux中list_head结构体相关 | 技术部落

發(fā)布時(shí)間:2024/9/27 linux 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内核重要结构体,Linux中list_head结构体相关 | 技术部落 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在Linux內(nèi)核中,提供了一個(gè)用來(lái)創(chuàng)建雙向循環(huán)鏈表的結(jié)構(gòu) list_head。雖然linux內(nèi)核是用C語(yǔ)言寫(xiě)的,但是list_head的引入,使得內(nèi)核數(shù)據(jù)結(jié)構(gòu)也可以擁有面向?qū)ο蟮奶匦?#xff0c;通過(guò)使用操作list_head 的通用接口很容易實(shí)現(xiàn)代碼的重用,有點(diǎn)類似于C++的繼承機(jī)制。

下面就是kernel中的list_head結(jié)構(gòu)定義:

struct list_head {

struct list_head *next, *prev;

};

list_head是linux?kernel中非常重要的一個(gè)結(jié)構(gòu)體,是雙向鏈表的數(shù)據(jù)結(jié)構(gòu)體,為了減少浪費(fèi),眾多鏈表都是用list_head以及其相關(guān)原語(yǔ)操作,list_head這個(gè)結(jié)構(gòu)看起來(lái)怪怪的,它竟沒(méi)有數(shù)據(jù)域!所以看到這個(gè)結(jié)構(gòu)的人第一反應(yīng)就是我們?cè)趺丛L問(wèn)數(shù)據(jù)?其實(shí)list_head不是拿來(lái)單獨(dú)用的,它一般被嵌到其它結(jié)構(gòu)中:

比如所有的進(jìn)程是靠它串聯(lián)在一起的,所有的inode也靠它串聯(lián)在一起等等。

需要注意的一點(diǎn)是,頭結(jié)點(diǎn)head是不使用的,這點(diǎn)需要注意。

使用list_head組織的鏈表的結(jié)構(gòu)如下圖所示:

list_head

特別注意的是,list_head中的指針存放的是另一個(gè)list_head的地址,而不是含有l(wèi)ist_head結(jié)構(gòu)的整個(gè)數(shù)據(jù)結(jié)構(gòu)的地址;

舉例如下:

struct file_node{

char c;

struct list_head node;

};

此時(shí)list_head就作為它的父結(jié)構(gòu)中的一個(gè)成員了,當(dāng)我們知道list_head的地址(指針)時(shí),我們可以通過(guò)list.c提供的宏 list_entry 來(lái)獲得它的父結(jié)構(gòu)的地址。下面我們來(lái)看看list_entry的實(shí)現(xiàn):(list_entry: 與container_of功能相同)

#define list_entry(ptr,type,member)\

container_of(ptr,type,member)

#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE *)0)->MEMBER)

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

const typeof( ((type*)0)->member ) *__mptr=(ptr);\

(type*)( (char*)__mptr - offsetof(type,member) );} )

這里涉及到三個(gè)宏,還是有點(diǎn)復(fù)雜的,我們一個(gè)一個(gè)來(lái)看:

#define offsetof(TYPE,MEMBER) ( (size_t)& ((TYPE *)0)-> MEMBER )

我們知道 0 地址內(nèi)容是不能訪問(wèn)的,但 0地址的地址我們還是可以訪問(wèn)的, 這里用到一個(gè)取址運(yùn)算符

(TYPE *)0 它表示將 0地址強(qiáng)制轉(zhuǎn)換為T(mén)YPE類型,((TYPE *)0)-> MEMBER 也就是從0址址找到TYPE 的成員MEMBER 。

我們結(jié)合上面的結(jié)構(gòu)file_node來(lái)看

將實(shí)參代入 offset( struct file_node, node );最終將變成這樣:

( (size_t) & ((struct file_node*)0)-> node );這樣看的還是不很清楚,我們?cè)僮冏?#xff1a;

struct file_node *p = NULL;

& p->node;

這樣應(yīng)該比較清楚了,即求 p 的成員 node的地址,只不過(guò)p 為0地址,從0地址開(kāi)始算成員node的地址,也就是 成員 node 在結(jié)構(gòu)體 struct file_node中的偏移量。offset宏就是算MEMBER在TYPE中的偏移量的。

我們?cè)倏吹诙€(gè)宏

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

const typeof( ((type*)0)->member ) *__mptr=(ptr);\

(type*)( (char*)__mptr - offsetof(type,member) );} )

這個(gè)宏是由兩個(gè)語(yǔ)句組成,最后container_of返回的結(jié)果就是第二個(gè)表達(dá)式的值。這里__mptr為中間變量,這就是list_head指針類型,它被初始化為ptr的值,而ptr就是當(dāng)前所求的結(jié)構(gòu)體中l(wèi)ist_head節(jié)點(diǎn)的地址。為什么要用中間變量,這是考慮到安全性因素,如果傳進(jìn)來(lái)一個(gè)ptr++,所有ptr++放在一個(gè)表達(dá)式中會(huì)有副作用,像 (p++)+(p++)之類。

(char*)__mptr 之所以要強(qiáng)制類型轉(zhuǎn)化為char是因?yàn)榈刂肥且宰止?jié)為單位的,而char的長(zhǎng)度就是一個(gè)字節(jié)。

container_of的值是兩個(gè)地址相減,

剛說(shuō)了__mptr是結(jié)構(gòu)體中l(wèi)ist_head節(jié)點(diǎn)的地址,offset宏求的是list_head節(jié)點(diǎn)MEMBER在結(jié)構(gòu)體TYPE中的偏移量,那么__mptr減去它所在結(jié)構(gòu)體中的偏移量,就是結(jié)構(gòu)體的地址。

所以list_entry(ptr,type,member)宏的功能就是,由結(jié)構(gòu)體成員地址求結(jié)構(gòu)體地址。其中ptr 是所求結(jié)構(gòu)體中l(wèi)ist_head成員指針,type是所求結(jié)構(gòu)體類型,member是結(jié)構(gòu)體list_head成員名。通過(guò)下圖來(lái)總結(jié)一下:

list

列舉一些雙鏈表的常用操作:

雙向鏈表的遍歷——list_for_each

//注:這里prefetch 是gcc的一個(gè)優(yōu)化選項(xiàng),也可以不要

#define list_for_each(pos, head) \

for (pos = (head)->next; prefetch(pos->next), pos != (head); \

pos = pos->next)

生成雙向鏈表的頭結(jié)點(diǎn)——LIST_HEAD()

//LIST_HEAD() -- 生成一個(gè)名為name的雙向鏈表頭節(jié)點(diǎn)

#define LIST_HEAD(name) \

struct list_head name = LIST_HEAD_INIT(name)

static inline void INIT_LIST_HEAD(struct list_head *list)

{

list->next = list;

list->prev = list;

}

雙向鏈表的插入操作 -- list_add()

//將new所代表的結(jié)構(gòu)體插入head所管理的雙向鏈表的頭節(jié)點(diǎn)head之后: (即插入表頭)

static inline void list_add(struct list_head *new, struct list_head *head)

{

__list_add(new, head, head->next);

}

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;

}

從list中刪除結(jié)點(diǎn)——list_del()

static inline void list_del(struct list_head *entry)

{

__list_del(entry->prev, entry->next);

entry->next = LIST_POISON1;

entry->prev = LIST_POISON2;

}

static inline void __list_del(struct list_head * prev, struct list_head * next)

{

next->prev = prev;

prev->next = next;

}

判斷鏈表是否為空(如果雙向鏈表head為空則返回真,否則為假)——list_empty()

static inline int list_empty(const struct list_head *head)

{

return head->next == head;

}

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的linux内核重要结构体,Linux中list_head结构体相关 | 技术部落的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。