zoukankan      html  css  js  c++  java
  • Linux内核hlist数据结构分析


         在内核编程中哈希链表hlist使用非常多,比方在openvswitch中流表的存储中就使用了(见[1])。hlist的表头仅有一个指向首节点的指针。而没有指向尾节点的指针,这样在有非常多个buckets的HASH表中存储的表头就能降低一半的空间消耗。
         和hlist相关的数据结构例如以下,桶中存储的 hlist_head 是具有同样hash值的entry构成的链表。每一个entry包括一个 hlist_node 成员,通过它链入到这个哈希链表中。
    struct hlist_head {
          struct hlist_node * first;
    };

    //next指向下一个节点
    // pprev指向前一个节点的next域
    struct hlist_node {
          struct hlist_node * next, ** pprev;
    };

    结构图为:


    因为头结点和其它节点的类型不一致。这样就不能使用普通的prev指针指向前一个节点(否则处理的时候还要讨论是否是第一个节点,没有通用性),这里设计者的巧妙之处就是pprev指针指向前一个节点的next,统一了兴许全部的节点。

    一些实用的宏:
    //头结点初始化
    #define HLIST_HEAD_INIT { .first = NULL }
    //构造一个名为name的头结点
    #define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }

    //初始化头指针,链表指针
    #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
    #define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)

    1.删除节点
    next得到当前节点的下一个节点。pprev是前一个节点的next字段的地址,那么*pprev就指向的是当前这个节点,那么 *pprev=next 就把当前节点更新为下一个节点了。假设n不是最后一个节点,还要设置next->pprev.
    static inline void __hlist_del (struct hlist_node *n)
    {
          struct hlist_node *next = n-> next;
          struct hlist_node **pprev = n-> pprev;
         *pprev = next;
          if (next)
              next-> pprev = pprev;
    }

    static inline void hlist_del (struct hlist_node *n)
    {
         __hlist_del(n);
         n-> next = LIST_POISON1;
         n-> pprev = LIST_POISON2;
    }

    2.插入节点
    (1)头插入:让插入的节点成为链表的第一个节点,依次更新对应的指针。示意图例如以下。
    static inline void hlist_add_head (struct hlist_node *n, struct hlist_head *h)
    {
          struct hlist_node *first = h-> first;
         n-> next = first;
          if (first)
              first-> pprev = &n-> next;
         h-> first = n;
         n-> pprev = &h-> first;
    }
    (2)在已知节点next之前/之后插入,通过自己绘图。非常easy理解清楚。
    /* next must be != NULL */
    static inline void hlist_add_before (struct hlist_node *n,
                             struct hlist_node *next)
    {
         n-> pprev = next->pprev ;
         n-> next = next;
         next-> pprev = &n-> next;
         *(n-> pprev) = n;
    }

    static inline void hlist_add_after (struct hlist_node *n,
                             struct hlist_node *next)
    {
         next-> next = n-> next;
         n-> next = next;
         next-> pprev = &n-> next;

          if(next-> next)
              next-> next-> pprev  = &next-> next;
    }

    3.通过看一个节点h的pprev是否为空。推断其是否在哈希链表中。

    static inline int hlist_unhashed (const struct hlist_node *h)
    {
          return !h-> pprev;
    }

    4.哈希链表的遍历(iterate)相关代码
    //通过一个字段member的地址 ptr。得到包括它的容器的地址
    #define hlist_entry(ptr, type, member) container_of(ptr,type,member)

    //用 pos作为游标来遍历这个链表, prefetch是数据预取
    #define hlist_for_each(pos, head)
          for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; });
              pos = pos->next)

    #define hlist_for_each_safe(pos, n, head)
          for (pos = (head)->first; pos && ({ n = pos->next; 1; });
              pos = n)

    //通用的哈希链表遍历,当中 pos指向当前节点。 tpos指向的包括hlist_node的当前结构体的指针
    #define hlist_for_each_entry(tpos, pos, head, member)              
          for (pos = (head)->first;                       
              pos && ({ prefetch(pos->next); 1;}) &&          
              ({ tpos = hlist_entry(pos, typeof (*tpos), member); 1;});
              pos = pos->next)

    /**
     * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
     * @ tpos: the type * to use as a loop counter.
     * @ pos:  the &struct hlist_node to use as a loop counter.
     * @member:   the name of the hlist_node within the struct.
     */
    #define hlist_for_each_entry_continue(tpos, pos, member)          
          for (pos = (pos)->next;                         
              pos && ({ prefetch(pos->next); 1;}) &&          
              ({ tpos = hlist_entry(pos, typeof (*tpos), member); 1;});
              pos = pos->next)

    /**
     * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
     * @ tpos: the type * to use as a loop counter.
     * @ pos:  the &struct hlist_node to use as a loop counter.
     * @member:   the name of the hlist_node within the struct.
     */
    #define hlist_for_each_entry_from(tpos, pos, member)           
          for (; pos && ({ prefetch(pos->next); 1;}) &&             
              ({ tpos = hlist_entry(pos, typeof (*tpos), member); 1;});
              pos = pos->next)

    /**
     * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
     * @ tpos: the type * to use as a loop counter.
     * @ pos:  the &struct hlist_node to use as a loop counter.
     * @n:        another &struct hlist_node to use as temporary storage
     * @head: the head for your list.
     * @member:   the name of the hlist_node within the struct.
     */
    #define hlist_for_each_entry_safe(tpos, pos, n, head, member)       
          for (pos = (head)->first;                       
              pos && ({ n = pos->next; 1; }) &&                    
              ({ tpos = hlist_entry(pos, typeof (*tpos), member); 1;});
              pos = n)





  • 相关阅读:
    oracle minus 与sqlserver except
    Ext.form.FieldSetI(转)
    C#系统服务定时执行(转)
    extjs4.0的数据代理proxy及数据模型的使用(转)
    JS属性defer的好处及IE8 提示 KB927917, IE6 IE7 提示操作已中止的解决办法
    水晶报表打印及多个报表打印到一个PDF文件里的办法
    C#不添加引用,动态调用webservice(转)
    向 ReportViewer 报表中添加页眉和页脚,控制页眉显示变量的值
    网站主要使用jquery总结(转)
    JSBuilder2使用方法(转)
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/5394727.html
Copyright © 2011-2022 走看看