内核hlist使用之pidhash散列表使用分析

对内核提供的散列表功能使用不是很熟悉,特分析进程pid管理对hlist的使用,进一步加深熟悉和理解。

先贴一下内核数据结构的定义

<span style="font-size:12px;"><list.h>

struct hlist_head {
        struct hlist_node *first;
};

struct hlist_node {
        struct hlist_node *next, **pprev;
};

#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)</span>

静态定义可以使用HLIST_HEAD,动态初始化使用INIT_HLIST_HEAD

内核管理pid使用的散列表通过全局变量pid_hash定义,通过pidhash_init()分配

<kernel/pid.c>

static struct hlist_head *pid_hash;
static int pidhash_shift;

/*
 * The pid hash table is scaled according to the amount of memory in the
 * machine.  From a minimum of 16 slots up to 4096 slots at one gigabyte or
 * more.
 */
void __init pidhash_init(void)
{
        int i, pidhash_size;
        unsigned long megabytes = nr_kernel_pages >> (20 - PAGE_SHIFT);

        pidhash_shift = max(4, fls(megabytes * 4));
        pidhash_shift = min(12, pidhash_shift);
        pidhash_size = 1 << pidhash_shift;

        printk("PID hash table entries: %d (order: %d, %Zd bytes)\n",
                pidhash_size, pidhash_shift,
                pidhash_size * sizeof(struct hlist_head));

        pid_hash = alloc_bootmem(pidhash_size * sizeof(*(pid_hash)));
        if (!pid_hash)
                panic("Could not alloc pidhash!\n");
        for (i = 0; i < pidhash_size; i++)
                INIT_HLIST_HEAD(&pid_hash[i]);
}

根据内核能够管理的内存页数nr_kernel_pages来计算分配2^pidhash_shift个hlist_head结构体,最少2^4最多2^12个

接下来看散列函数这个是关键

#define pid_hashfn(nr, ns)      \
        hash_long((unsigned long)nr + (unsigned long)ns, pidhash_shift)

/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
#define GOLDEN_RATIO_PRIME 0x9e370001UL

static inline unsigned long hash_long(unsigned long val, unsigned int bits)
{
        unsigned long hash = val;
          
        /* On some cpus multiply is faster, on others gcc will do shifts */
        hash *= GOLDEN_RATIO_PRIME;  

        /* High bits are more random, so use them. */
        return hash >> (BITS_PER_LONG - bits);
}
本质就是将nr与ns的和乘上一个合适的值,然后取高pidhash_shift位,这个值做为表头数组的索引

插入和查找涉及的结构体定义如下

   <pid.h>  
    enum pid_type  
    {  
        PIDTYPE_PID,  
        PIDTYPE_PGID,  
        PIDTYPE_SID,  
        PIDTYPE_MAX  
    };  
      
    struct upid {  
        /* Try to keep pid_chain in the same cacheline as nr for find_vpid */  
        int nr;  
        struct pid_namespace *ns;  
        struct hlist_node pid_chain;  
    };  
      
    struct pid  
    {  
        atomic_t count;  
        unsigned int level;  
        /* lists of tasks that use this pid */  
        struct hlist_head tasks[PIDTYPE_MAX];  
        struct rcu_head rcu;  
        struct upid numbers[1];  
    };  

1. 插入操作

alloc_pid函数用于创建struct pid结构体实例

<pid.c>

struct pid *alloc_pid(struct pid_namespace *ns)
{
        struct pid *pid;
        enum pid_type type;
        int i, nr;
        struct pid_namespace *tmp;
        struct upid *upid;

        pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
        if (!pid)
                goto out;

        tmp = ns;
        for (i = ns->level; i >= 0; i--) {
                nr = alloc_pidmap(tmp);
                if (nr < 0)
                        goto out_free;

                pid->numbers[i].nr = nr;
                pid->numbers[i].ns = tmp;
                tmp = tmp->parent;
        }

        get_pid_ns(ns);
        pid->level = ns->level;
        atomic_set(&pid->count, 1);
        for (type = 0; type < PIDTYPE_MAX; ++type)
                INIT_HLIST_HEAD(&pid->tasks[type]);

        spin_lock_irq(&pidmap_lock);
        for (i = ns->level; i >= 0; i--) {
                upid = &pid->numbers[i];
                hlist_add_head_rcu(&upid->pid_chain,
                                &pid_hash[pid_hashfn(upid->nr, upid->ns)]);
        }
        spin_unlock_irq(&pidmap_lock);

out:
        return pid;

out_free:
        for (i++; i <= ns->level; i++)
                free_pidmap(pid->numbers[i].ns, pid->numbers[i].nr);

        kmem_cache_free(ns->pid_cachep, pid);
        pid = NULL;
        goto out;
}

2.查找操作

<pid.c>

struct pid * fastcall find_pid_ns(int nr, struct pid_namespace *ns)
{
        struct hlist_node *elem;
        struct upid *pnr;

        hlist_for_each_entry_rcu(pnr, elem,
                        &pid_hash[pid_hashfn(nr, ns)], pid_chain)
                if (pnr->nr == nr && pnr->ns == ns)
                        return container_of(pnr, struct pid,
                                        numbers[ns->level]);

        return NULL;
}

总结:hlist的使用需要(1)分配hlist_head数组 (2)定义散列函数,剩下的操作跟双向链表相同


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值