深入分析 Linux 内核链表的数据结构(2)

分类: 内核技术   出处:iocblog整理  更新时间:2008-08-02   添加到收藏  

  2. 插入/删除/合并
  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);
  
  因为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_sockopt_ops结构变量new_sockopt需要添加到nf_sockopts链表头,我们应当这样操作:
  
  list_add(&new_sockopt.list, &nf_sockopts);
  
  从这里我们看出,nf_sockopts链表中记录的并不是new_sockopt的地址,而是其中的list元素的地址。如何通过链表访问到new_sockopt呢?下面会有详细介绍。
  
  b) 删除
  
  static inline void list_del(struct list_head *entry);
  
  当我们需要删除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)

上一页 [1] [2]



文章整理:iocblog
版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。