linux 内核hash_list学习笔记

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)

参考文档:

https://blog.csdn.net/zhanglei4214/article/details/6767288

http://blog.chinaunix.net/uid-28458801-id-4276934.html

猜你喜欢

转载自blog.csdn.net/qq_33195791/article/details/81291721