刚刚把hlist有关的函数和宏定义都过了一遍,在此做了一下整理。希望对大家以后学习linux有用,也欢迎大家来拍砖

 

 

/*Linux链表设计者(认为双头(next、prev)的双链表对于HASH表来说"过于浪费",因而另行设计了一套用于HASH表应用的hlist数据结构--单指针表头双循环链表,hlist的表头仅有一个指向首节点的指针,而没有指向尾节点的指针,这样在可能是海量的HASH表中存储的表头就能减少一半的空间消耗。

*/

 

//HASH LIST

//表头结点(不同于结点的数据结构)

struct hlist_head {                          
        struct hlist_node *first;
};


 

/*pprev首先也是一个指针,不过它是指向指针的指针(这点请仔细体会),在hlist中pprev指向的是当前hlist_node变量中的前一个变量的next的地址,
如果是第一个元素的话,这个值指向的是first的地址,如果是最后一个节点的话,指向的是NULL。如果再深入理解一下,
其实*pprev和next这两个变量分别表示指向自己的指针和指向其他节点的指针。
*/

struct hlist_node {                           //结点
        struct hlist_node *next, **pprev;
};

 

//给struct hlist_head变量来进行初始化的。

#define HLIST_HEAD_INIT { .first = NULL }


//定义一个一个struct hlist_head节点,并且给其初始化为NULL

#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }


//给struct hlist_head节点进行初始化的。

#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)


//这个宏函数是对struct hlist_node节点的一个变量进行初始化的操作。这个宏用在删除节点之后对节点的操作当中。

#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)


 

/*h:struct hlist_node节点指针。
判断h->prev是不是为空,如果pprev的指向是空的话,表示这个节点没有添加到这个链表当中来,如果是空,返回true,否则返回false*/

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


/*h:struct hlist_head节点指针(hlist链表的头节点)。
判断hlist链表是不是空链表,如果是,返回true,否则返回false。
*/

static inline int hlist_empty(const struct hlist_head *h)
{
        return !h->first;
}


 

/*真正意义上实现删除操作的函数。
n:要删除的节点。
对于删除操作的话,要注意n是不是末尾节点,如果是末尾节点的话,next就是NULL,所以就没有指向的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;
}


/*n:要删除的节点。
在这个函数中首先删除了n节点,之后将n节点的两个指针指向了LIST_POSION,表示不可使用的地方
*/

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


/*n:要删除的节点。
首先判断要删除的pprev是不是空,如果是,则不能删除,否则进行删除操作,删除完成之后,将n节点的next和pprev都指向NULL(初始化节点)。
*/

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


 

/*
n:要添加的新的节点。
h:hlist链表的表头节点。
这个函数是给h的下一个和first节点中添加一个新的hlist_node节点,类似与头插。
*/

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



/* next must be != NULL */
/*
n:要添加的新的节点。
next:在next节点之前添加n。
在next节点的前面添加一个新的节点n,在使用这个函数中要特别注意,next不能为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;
}



/*n:表示在n节点之后添加next。
next:要添加的新的节点。
在n节点的后面添加一个新的节点next,这里也要求n不能为NULL
*/

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


 

/*说明:有关hlist中的宏定义与list中的宏定义大同小异,所以在此只是简单分析,具体分析见上面代码*/


/*ptr:表示struct hlist_node类型的一个地址。
type:结构体名
member:type结构体中的hlist_node成员变量的名称
这个宏在list链表中分析过了,表示得到ptr所指地址的这个结构体的首地址
*/

#define hlist_entry(ptr, type, member) container_of(ptr,type,member)


 



/*pos:struct hlist_node类型的一个指针;
head:struct hlist_head类型的一个指针,表示hlist链表的头结点。
这个实际上就是一个for循环,从头到尾遍历链表。
*/

#define hlist_for_each(pos, head) \
        for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
             pos = pos->next)


/*这个实际上就是一个for循环,从头到尾遍历链表。这个和前面的不同的是多了一个n,这么做是为了遍历过程中防止断链的发生。
pos:struct hlist_node类型的一个指针;
n:struct hlist_node类型的一个指针;
head:struct hlist_head类型的一个指针,表示hlist链表的头结点。
*/   

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


/*tops:用来存放遍历到的数据结构的地址,类型是type *;
pos:struct hlist_node类型的一个指针;
head:hlist链表的头结点;
member:struct hlist_node在type结构体中的变量的名称。
在循环中,我们就可以使用tops来指向type类型结构体的任何一个变量了。
*/
   

#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)


   
/*tops:用来存放遍历到的数据结构的地址,类型是type *;
pos:struct hlist_node类型的一个指针;
member:struct hlist_node在type结构体中的变量的名称。
这个宏是从pos这个地址的下一个元素处开始继续往下遍历。
*/   

#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)


   
/*tops:用来存放遍历到的数据结构的地址,类型是type *;
pos:struct hlist_node类型的一个指针;
member:struct hlist_node在type结构体中的变量的名称。
这个宏是从pos这个地址处开始继续往下遍历。
*/   

#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)


   
/*tops:用来存放遍历到的数据结构的地址,类型是type *;
pos:struct hlist_node类型的一个指针;
n:struct hlist_node类型的一个指针;
head:hlist链表的头结点;
member:struct hlist_node在type结构体中的变量的名称。
在循环中,我们就可以使用tops来指向type类型结构体的任何一个变量了。这个宏函数也是为了防止在遍历的时候删除节点而引入的。
*/

#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)

#endif




 

 


 

Logo

更多推荐