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

来源:互联网 发布:虚无世界用哪个java 编辑:程序博客网 时间:2024/06/01 08:23

对内核提供的散列表功能使用不是很熟悉,特分析进程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 0x9e370001ULstatic 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
原创粉丝点击