1.设备号的分配和管理

在内核源码中,涉及设备号分配和管理的函数有两个

1.register_chrdev_region函数

将当前设备驱动程序要使用的设备号记录到chrdevs数组中,前提是驱动程序已经知道要分配的设备号是多少啦

int register_chrdev_region(dev_t from,unsigned int count,char*name)  //from为第一个设备编号,count为连续的设备个数,即是驱动程序所管理的设备个数,name为驱动程序名

{

//全局性指针数组,是内核用于设备号分配和管理的核心要素

/*

static struct char_device_struct {

       struct char_device_struct *next;

       unsigned int major;

       unsigned int baseminor;

       int minorct;

       char name[64];

       struct cdev *cdev; //*will die*/

} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

*/

//其初始化状态为

       struct char_device_struct *cd;  

       dev_t to=from+count;  //末设备号

       dev_t n,next;

       from(n=from;n<to;n=next) {

              next=MKDEV(MAJOR(n)+1,0);   //下一个主设备号的首个设备

              if(next>to) 

                     next=to;

              cd=_register_chrdev_region(MAJOR(n),MINOR(n),next-n,name);  //最终要调用此函数实现分配

              if(IS_ERR(cd))

                     goto fail;

       }

       return 0;

fail:

       to=n;

       for(n=from;n<to;n=next) {

               next=MKDEV(MAJOR(n)+1,0);

              kfree(_unregister_chrdev_region(MAJOR(n),MINOR(n),next-n));

       }

       return PTR_ERR(cd);

}

static struct char_device_struct *

_register_chrdev_region(unsigned int major,unsigned int baseminor,int minorct,const char *name)

{


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

    ...

    cd->major=major;

    cd->baseminor=baseminor;

    cd->minorct=minorct;

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

//分配一个struct char_device_struct对其初始化

    i=major_to_index(major); //开始搜索chrdev数组,以哈希表形式进行,为此必须获取一个散列关键值i=major%255.

此后函数将对chrdevs[i]元素管理的链表扫描

现在以一个具体事例来说明

现假设有一个设备驱动程序使用的主设备号是257,次设备号分别是0,1,2,3

其调用

int ret=register_chrdev_region(MKDEV(257,0),4,"demodev");

调用完毕其数组为

现在又有一个设备驱动程序使用的主设备号为2,次设备号为0,调用

int ret=register_chrdev_region(MKDEV(2,0),1,"augdev");

2%255=2;


有趣的是另一个设备驱动程序调用register_chrdev_region向系统注册,主设备号也为257,只要其次设备号所在范围[baseminor,baseminor+minrct]不与demodev的次设备号重叠则也会分配成功,系统依然会生成一个新的struct char_device_struct 插入链表。否则返回失败。

2.alloc_chrdev_region函数

该函数由系统协助分配设备号,分配的主设备号在1-254间

int alloc_chrdev_region(dev_t *dev,unisgned baseminor,unsigned count,const char*name)     //dev仅用于输出的参数,在成功完成调用后将保存以分配范围的第一个编号。baseminor所请求的第一个编号,count是连续的分配个数,char为驱动程序名

{

      struct char_device_struct *cd;

/*此函数核心部分依然是_register_chrdev_region,其首参为0,将调用如下逻辑


static struct char_device_struct *

_register_chrdev_region(unsigned int major,unsigned int baseminor,int minorct,const char *name)

{


      ...

      if(major==0) {

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

                       if(chrdevs[i]==NULL

                                 break;

               }

               if(i==0) {

                      ret=-EBUSY;

                      goto out;

               }

               major=i;

               ret=major;

               }

               ...

 }*/

/*上述代码片段的实现原理很简单,在for循环中从chrdevs数组的末项(254)前向扫描,如发现该数组中某项,如i项对应为NULL,那就把该项对应的索引值i作为分配的主设备返回给驱动程序,同时生成一个struct char_device_struct插入到

chrdevs[i]中。

*/



      cd=_register_chrdev_region(0,baseminor,count,name);

      if(IS_ERR(cd))

               return PTR_ERR(cd);

      *dev=MKDEV(cd->major,cd->baseminor);

      return 0;

}

设备号做为一种系统资源,当所对应的设备驱动程序被卸载时,很显然要把其所占用的设备号归还给系统,以便分配给其他内核模块使用。

其释放函数为

void unregister_chrdev_region(dev_t from , unsigned count);

Logo

更多推荐