linux内核学习中--“list.h”文件中 hlist所有函数与宏定义的详解
刚刚把hlist有关的函数和宏定义都过了一遍,在此做了一下整理。希望对大家以后学习linux有用,也欢迎大家来拍砖 /*Linux链表设计者(认为双头(next、prev)的双链表对于HASH表来说"过于浪费",因而另行设计了一套用于HASH表应用的hlist数据结构--单指针表头双循环链表,hlist的表头仅有一个指向首节点的指针,而没有指向尾节点的指针,这样在可能是海量的HASH
刚刚把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
更多推荐
所有评论(0)