美文网首页
linux kernel 中的哈西表

linux kernel 中的哈西表

作者: cc14514 | 来源:发表于2023-04-24 15:25 被阅读0次

hash table 内核中的哈西表结构

常用 API

在 Linux 内核中,Hash Table 和 RCU 是经常一起使用的。Hash Table 主要用于快速查找元素,而 RCU 用于读操作的并发保护,以便多个线程能够同时访问 Hash Table。以下是 Hash Table 与 RCU 相关的一些 API:

  • hlist_add_head_rcu() 和 hlist_add_tail_rcu():在 Hash Table 中添加新节点,使用 RCU 确保并发读访问的正确性。

  • hlist_del_rcu():从 Hash Table 中删除一个节点,使用 RCU 确保并发读访问的正确性。

  • hlist_for_each_entry_rcu():使用 RCU 遍历 Hash Table,读取节点数据时使用 rcu_dereference() 等 RCU API 以确保正确性。

  • rcu_assign_pointer():用于使用 RCU 保护指针赋值操作。

  • synchronize_rcu():等待所有已经开始的 RCU 读操作完成,以便对 Hash Table 进行修改。

使用 Hash Table 时,需要特别注意并发读写的正确性,以避免数据不一致等问题。使用 RCU 可以有效地提高 Hash Table 的并发读取性能,并减少锁竞争。

demo

首先,定义一个哈希表结构体 my_file_ht,包含了一个哈希数组和一个自旋锁用于保护哈希表的并发操作。

#define HT_SIZE 1024

struct my_file_ht {
    struct hlist_head head[HT_SIZE];
    spinlock_t lock;
};

接着,定义一个缓存文件状态的结构体 my_file_ar,包含了文件描述符的 inode 号和状态掩码。

struct my_file_ar {
    __le32 ino;
    int mask;
    struct hlist_node node;
};

接下来,提供哈希表的初始化函数,初始化哈希表的每个哈希槽都为空链表,并初始化哈希表的自旋锁。

void my_file_ht_init(struct my_file_ht *ht)
{
    int i;
    for (i = 0; i < HT_SIZE; i++)
        INIT_HLIST_HEAD(&ht->head[i]);
    spin_lock_init(&ht->lock);
}

然后,提供一个添加节点的函数,使用哈希表的自旋锁来保护节点的添加操作。

void my_file_ht_add(struct my_file_ht *ht, struct my_file_ar *ar)
{
    unsigned int hash = my_file_ht_hashfn(ar->ino);
    spin_lock_bh(&ht->lock);
    hlist_add_head_rcu(&ar->node, &ht->head[hash]);
    spin_unlock_bh(&ht->lock);
}

接下来,提供一个查找节点的函数,使用 RCU 机制来保护节点的读操作。需要注意的是,这里使用了 hlist_for_each_entry_rcu 函数来遍历链表。

struct my_file_ar *my_file_ht_lookup(struct my_file_ht *ht, __le32 ino)
{
    unsigned int hash = my_file_ht_hashfn(ino);
    struct my_file_ar *ar = NULL;
    struct hlist_node *n;
    rcu_read_lock();
    hlist_for_each_entry_rcu(ar, n, &ht->head[hash], node) {
        if (ar->ino == ino) {
            rcu_read_unlock();
            return ar;
        }
    }
    rcu_read_unlock();
    return NULL;
}

最后,提供一个删除节点的函数,使用哈希表的自旋锁来保护节点的删除操作。

void my_file_ht_del(struct my_file_ht *ht, struct my_file_ar *ar)
{
    unsigned int hash = my_file_ht_hashfn(ar->ino);
    spin_lock_bh(&ht->lock);
    hlist_del_rcu(&ar->node);
    spin_unlock_bh(&ht->lock);
    call_rcu(&ar->rcu, my_file_ht_rcu_free);
}

同时,我们还需要提供一个回调函数 my_file_ht_rcu_free,用于释放节点内存空间:

static void my_file_ht_rcu_free(struct rcu_head *rcu)
{
    struct my_file_ar *ar = container_of(rcu, struct my_file_ar, rcu);
    kfree(ar);
}

在这个回调函数中,我们使用 container_of 宏将 RCU 头转换为节点指针,然后调用 kfree 函数释放节点内存空间。

这样,就完成了一个基于哈希表和 RCU 的缓存文件描述符及其状态的 demo。在多线程环境下,读操作不需要加锁,写操作只需要用自旋锁来保护并发修改,从而提高了读写操作的效率。

static inline unsigned int my_file_ht_hashfn(__le32 ino)
{
    return le32_to_cpu(ino) % HT_SIZE;
}

这个哈希函数使用了 inode 的值对哈希表大小进行求模,从而获得在哈希表中的索引值。其中,le32_to_cpu 用于将 inode 的字节序从小端转换为主机字节序,以便更好地进行哈希计算。

请注意,这个哈希函数只是一个简单的示例,实际应用中可能需要根据具体的场景来编写更加复杂的哈希函数。

copy : https://github.com/cc14514/notes/blob/main/linux-kernel/hash_table.md

相关文章

网友评论

      本文标题:linux kernel 中的哈西表

      本文链接:https://www.haomeiwen.com/subject/vgvujdtx.html