linux内核重要结构体,Linux中list_head结构体相关 | 技术部落
在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)題。
- 上一篇: 定义快捷代码_nodepad++代码编辑
- 下一篇: Java List集合转换相关操作