views:

101

answers:

1

Hi, I am trying to understand Linux Kernel implementation of linked list and hash table. A link to the implementation is here. I understood the linked list implementation. But i am little confused of why double pointers is being used in hlist (**pprev). Link for hlist is here. I understand that hlist is used in implementation of hash table since head of the list requires only one pointer and it saves space. Why cant it be done using single pointer (just *prev like the linked list)? Please help me.

+3  A: 

The reason can be found in one of the comments:

 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 */

If you had *prev instead of **pprev, and because we are trying to save memory, we don't include *prev in the head, then our hlist implementation looks like this:

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

struct hlist_node {
  struct hlist_node *next;
  struct hlist_node *prev;
};

Notice that the prev pointer cannot point to the head, or head->first (unlike **pprev). This complicates the hlist implementation, as you'll see when we implement 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;
  }
}

Notice that prev has nothing to point to, in the above imeplementation of hlist_add_head(). So, now when you implement hlist_add_before() it looks like this:

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;
  }
}

Notice that now we need to pass in head as well to hlist_add_before(), which requires an extra push instruction for pushing head on the stack. Besides, there's an extra conditional check in the implementation, which further slows down things.

Now, try implementing other hlist operations, with *prev instead of **pprev, and you'll find out that your implementation is going to be slower than what you saw in the linux kernel.

Sudhanshu
Thanks for your answer. But my doubt is why not have *prev and have a doubly linked list. Using this you can traverse both ways. You can add and delete nodes in O(1). The memory used is same for both cases. I am obviously missing something here. Could you please point out my mistake?
See, if my elaborated answer helps. :)
Sudhanshu
Thank you very much...