linux内核为了方便进行链表的操作,定义了一系列很方便的宏定义,在平时的项目开发中,应用宏定义能使得项目事半功倍,下面是内核实现源码的实现,仅重定义了名字,正在学习,就暂时记录下来吧:
结构定义:
typedef struct hlist_node
{
struct hlist_node *next; // 指向下一个结点的指针
struct hlist_node **pprev;// 指向上一个结点的next指针的地址
}HLIST_NODE_S;
typedef struct hlist_head
{
struct hlist_head *first; // 指向每一个hash桶的第一个结点的指针
}HLIST_HEAD_S;
初始化工作:
// 初始化hash桶的头结点
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
// 初始化hash桶的普通结点
static inline void INIT_HLIST_NODE(HLIST_NODE_S *node)
{
node->next = NULL;
node->pprev = NULL;
}
双链表基本操作
<1> 添加到头节点处
/**
* HLIST_Add_Head
* @n: the element to add to the hash list.
* @h: the list to add to.
*/
static inline void HLIST_Add_Head(HLIST_NODE_S *node, HLIST_HEAD_S *head)
{
HLIST_NODE_S *first = head->first;
node->next = first;
if (NULL != first)
{
first->pprev = &node->next;
}
head->first = node;
node->pprev = &head->first;
}
<2>添加到指定节点前
/* next must be != NULL */
/* node:要添加的新的节点。
* next:在next节点之前添加node。
* 在next节点的前面添加一个新的节点n,在使用这个函数中要特别注意,next不能为NULL。
*/
static inline void HLIST_Add_Before(HLIST_NODE_S *node, HLIST_NODE_S *next)
{
node->pprev = next->pprev;
node->next = next;
next->pprev = &node->next;
*(node->pprev) = node;
}
<3>添加到指定节点后
/* next must be != NULL */
/* node:要添加的新的节点。
* next:表示在next节点之后添加node。
* 在next 节点的后面添加一个新的节点node,这里也要求next不能为NULL
*/
static inline void HLIST_Add_After(HLIST_NODE_S *node, HLIST_NODE_S *next)
{
next->next = node->next;
node->next = next;
next->pprev = &node->next;
if(NULL != next->next)
{
next->next->pprev = &next->next;
}
}
<4>删除指定节点
/* node:要删除的节点。
* 对于删除操作的话,要注意node是不是末尾节点,如果是末尾节点的话,next就是NULL?
* 所以就没有指向的pprev,就更不能进行相应的修改了,否则进行修改。
*/
static inline void __hlist_del(HLIST_NODE_S *node)
{
HLIST_NODE_S *next = node->next;
HLIST_NODE_S **pprev = node->pprev;
*pprev = next;
if (NULL != next)
{
next->pprev = pprev;
}
}
/* node:要删除的节点。
* 在这个函数中首先删除了node节点,之后将n节点的两个指针指向了NULL,表示不可使用的地方
*/
static inline void HLIST_Del(HLIST_NODE_S *node)
{
__hlist_del(node);
node->next = NULL;
node->pprev = NULL;
}
<5>判断链表是否为NULL或节点是否在hash内
/*
* 判断一个结点是否已经存在于hash桶中
* 判断h->prev是不是为空,如果pprev的指向是空的话,表示这个节点没有添加到这个链表当中来,
* 如果是空,返回true,否则返回false
*/
static inline int HLIST_UnHashed(const HLIST_NODE_S *node)
{
return !node->pprev;
}
// 判断一个hash桶是否为空
/* head:struct hlist_head节点指针(hlist链表的头节点)。
* 判断hlist链表是不是空链表,如果是,返回true,否则返回false。
*/
static inline int HLIST_IsEmpty(const HLIST_HEAD_S *head)
{
return (NULL != head->first);
}
遍历hash_list
<1>两个方便的宏定义
/***********************************************************
* 遍历hash链表,需要用到两个地址偏移的宏定义 *
************************************************************/
//获取结构体成员相对于结构体的偏移
#define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER)
//通过获取结构体中的某个成员,反推该结构体的指针
#define container_of(ptr, type , member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr) ; \
(type *)((char *)__mptr - offsetof(type,member)) ;})
<2>基本遍历,用于查找
/* pos:struct hlist_node类型的一个指针;
* head:struct hlist_head类型的一个指针,表示hlist链表的头结点。
* 这个实际上就是一个for循环,从头到尾遍历链表。
*/
/* 普通遍历,遍历过程中不能删除节点,否则可能会出现后续节点的访问错误 */
#define HLIST_FOR_EACH(pos, head) \
for (pos = (head)->first; pos != NULL ; 1; }); \
pos = pos->next)
<3>安全遍历,用于遍历中删除
/* 这个实际上就是一个for循环,从头到尾遍历链表。这个和前面的不同的是多了一个n,
* 这么做是为了遍历过程中防止断链的发生。删除时用这个。
* pos:struct hlist_node类型的一个指针;
* next:struct hlist_node类型的一个指针,这里为区分,写作next;
* head:struct hlist_head类型的一个指针,表示hlist链表的头结点。
这是安全遍历,实际上就是预读取,提前指向下一个节点,遍历过程中可以删除节点
*/
#define HLIST_FOR_EACH_SAFE(pos, next, head) \
for (pos = (head)->first; pos && ({ next = pos->next; 1; }); \
pos = next)
<4>访问元素基本遍历:访问链表所在结构体其他成员
/* ptype:用来存放遍历到的数据结构的地址,类型是type *;
* pos:struct hlist_node类型的一个指针;
* head:hlist链表的头结点;
* member:struct hlist_node在type结构体中的变量的名称。
* 通过hlist_entry 这个宏,我们可以访问hash链表所在结构体的其他成员,很多时候需要这样访问。
本遍历宏并非安全定义,遍历过程中不能删除节点
*/
/**
* HLIST_FOR_EACH_ENTRY - iterate over list of given type
* @ptype: the type * to use as a loop cursor.
* @pos: the &HLIST_NODE_S to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define HLIST_FOR_EACH_ENTRY(ptype, pos, head, member) \
for (pos = (head)->first; \
(pos != NULL) && \
({ ptype = hlist_entry(pos, typeof(*ptype), member); 1;}); \
pos = pos->next)
<5>访问元素安全遍历,用于遍历过程中删除节点
/* ptype:用来存放遍历到的数据结构的地址,类型是type *;
* pos:struct hlist_node类型的一个指针;
* n:struct hlist_node类型的一个指针;
* head:hlist链表的头结点;
* member:struct hlist_node在type结构体中的变量的名称。
* 在循环中,我们就可以使用tops来指向type
* 类型结构体的任何一个变量了。这个宏函数也是为了防止在遍历的时候删除节点而引入的。
*/
/**
* 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 cursor.
* @pos: the &HLIST_NODE_S to use as a loop cursor.
* @n: another &HLIST_NODE_S 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(ptype, pos, next, head, memsber) \
for (pos = (head)->first; \
pos && ({ next = pos->next; 1; }) && \
({ ptype = hlist_entry(pos, typeof(*ptype), member); 1;}); \
pos = next)
参考文档: