Linux 2.6内核链表数据结构学习2

作者在 2011-04-27 12:00:45 发布以下内容
4 插入

对链表的插入操作有两种:在表头插入和在表尾插入。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);

因为Linux链表是循环表,且表头的next、prev分别指向链表中的第一个和最末一个节点,所
以,list_add和list_add_tail的区别并不大,实际上,Linux分别用

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

来实现两个接口,可见,在表头插入是插入在head之后,而在表尾插入是插入在head->prev
之后。

假设有一个新nf_sockopts结构变量new_sockopt需要添加到sockopt链表头,我们应
当这样操作:
list_add(&new_sockopt.list, &sockopt.list);
 
从这里我们看出,sockopt链表中记录的并不是new_sockopt的地址,而是new_sockopt的list元素的
地址。

5 删除

static inline void list_del(struct list_head *entry);
 
当我们需要删除sockopt链表中添加的new_sockopt项时,我们这么操作:
list_del(&new_sockopt.list);
 
被剔除下来的new_sockopt.list,prev、next指针分别被设为LIST_POSITION2和
LIST_POSITION1两个特殊值,这样设置是为了保证不在链表中的节点项不可访问--对
LIST_POSITION1和LIST_POSITION2的访问都将引起页故障。
与之相对应,list_del_init()函数将节点从链表中解下来之后,调用LIST_INIT_HEAD()将节点置为空链状态。

6 搬移

Linux提供了将原本属于一个链表的节点移动到另一个链表的操作,并根据插入到新链表的位
置分为两类:

static inline void list_move(struct list_head *list, struct list_head *head);
static inline void list_move_tail(struct list_head *list, struct list_head *head);
 
例如list_move(&new_sockopt.list,&sockopt.list)会把new_sockopt从它所在的链表上删
除,并将其再链入sockopt的表头。

7 合并

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

static inline void list_splice(struct list_head *list, struct list_head *head);
 
假设当前有两个链表,表头分别是list1和list2(都是struct list_head变量),当调用
list_splice(&list1,&list2)时,只要list1非空,list1链表的内容将被挂接在list2链表
上,位于list2和list2.next(原list2表的第一个节点)之间。

当list1被挂接到list2之后,作为原表头指针的list1的next、prev仍然指向原来的节点,为
了避免引起混乱,Linux提供了一个list_splice_init()函数:

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

该函数在将list合并到head链表的基础上,调用INIT_LIST_HEAD(list)将list设置为空链。

待续...
linux | 阅读 781 次
文章评论,共0条
游客请输入验证码
文章分类
文章归档
最新评论