|
当我们需要删除 nf_sockopts 链表中添加的 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() 把节点置为空链状态。
c) 搬移
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,&nf_sockopts) 会把 new_sockopt 从它所在的链表上删除,并把其再链入 nf_sockopts 的表头。
d) 合并
除了针对节点的插入、删除操作,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 表的第一个节点)之间。新 list2 链表把以原 list1 表的第一个节点为首节点,而尾节点不变。如图(虚箭头为next指针):
当 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 设置为空链。
3. 遍历
遍历是链表最经常的操作之一,为了方便核心应用遍历链表,Linux 链表把遍历操作抽象成几个宏。在介绍遍历宏之前,我们先看看怎么样从链表中访问到我们真正需要的数据项。
a) 由链表节点到数据项变量
我们知道,Linux 链表中仅保存了数据项结构中 list_head 成员变量的地址,那么我们怎么样通过这个 list_head 成员访问到作为它的所有者的节点数据呢?Linux 为此提供了一个 list_entry(ptr,type,member) 宏,其中ptr是指向该数据中 list_head 成员的指针,也就是存储在链表中的地址值,type 是数据项的类型,member 则是数据项类型定义中 list_head 成员的变量名,例如,我们要访问 nf_sockopts 链表中首个 nf_sockopt_ops 变量,则如此调用:
list_entry(nf_sockopts->next, struct nf_sockopt_ops, list);
|
这里 "list" 正是 nf_sockopt_ops 结构中定义的用于链表操作的节点成员变量名。
以上只是一些略详介绍,当然,kernel,的面纱,还须读者们,更多,更深的去揭露,欢迎大家共同交流,
共4页: 上一页 [1] [2] 3 [4] 下一页
|