linux内核中hlist_head和hlist_node结构解析

转自https://www.cnblogs.com/zafu/p/7412424.html

hlist_headhlist_node用于散列表,分表表示列表头(数组中的一项)和列表头所在双向链表中的某项,两者结构如下:

1

2

3

struct hlist_head {

struct hlist_node *first;

};

1

2

3

struct hlist_node {

struct hlist_node *next, **pprev;

};

在内核中的普通双向链表基本上都是通过list_head实现的:

1

2

3

struct list_head {

struct list_head *next, *prev;

};

list_head很好理解,但是hlist_headhlist_node为何要这样设计呢?

先看下hlist_headhlist_node的示意图:

hash_table为散列表(数组),其中的元素类型为struct hlist_head。以hlist_head为链表头的链表,其中的节点hash值是相同的(也叫冲突)。first指针指向链表中的节点①,然后节点①的pprev指针指向hlist_head中的first,节点①的next指针指向节点②。以此类推。

hash_table的列表头仅存放一个指针,也就是first指针,指向的是对应链表的头结点,没有tail指针也就是指向链表尾节点的指针,这样的考虑是为了节省空间——尤其在hash bucket(数组size)很大的情况下可以节省一半的指针空间。

为什么pprev是一个指向指针的指针呢?按照这个设计,我们如果想要得到尾节点,必须遍历整个链表,可如果是一个指向节点的指针,那么头结点现在的pprev便可以直接指向尾节点,也就是list_head的做法。

对于散列表来说,一般发生冲突的情况并不多(除非hash设计出现了问题),所以一个链表中的元素数量比较有限,遍历的劣势基本可以忽略。

在删除链表头结点的时候,pprev这个设计无需判断删除的节点是否为头结点。如果是普通双向链表的设计,那么删除头结点之后,hlist_head中的first指针需要指向新的头结点。

 

Linux内核中,`hlist_head` `hlist_node` 结构常用于实现哈希链表的查找。这里提供一个简化版的示例,假设我们有一个简单的哈希表结构 `MyHashTable` 相关的节点结构 `MyHashNode`,它们使用 `hlist_node` 来存储数据: ```c #include <linux/list.h> // 哈希节点结构 typedef struct MyHashNode { char key; // 这里假设key是我们哈希的关键字 struct hlist_node node; } MyHashNode; // 哈希表结构 typedef struct MyHashTable { size_t size; // 哈希表大小 struct hlist_head *buckets; // 每个桶是一个hlist_head } MyHashTable; // 初始化哈希表 void my_hash_table_init(MyHashTable *table, size_t size) { table->size = size; table->buckets = kcalloc(size, sizeof(struct hlist_head), GFP_KERNEL); if (!table->buckets) panic("Failed to allocate memory for buckets"); } // 计算键值的哈希索引 static inline int hash_key(const char *key, size_t size) { return (unsigned long)key % table->size; } // 插入节点到哈希表 void my_hash_table_insert(MyHashTable *table, const char *key, void *data) { MyHashNode *node = kmalloc(sizeof(MyHashNode), GFP_KERNEL); if (!node) return; node->key = *key; // 将数据复制到节点 memcpy(&node->data, data, sizeof(void*)); // 假设数据是void* int index = hash_key(key, strlen(key)); hlist_add_tail(&node->node, &table->buckets[index]); } // 查找节点(在这里只是一个基本的例子,实际查找需要遍历链表) void *my_hash_table_search(MyHashTable *table, const char *key) { int index = hash_key(key, strlen(key)); struct hlist_node *node = table->buckets[index].first; while (node) { MyHashNode *hash_node = container_of(node, MyHashNode, node); if (hash_node->key == *key) { return hash_node->data; // 如果找到匹配,返回数据 } node = node->next; } return NULL; // 没有找到匹配 } // 示例用法 void main() { MyHashTable table; my_hash_table_init(&table, 10); // 插入一些项 my_hash_table_insert(&table, "A", (void*)0x123); my_hash_table_insert(&table, "B", (void*)0x456); // 查找并打印结果 void *found_data = my_hash_table_search(&table, "A"); if (found_data) { printk(KERN_INFO "Found key 'A': 0x%p\n", found_data); } else { printk(KERN_INFO "Key 'A' not found.\n"); } } ``` 注意这仅是一个简化的示例,实际应用中可能需要处理更多的边界情况错误处理。此外,`hlist_head` 的操作通常在中断上下文中不是安全的,因此在实际的内核代码中可能会有所调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值