linux内核链表图解,Linux内核链表的精彩实现

//Linux内核链表的精彩实现

//链表数据结构的定义很简单([include/linux/list.h],以下所有代码

struct list_head{

struct list_head *next,*prev;

};

//一般此结构可以实现双循环链表的功能

//②.链表接口操作

//1. 声明和初始化

/*实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来

的呢 ?让我们来看看LIST_HEAD()这个宏:*/

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)

/*当我们用LIST_HEAD(nf_sockopts)声明一个名为nf_sockopts的链表头时,它的next、prev指针都

初始化为指向自己,这样,我们就有了一个空链表,因为Linux用头指针的next是否指向自己来

判断链表是否为空:*/

static inline int list_empty(const struct list_head *head)

{

return head->next == head;

}

/*除了用LIST_HEAD()宏在声明的时候初始化一个链表以外,Linux还提供了一个

INIT_LIST_HEAD宏用于运行时初始化链表:*/

#define INIT_LIST_HEAD(ptr) do { \

(ptr)->next = (ptr); (ptr)->prev = (ptr); \

} while (0)

/*③插入/删除/合并

a 插入

对链表的插入操作有两种:在表头插入和在表尾插入。Linux为此提供了两个接口:

*/

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

static inline void list_add_tail(struct list_head *new, struct list_head *head);

/*表头插入函数list_add的实现*/

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

{

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

}

/*__list_add的实现*/

void __list_add(struct list_head *new,

struct list_head *prev,

struct list_head *next)

{

WARN(next->prev != prev,

"list_add corruption. next->prev should be "

"prev (%p), but was %p. (next=%p).\n",

prev, next->prev, next);

WARN(prev->next != next,

"list_add corruption. prev->next should be "

"next (%p), but was %p. (prev=%p).\n",

next, prev->next, prev);

next->prev = new;

new->next = next;

new->prev = prev;

prev->next = new;

}

/*表头插入函数list_add_tail的实现*/

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

{

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

}

/*list_add_tail的实现*/

void __list_add(struct list_head *new,

struct list_head *prev,

struct list_head *next)

{

WARN(next->prev != prev,

"list_add corruption. next->prev should be "

"prev (%p), but was %p. (next=%p).\n",

prev, next->prev, next);

WARN(prev->next != next,

"list_add corruption. prev->next should be "

"next (%p), but was %p. (prev=%p).\n",

next, prev->next, prev);

next->prev = new;

new->next = next;

new->prev = prev;

prev->next = new;

}

/*b 删除*/

static inline void list_del(struct list_head *entry);

void list_del(struct list_head *entry)

{

WARN(entry->prev->next != entry,

"list_del corruption. prev->next should be %p, "

"but was %p\n", entry, entry->prev->next);

WARN(entry->next->prev != entry,

"list_del corruption. next->prev should be %p, "

"but was %p\n", entry, entry->next->prev);

__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;

}

/*d 合并

除了针对节点的插入、删除操作,Linux链表还提供了整个链表的插入功能:

*/

static inline void list_splice(struct list_head *list, struct list_head *head);

static inline void list_splice(const struct list_head *list,

struct list_head *head)

{

if (!list_empty(list))

__list_splice(list, head, head->next);

}

static inline void __list_splice(const struct list_head *list,

struct list_head *prev,

struct list_head *next)

{

struct list_head *first = list->next;

struct list_head *last = list->prev;

first->prev = prev;

prev->next = first;

last->next = next;

next->prev = last;

}

/*假设当前有两个链表,表头分别是list1和list2(都是struct list_head变量),当调用

list_splice(&list1,&list2)时,只要list1非空,list1链表的内容将被挂接在list2链表上

位于list2和list2.next之间。新list2链表将以原list1表的第一个节点为首节点,

而尾节点不变。

当list1被挂接到list2之后,作为原表头指针的list1的next、prev仍然指向原来的节点,

为了避免引起混乱,Linux提供了一个list_splice_init()函数:

*/

static inline void list_splice_init(struct list_head *list, struct list_head *head);

static inline void list_splice_init(struct list_head *list,

struct list_head *head)

{

if (!list_empty(list)) {

__list_splice(list, head, head->next);

INIT_LIST_HEAD(list);

}

}

/*④.遍历

遍历是链表最经常的操作之一,为了方便核心应用遍历链表,Linux链表将遍历操作抽象成几

个宏。在介绍遍历宏之前,我们先看看如何从链表中访问到我们真正需要的数据项。

a) 由链表节点到数据项变量

我们知道,Linux链表中仅保存了数据项结构中list_head成员变量的地址,那么我们如何通过这

个list_head成员访问到作为它的所有者的节点数据呢?Linux为此提供了一个

list_entry(ptr,type,member)宏,其中ptr是指向该数据中list_head成员的指针,也就是存储在链表

中的地址值,type是数据项的类型,member则是数据项类型定义中list_head成员的变量名

*/

#define list_entry(ptr, 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)

/*这里使用的是一个利用编译器技术的小技巧,即先求得结构成员在与结构中的偏移量,然后根

据成员变量的地址反过来得出属主结构变量的地址。

container_of()和offsetof()并不仅用于链表操作,这里最有趣的地方是((type *)0)->member,它将

0地址强制"转换"为type结构的指针,再访问到type结构中的member成员。在container_of宏中,

它用来给typeof()提供参数(typeof()是gcc的扩展,和sizeof()类似),以获得member成员的数

据类型;在offsetof()中,这个member成员的地址实际上就是type数据结构中member成员相对

于结构变量的偏移量。*/

/*遍历宏

大多数情况下,遍历链表的时候都需要获得链表节点数据项,也就是说list_for_each()和

list_entry()总是同时使用。对此Linux给出了一个list_for_each_entry()宏:

*/

//#define list_for_each_entry(pos, head, member)

#define list_for_each_entry(pos, head, member)

\

for (pos = list_entry((head)->next, typeof(*pos), member);

\

&pos->member != (head);

\

pos = list_entry(pos->member.next, typeof(*pos), member))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值