【问题标题】:Use of double pointer in linux kernel Hash list implementationlinux内核哈希表实现中双指针的使用
【发布时间】:2010-06-17 02:51:21
【问题描述】:

我正在尝试了解链表和哈希表的 Linux 内核实现。实现的链接是here。我了解链表的实现。但是我对为什么在 hlist (**pprev) 中使用双指针有点困惑。 hlist 的链接是here。我知道 hlist 用于实现哈希表,因为列表的头部只需要一个指针并且它节省空间。为什么不能使用单指针来完成(就像链表一样 *prev )?请帮帮我。

【问题讨论】:

    标签: c linux linux-kernel


    【解决方案1】:

    原因可以在其中一个 cmets 中找到:

     547/*
     548 * Double linked lists with a single pointer list head.
     549 * Mostly useful for hash tables where the two pointer list head is
     550 * too wasteful.
     551 * You lose the ability to access the tail in O(1).
     552 */
    

    如果你有 *prev 而不是 **pprev,并且因为我们试图节省内存,所以我们不在头部包含 *prev,那么我们的 hlist 实现如下所示:

    struct hlist_head {
      struct hlist_node *first = null;
    };
    
    struct hlist_node {
      struct hlist_node *next;
      struct hlist_node *prev;
    };
    

    请注意prev 指针不能指向头部或head->first(与**pprev 不同)。这使 hlist 实现变得复杂,正如您在我们实现 hlist_add_before() 时所见:

    void
    hlist_init(struct hlist_head *head) {
      head->first = null;  
    }
    
    void
    hlist_add_head(struct hlist_head *head, struct hlist_node *node) {
      struct hlist_node *next = head->first;
    
      head->first = node;
      node->next = next;
      node->prev = NULL;
      if (next) {
        next->prev = node;
      }
    }
    

    请注意,prev 在上述hlist_add_head() 的实现中没有任何可指点。所以,现在当你实现hlist_add_before() 时,它看起来像这样:

    void
    hlist_add_before(struct hlist_head *head,
                     struct hlist_node *node,
                     struct hlist_next *next) {
      hlist_node *prev = next->prev;
    
      node->next = next;
      node->prev = prev;
      next->prev = node;
    
      if (prev) {
        prev->next = node;
      } else {
        head->first = node;
      }
    }
    

    请注意,现在我们需要将head 也传递给hlist_add_before(),这需要额外的push 指令来将head 压入堆栈。此外,在实现中还有一个额外的条件检查,这进一步减慢了速度。

    现在,尝试使用*prev 而不是**pprev 实现其他 hlist 操作,您会发现您的实现将比您在 linux 内核中看到的要慢。

    【讨论】:

    • 感谢您的回答。但我的疑问是为什么没有 *prev 并且有一个双向链表。使用它,您可以遍历两种方式。您可以在 O(1) 中添加和删除节点。两种情况使用的内存相同。我显然在这里遗漏了一些东西。你能指出我的错误吗?
    • 我无法通过使用 hlist 获得性能提升。你能解释一下吗?
    【解决方案2】:

    列表有两种类型:普通列表头类型和哈希列表(hlist)。 “list_head”类型只有一个结构,即“struct list_head”。但是 hlist 有两个结构,“struct hlist_head”和“struct hlist_node”。因此对于 hlist,当试图指向前一个元素时,该元素可以是“hlist_head”或“hlist_node”类型。我们不能使用一种常见的指针类型来指向两种元素类型。 (当然,我们可以使用void*,但它不是一个好的指针类型,需要在实际使用之前进行类型转换)所以解决方案是让“prev”指针指向一个常见的struct类型,即“struct hlist_node” *”。这种常见类型由 hlist_head 中的字段“first”和 hlist_node 中的字段“next”共享。因此“prev”指针变为“struct hlist_node **”,可以同时指向 hlist_head.first 和 hlist_node.next。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-16
      • 2013-06-03
      • 2014-05-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多