linux 核心hash_list學習筆記
阿新 • • 發佈:2019-01-26
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)
參考文件: