字符设备的组织形式与设备号的注册分配



kernel中字符设备用结构体struct char_device_struct表示,其定义如下:

#define CHRDEV_MAJOR_HASH_SIZE   255 /*kernel/include/linux/fs.h*/    

static struct char_device_struct{/*kernel/fs*/

        structchar_device_struct *next;/*指向主设备号相同、次设备号范围相互不重叠的兄弟节点*/

        unsignedint major;/*主设备号*/

        unsignedint baseminor;/*起始次设备号*/

        intminorct;/*次设备号数*/

        charname[64];/*设备名称*/

        structcdev *cdev;           /* will die */

} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

 

chrdevs指针数组的每个元素代表主设备号相同的一类设备,其实际上是一个链表头,指向char_device_struct的指针,而每一个 char_device_struct 结构都含有一个 next指针,它指向其兄弟字符设备,兄弟设备的主设备号相同,各自的次设备号范围相互不重叠。chrdevs指针数组组织方式如下图所示:

               

说明:

1)主设备号0比较特殊,其表示需要给设备重新分配主设备号

2)注册设备时,允许申请的主设备号大于等于CHRDEV_MAJOR_HASH_SIZE255),

    在处理时,主设备号的计算采用取余计算:

          static inline intmajor_to_index(unsigned major) {

             return major %CHRDEV_MAJOR_HASH_SIZE;

     }

3)次设备号区间[0,255]


Linux内核有两个分配设备号的函数register_chrdev_regionalloc_chrdev_region这两个函数最终都会调用__register_chrdev_region函数。


/*

 * Register a single majorwith a specified minor range.

 * If major == 0 thisfunctions will dynamically allocate a major and return its number.

 * If major > 0 thisfunction will attempt to reserve the passed range of minors and will returnzero on success.

 * Returns a -ve errno onfailure.

 */

static struct char_device_struct *__register_chrdev_region(unsigned intmajor, unsigned int baseminor,int minorct, const char *name)

{

        structchar_device_struct *cd, **cp;

        intret = 0;

        inti;

   //char_device_struct结构体分配内存,用于保存新的设备

        cd= kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);

        if(cd == NULL)

                  returnERR_PTR(-ENOMEM);

        mutex_lock(&chrdevs_lock);

        //如果主设备号为0,则给设备重新分配一个主设备号

       if(major == 0) {

                  //从元素序号最大的开始,寻找未使用的主设备号

                  for(i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {

                           if(chrdevs[i] == NULL)

                                    break;

                  }

                  //i==0,说明没有未使用的主设备号,返回错误

                  if(i == 0) {

                           ret= -EBUSY;

                           gotoout;

                  }

                  major= i;

                  ret= major;

        }


       //填充char_device_struct结构体

    cd->major= major;

        cd->baseminor= baseminor;

        cd->minorct= minorct;

        strlcpy(cd->name,name, sizeof(cd->name));

       //主设备号采用求余操作,主要是针对主设备号大于等于CHRDEV_MAJOR_HASH_SIZE的进行处理

        i= major_to_index(major); // return major %CHRDEV_MAJOR_HASH_SIZE


        for(cp = &chrdevs[i]; *cp; cp = &(*cp)->next)

               //正常情况下,不会满足if条件,不知作者出于什么目的,可以省略这段代码

               if((*cp)->major > major || ((*cp)->major == major &&(((*cp)->baseminor >= baseminor) ||

                                                  ((*cp)->baseminor + (*cp)->minorct> baseminor))))

                           break;

                  //检查次设备号是否和兄弟设备重合,*cp实际上就是chrdevs[i],假设有A[Amin,Amax]B[Bmin,Bmax]两个设

                 //备,A是已经存在的设备,B是要添加的设备,若两个设备次设备号重叠,则存在三种情况:

                 //(1)B设备号区间的左侧重叠,则有Amin=<Bmax<=Amax;

                //(2)B设备号区间的右侧重叠,则有Amin=<Bmin<=Amax;

               //(3)B设备号区间包含在A设备号区间,满足上述(1)、(2)两个条件,因此三种情况可以简化为上述两种情况。

              if(*cp && (*cp)->major == major) {

                  intold_min = (*cp)->baseminor;

                  intold_max = (*cp)->baseminor + (*cp)->minorct - 1;

                  intnew_min = baseminor;

                  intnew_max = baseminor + minorct - 1;

                   /*在新设备次设备号区间左侧重叠 */

                  if(new_max >= old_min && new_max <= old_max) {

                           ret= -EBUSY;

                           gotoout;

                  }

                  /*在新设备次设备号区间右侧重叠*/

                  if(new_min <= old_max && new_min >= old_min) {

                           ret= -EBUSY;

                           gotoout;

                  }

        }


//以下两行更新链表头,即更新chrdevs[i]

        cd->next= *cp;

        *cp= cd;

        mutex_unlock(&chrdevs_lock);

        returncd;

out:

        mutex_unlock(&chrdevs_lock);

        kfree(cd);

        returnERR_PTR(ret);

}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值