Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3045531
  • 博文数量: 396
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4209
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-04 13:04
文章分类

全部博文(396)

文章存档

2022年(1)

2021年(2)

2020年(8)

2019年(24)

2018年(135)

2017年(158)

2016年(68)

我的朋友

分类: 嵌入式

2016-09-23 10:23:46

 最近研究进程间通信,遇到了idr相关的函数,为了扫清障碍,先研究了linux的idr机制。

        所谓IDR,其实就是和身份证的含义差不多,我们知道,每个人有一个身份证,身份证只是 一串数字,从数字,我们就能知道这个人的信息。同样道理,idr的要完成的任务是给要管理的对象分配一个数字,可以通过这个数字找到要管理的对象。

 ID -------------------->struct obj* 


应用IDR机制时要包含头文件

struct idr {
struct idr_layer *top;  //idr的top层,可以方便的理解为根节点。
struct idr_layer *id_free; //id_free为首的形成一个链表,这个是预备队,
                                           //并没有参与到top为根的节点中去
int  layers; //当前的层数。
int  id_free_cnt;// 预备队的个数。
spinlock_t  lock;
};


struct idr_layer {
unsigned long bitmap; /* A zero bit means "space here" */
struct idr_layer*ary[1<
int count; /* When zero, we can release it */
};

      IDR_BITS 在32位操作系统是5 ,64位操作系统是6,我们以32位操作系统为例。

      本文的介绍以两层的为例。layers = 2.如下图所示

      idr中的top指向的是当前正在工作的最高层的idr_layer,即图中的A,top的ary是个指针数组,指向
低一层的idr_layer。top层ary指针数组不一定都指向已经分配了的低一层idr_layer。也可能某个指针指
向NULL。如下图的ary[1]就指向NULL。

       最后一层idr_layer 叶子层  例如B,他的指针数组ary中的元素,如果分配出去了那么指向某个结构体的地址,这个地址指向要管理的数据结构。如果没有分配出去,指针指向NULL。 对于叶子层而言,判断指针数组某个元素是否指向有意义的数据结构,用位图bitmap。bitmap对应的位 是1,表示ary数组的对应元素指向某有意义的数据结构。

       最后一层的bitmap的含义已经介绍,但是top层(或者层数大于2的时候,中间某层)bitmap的含义是什么呢?以两层为例,如果图中B的 bitmap是0xFFFFFFFF,即每一个指针都分配出去了,那么A的bitmap的第0位置1.同样如果A的bitmap的第2位是1,表示 ary[2]指向的C的bitmap是0xFFFFFFFF,即C也ary数组也分配完毕。

      这部分是函数idr_mark_full来实现。

static void idr_mark_full(struct idr_layer **pa, int id)
{
struct idr_layer *p = pa[0];
int l = 0;

__set_bit(id & IDR_MASK, &p->bitmap);// 叶子层数字id对应的位 置1.
/*
 * If this layer is full mark the bit in the layer above to
 * show that this part of the radix tree is full.  This may
 * complete the layer above and require walking up the radix
 * tree.
 */
while (p->bitmap == IDR_FULL) {
if (!(p = pa[++l]))              // pa[++l]记录的上一层idr_layer。
break;
id = id >> IDR_BITS;
__set_bit((id & IDR_MASK), &p->bitmap); //如果由于本层满了,则上一层对应位置1.
                                                                           //循环检测。
}
}


      介绍完负责工作的部分,下面介绍预备役。所谓预备役就是id_free指向的空闲的idr_layer。所谓空闲是指,这些idr_layer并没有投 入。如果需要分配一个idr_layer,首先将id_free指向的idr_layer取出来使用,同时id_free指向下一个。即如下图所示,如果 需要分配,D被取出来使用,同时id_free指针指向E,同时id_freecnt减一。
     将预备役投入使用是函数alloc_layer完成的。
      
static struct idr_layer *alloc_layer(struct idr *idp)
{
struct idr_layer *p;
unsigned long flags;

spin_lock_irqsave(&idp->lock, flags);
if ((p = idp->id_free)) {   
idp->id_free = p->ary[0]; // id_free 指向D的下一位 E 
idp->id_free_cnt--;         // 预备役的个数减1
p->ary[0] = NULL;          //D要被使用了,第0个指针不再指向E,初始化为NULL
}
spin_unlock_irqrestore(&idp->lock, flags);
return(p);  // 返回D
}


     有个问题是预备役是怎么来的,如果预备役分配光了怎么办。分配光了也没有关系,还好我们有idr_pre_get函数。

#if BITS_PER_LONG == 32
# define IDR_BITS 5

 #define MAX_ID_SHIFT (sizeof(int)*8 - 1)               //31
#define MAX_LEVEL         (MAX_ID_SHIFT + IDR_BITS - 1) / IDR_BITS              //7
#define IDR_FREE_MAX      MAX_LEVEL + MAX_LEVEL                                 //14

      坦白说,MAX_LEVEL的含义是什么,我并不清楚。为什么一次分配14个idr_layer充当预备役我并不清楚。请清楚的兄弟不吝赐教。
      这个函数的含义就是我要分配14个idr_layer,充当预备役。如果中间分配失败,那么能分配几个算几个。投入预备役的函数是free_layer。比较好懂我就不解释了。
     
   
int idr_pre_get(struct idr *idp, gfp_t gfp_mask)
{
while (idp->id_free_cnt < IDR_FREE_MAX) {
struct idr_layer *new;
new = kmem_cache_alloc(idr_layer_cache, gfp_mask);
if (new == NULL)
return (0);
free_layer(idp, new);
}
return 1;
}

static void free_layer(struct idr *idp, struct idr_layer *p)
{
unsigned long flags;

/*
 * Depends on the return element being zeroed.
 */
spin_lock_irqsave(&idp->lock, flags);
__free_layer(idp, p);
spin_unlock_irqrestore(&idp->lock, flags);
}

static void __free_layer(struct idr *idp, struct idr_layer *p)
{
p->ary[0] = idp->id_free;
idp->id_free = p; 
idp->id_free_cnt++; 
}

    从预备役机制上看,我们可以得到使用idr编程流程应该是这样的。
    首先调用idr_pre_get,来分配可用的idr_layer,投入预备役,接下来调用idr_get_new,
给要管理的对象target分配一个数字id,这个过程中可能会调用alloc_layer,将预备役中的
idr_layer投入使用,用在top为根管理结构中。  终有一天,预备役也被打光了idr_get_new
函数返回-EAGAIN,告诉我们,预备役全部阵亡,于是,我们从-EAGAIN的遗言中,知道,我们需要调用
idr_pre_get来充实预备役了。         

again: if (idr_pre_get(&my_idr, GFP_KERNEL) == 0) {
            /* No memory, give up entirely */ 
       } 
       spin_lock(&my_lock); 
       result = idr_get_new(&my_idr, &target, &id); 
       if (result == -EAGAIN) { 
                 sigh(); 
                 spin_unlock(&my_lock);
                 goto again;
       }


终于终于无限接近我们的真正想干的事情了。下面我们介绍如何将要管理的数据结构和某个ID组合起来。
太晚了,下一篇博文介绍。



IDR--Integer ID Management

IDR是用来管理小整形ID数的库函数集合。这些代码最早是在2003年2月作为POSIX clocks补丁的一部分加入的;现在,在内核的许多地方都可以看到IDR的应用。IDR算法的实现采用了radix tree的思想,从本质上讲,IDR是一个稀疏数组,能将整形ID号映射到任意指针上。

应用IDR机制时要包含头文件(注:本文内核版本为2.6.32.10),IDR结构体如下
struct idr {
    struct idr_layer *top; //IDR顶层
    struct idr_layer *id_free; //预留的可用idr layer指针
    int          layers; /* only valid without concurrent changes */ //IDR树的层数
    int          id_free_cnt; //预留的可用idr layer数目
    spinlock_t      lock; //锁保护
};
而idr layer的结构体如下
struct idr_layer {
    unsigned long         bitmap; /* A zero bit means "space here" */ //节点的位图,0表示slot槽空闲可用
    struct idr_layer    *ary[1<     int             count;     /* When zero, we can release it *///与bitmap,ary对应,表示已标注的slot槽数目
    int             layer;     /* distance from leaf */ //与叶子节点的距离,以0开始
    struct rcu_head         rcu_head;//rcu锁
};

首先创建一个IDR对象需要定义一个struct idr,并将指针传给函数void idr_init(struct idr *idp);一般采用快捷方式DEFINE_IDR(name)来创建,定义如下
#define IDR_INIT(name)                        \
{                                \
    .top        = NULL,                    \
    .id_free    = NULL,                    \
    .layers     = 0,                    \
    .id_free_cnt    = 0,                    \
    .lock        = __SPIN_LOCK_UNLOCKED(name.lock),    \
}
#define DEFINE_IDR(name)    struct idr name = IDR_INIT(name)

 

要获得一个ID号要分两个步骤,首先分配内存,其次是获得ID号;因此,先调用函数
int idr_pre_get(struct idr *idp, gfp_t gfp_mask);
注意该函数会导致睡眠,因此不应该用锁保护,函数实现如下
int idr_pre_get(struct idr *idp, gfp_t gfp_mask)
{
//通过kmem_cache_zalloc函数申请足够的内存空间,IDR_FREE_MAX定义为2倍的MAX_LEVEL,即14;所以一共预申请了14个idr_layer内存空间
    while (idp->id_free_cnt < IDR_FREE_MAX) {
        struct idr_layer *new;
        new = kmem_cache_zalloc(idr_layer_cache, gfp_mask);
        if (new == NULL)
            return (0);
        //该函数将预申请的idr_layer内存放入可用链表中,具体分析如下
        move_to_free_list(idp, new);
    }
    return 1;
}

static void move_to_free_list(struct idr *idp, struct idr_layer *p)
{
    unsigned long flags;

    /*
     * Depends on the return element being zeroed.
     */
    spin_lock_irqsave(&idp->lock, flags);
    __move_to_free_list(idp, p); //真正调用的函数
    spin_unlock_irqrestore(&idp->lock, flags);
}
static void __move_to_free_list(struct idr *idp, struct idr_layer *p)
{
    p->ary[0] = idp->id_free;
    idp->id_free = p;
    idp->id_free_cnt++;
}
在初始化创建IDR对象时,top设为NULL,id_free设为NULL,id_free_cnt设为0,在将第一个idr_layer移入链表时, 该idr_layer第一个slot槽(ary[0])指向id_free(NULL),而id_free指向新加入的 idr_layer,id_free_cnt加1,如下图所示

 

第二个idr_layer移入链表时,该idr_layer第一个slot槽(ary[0])指向第一个idr_layer,而id_free指向第二个 idr_layer,id_free_cnt自加到2;一次移入第三个,第四个,…,直到第十四个idr_layer为止,最终id_free指向最后一 个idr_layer,id_free_cnt为14,如下图所示

调用函数idr_pre_get成功后,将返回1,若申请不到内存则返回0;接下来,可通过下面的任一函数来获得ID号:
int idr_get_new(struct idr *idp, void *ptr, int *id);
int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id);
这两个函数的区别在于idr_get_new函数是从0开始申请ID号,而idr_get_new_above是从starting_id开始申请ID号,这里我们将只分析第一个函数:
int idr_get_new(struct idr *idp, void *ptr, int *id)
{
    int rv;
//具体调用idr_get_new_above_int函数,我们发现idr_get_new和idr_get_new_above函数的区别在于传入的第三个参数是0还是starting_id
    rv = idr_get_new_above_int(idp, ptr, 0);
    /*
     * This is a cheap hack until the IDR code can be fixed to
     * return proper error values.
     */
    if (rv < 0)
        return _idr_rc_to_errno(rv);
    *id = rv;
    return 0;
}

static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id)
{
    struct idr_layer *pa[MAX_LEVEL];
    int id;

    id = idr_get_empty_slot(idp, starting_id, pa);
    if (id >= 0) {
        /*
         * Successfully found an empty slot.  Install the user
         * pointer and mark the slot full.
         */
        rcu_assign_pointer(pa[0]->ary[id & IDR_MASK],
                (struct idr_layer *)ptr);
        pa[0]->count++;
        idr_mark_full(pa, id);
    }

    return id;
}
该函数先后调用了idr_get_empty_slot,rcu_assign_pointer和idr_mark_full这三个函数,先来分析idr_get_empty_slot函数:
static int idr_get_empty_slot(struct idr *idp, int starting_id,
                  struct idr_layer **pa)
{
    struct idr_layer *p, *new;
    int layers, v, id;
    unsigned long flags;

    id = starting_id; //starting_id为0
build_up:
    //我们以初始化idr后的第一次申请ID号为例
    p = idp->top; //初始化时idp->top设为NULL
    layers = idp->layers; //初始化时idp->layers设为0
    if (unlikely(!p)) {
        //该函数是从idr_pre_get申请的空闲idr_layer内存中获得一块赋给p,
        具体实现函数如下分析
        if (!(p = get_from_free_list(idp)))
            return -1;
        p->layer = 0;
        layers = 1;
    }
    /*
     * Add a new layer to the top of the tree if the requested
     * id is larger than the currently allocated space.
     */
    while ((layers < (MAX_LEVEL - 1)) && (id >= (1 << (layers*IDR_BITS)))) {
        layers++;
        if (!p->count) {
            /* special case: if the tree is currently empty,
             * then we grow the tree by moving the top node
             * upwards.
             */
            p->layer++;
            continue;
        }
        if (!(new = get_from_free_list(idp))) {
            /*
             * The allocation failed.  If we built part of
             * the structure tear it down.
             */
            spin_lock_irqsave(&idp->lock, flags);
            for (new = p; p && p != idp->top; new = p) {
                p = p->ary[0];
                new->ary[0] = NULL;
                new->bitmap = new->count = 0;
                __move_to_free_list(idp, new);
            }
            spin_unlock_irqrestore(&idp->lock, flags);
            return -1;
        }
        new->ary[0] = p;
        new->count = 1;
        new->layer = layers-1;
        if (p->bitmap == IDR_FULL)
            __set_bit(0, &new->bitmap);
        p = new;
    }
    //将p,即通过get_from_free_list函数获得的内存赋给top层
    rcu_assign_pointer(idp->top, p);
    idp->layers = layers; //此时只有根节点,layers为1
    //该函数真正实现ID号的获得
v = sub_alloc(idp, &id, pa);
    if (v == IDR_NEED_TO_GROW)
        goto build_up;
    return(v);
}

static struct idr_layer *get_from_free_list(struct idr *idp)
{
    struct idr_layer *p;
    unsigned long flags;

    spin_lock_irqsave(&idp->lock, flags);
    //idp->id_free非零,说明有预留的idr_layer内存可用
    if ((p = idp->id_free)) {
//此时将最后申请的可用idr_layer内存从链表中移除,将id_free指向前一个预留内存,如下如图所示(蓝线标出)
        idp->id_free = p->ary[0];//p->ary[0]指向前一个预留内存
        idp->id_free_cnt--; //数目减一
        p->ary[0] = NULL; //slot0槽设为空
    }
    spin_unlock_irqrestore(&idp->lock, flags);
    return(p);
}

函数idr_get_new_above_int中的rcu_assign_pointer实现了指针与ID号的绑定,并将指针保存在ary的slot槽中;
函数idr_get_new_above_int中的idr_mark_full根据ID号来置位bitmap,代码如下
static void idr_mark_full(struct idr_layer **pa, int id)
{
    struct idr_layer *p = pa[0];
    int l = 0;
   //根据id设置bitmap相应位
    __set_bit(id & IDR_MASK, &p->bitmap);
    /*
     * If this layer is full mark the bit in the layer above to
     * show that this part of the radix tree is full.  This may
     * complete the layer above and require walking up the radix
     * tree.
     */
    while (p->bitmap == IDR_FULL) {
        if (!(p = pa[++l]))
            break;
        id = id >> IDR_BITS;
        __set_bit((id & IDR_MASK), &p->bitmap);
    }
}

总的实现如下图的红色部分表示。

 

若已知ID号,而要得到相应的指针,则可调用函数:
void *idr_find(struct idr *idp, int id);
若删除ID号并释放指针,可调用函数:
void idr_remove(struct idr *idp, int id);
若要销毁idr树,则可调用函数:
void idr_destroy(struct idr *idp);

通过上述这些函数接口,我们能够产生ID号用做设备的次设备号,节点号等,在内核的rtc子系统的设备注册中,就用到了idr机制,代码如下(/drivers/rtc/class.c):
//初始化一个rtc的idr对象
static DEFINE_IDR(rtc_idr);

struct rtc_device *rtc_device_register(const char *name, struct device *dev,
                    const struct rtc_class_ops *ops,
                    struct module *owner)
{
    struct rtc_device *rtc;
    int id, err;
    //为rtc_idr预留内存空间
    if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
        err = -ENOMEM;
        goto exit;
    }


    mutex_lock(&idr_lock);
    err = idr_get_new(&rtc_idr, NULL, &id); //获得ID号
    mutex_unlock(&idr_lock);

    if (err < 0)
        goto exit;

id = id & MAX_ID_MASK; //ID号范围是0~0x7fffffff,而rtc的设备数目RTC_DEV_MAX定义为16

    rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
    if (rtc == NULL) {
        err = -ENOMEM;
        goto exit_idr;
    }

    rtc->id = id;//将获得的ID号作为rtc的次设备号
    rtc->ops = ops;
    rtc->owner = owner;
    rtc->max_user_freq = 64;
    rtc->dev.parent = dev;
    rtc->dev.class = rtc_class;
    rtc->dev.release = rtc_device_release;

    mutex_init(&rtc->ops_lock);
    spin_lock_init(&rtc->irq_lock);
    spin_lock_init(&rtc->irq_task_lock);
    init_waitqueue_head(&rtc->irq_queue);

    strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
    dev_set_name(&rtc->dev, "rtc%d", id);

    rtc_dev_prepare(rtc);

    err = device_register(&rtc->dev);
    if (err)
        goto exit_kfree;

    rtc_dev_add_device(rtc);
    rtc_sysfs_add_device(rtc);
    rtc_proc_add_device(rtc);

    dev_info(dev, "rtc core: registered %s as %s\n",
            rtc->name, dev_name(&rtc->dev));

    return rtc;

exit_kfree:
    kfree(rtc);

exit_idr:
    mutex_lock(&idr_lock);
    idr_remove(&rtc_idr, id);
    mutex_unlock(&idr_lock);

exit:
    dev_err(dev, "rtc core: unable to register %s, err = %d\n",
            name, err);
    return ERR_PTR(err);
}

阅读(1260) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~