linux内核链表结构

linux的链表结构定义在include/linux/list.h中

struct list_head{
    struct list_head *next, *prev;
};

list_head结构包含着指向list_head结构的指针prev和next,由此可见,内核链表具备双链表功能,实际上,通常它都组织成双循环链表。不过它和其他双链表结构模型不同,list_head没有数据域。在linux内核链表中,不是在链表结构中包含数据,而是在数据结构中包含链表节点,也就是说,开发者预计某种数据结构将要组成链表时,它的成员中必含struct list_head成员。

由链表节点到数据项变量

linux链表中仅保存了数据项结构中的list_head成员变量的地址,那么如何通过这个list_head成员访问到它的所有者呢?linux为此提供了一个list_entry(ptr, type, member)宏,其中ptr是指向该数据中list_head成员的指针,type是数据项的类型,member则是数据项类型,例如,有一个由proto{}组成的链表,名字叫proto_list,而每个proto{}结构中的list_head{}成员名字叫node。我们要访问这个链表中的第一个节点,则如此调用:

list_entry(proto_list->next, struct proto, node);

list_entry的使用相当简单,相比这下,它的实现则很难理解,linux内核用GCC编译器使用了一些比较新的关键字,比如typeof,所以以下的代码无法在VC等编译器上通过:

#define list_entry(prt, type, member) container_of(ptr, type, member)
#define container_of(ptr, type, member) ({  \
const typeof( ((type *)0)->member) * mptr = (ptr);   \
(type *)(char *)mptr - offsetof(type, member)
#define offsetof(TYPE, MEMBER) (size_t)((TYPE *)0)->MEMBER

正是利用了typeof,内核中许多技巧得以实现,这里,先求得结构成员在与结构中的偏移量,然后根据成员变量地址反过来得出属主结构变量的地址。
要记住的是container_of()和offsetof()并不仅用于链表操作,这里最重要的地方是((type *)0)->member,它将0地址强制转换为type结构的指针,再访问到type结构中的member成员。
对于给定一个结构,offsetof(type, member)是一个常量,list_entry()正是利用这个不变的偏移量来求得链表数据项的变量地址。

hlist结构
hlist是hash_list的简称,即用拉链法实现的hash数据结构,它是由2部分组成:hash数据和冲突链。当第一次要插入hash表的时候,它必定是先插入hash数组中,而以后要插入的节点如果发生了冲突,则可以挂在数据后面,形成一条链表。当然也可以插入数组,原先在数组中的节点被挤出来形成一个链表,list和hlist的区别如图:

这里写图片描述

也许linux链表设计者认为双头(next,prev)的双链表对于hash表来说”过于浪费“,因而另行设计了一套用户hash表应用的hlist数据结构——单指针表头双循环链表。

struct hlist_head{
    struct hlist_node *frist;
};
struct hlist_node{
    struct hlist_node *next, **prev;
};

struct hlist_head仅仅用了一个指针,占4个字节,因为hash数组往往相当大,这样可以节省4个字节乘以数据大小的空间。hlist的表头仅有一个指向首节点的指针,而没有指向尾节点的指针,这样在可能是海量的hash表中存储的表头就能减少一半的空间消耗,hash数组可以称为hash表,冲突节点链表称为hash链。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值