zoukankan      html  css  js  c++  java
  • 字符设备如何分配/注销设备号

    前一篇对cdev结构体及初始化做了简单介绍

    Linux内核有两个分配设备号的函数

     1 /**
     2  * register_chrdev_region() - register a range of device numbers
     3  * @from: the first in the desired range of device numbers; must include
     4  *        the major number.
     5  * @count: the number of consecutive device numbers required
     6  * @name: the name of the device or driver.
     7  *
     8  * Return value is zero on success, a negative error code on failure.
     9  */
    10 int register_chrdev_region(dev_t from, unsigned count, const char *name)
    11 {
    12     struct char_device_struct *cd;
    13     dev_t to = from + count;
    14     dev_t n, next;
    15 
    16     for (n = from; n < to; n = next) {
    17         next = MKDEV(MAJOR(n)+1, 0);
    18         if (next > to)
    19             next = to;
    20         cd = __register_chrdev_region(MAJOR(n), MINOR(n),
    21                    next - n, name);
    22         if (IS_ERR(cd))
    23             goto fail;
    24     }
    25     return 0;
    26 fail:
    27     to = n;
    28     for (n = from; n < to; n = next) {
    29         next = MKDEV(MAJOR(n)+1, 0);
    30         kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
    31     }
    32     return PTR_ERR(cd);
    33 }
    register_chrdev_region()
     1 /**
     2  * alloc_chrdev_region() - register a range of char device numbers
     3  * @dev: output parameter for first assigned number
     4  * @baseminor: first of the requested range of minor numbers
     5  * @count: the number of minor numbers required
     6  * @name: the name of the associated device or driver
     7  *
     8  * Allocates a range of char device numbers.  The major number will be
     9  * chosen dynamically, and returned (along with the first minor number)
    10  * in @dev.  Returns zero or a negative error code.
    11  */
    12 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
    13             const char *name)
    14 {
    15     struct char_device_struct *cd;
    16     cd = __register_chrdev_region(0, baseminor, count, name);
    17     if (IS_ERR(cd))
    18         return PTR_ERR(cd);
    19     *dev = MKDEV(cd->major, cd->baseminor);
    20     return 0;
    21 }
    alloc_chrdev_region()

    1. 这两个函数最终都会调用__register_chrdev_region()函数。并且会用到一个重要的 struct char_device_struct结构体。

      1 #define CHRDEV_MAJOR_HASH_SIZE    255
      2 /*定义char_device_struct结构体并声明一个全局的指针数组*/
      3 static struct char_device_struct {
      4     struct char_device_struct *next;
      5     unsigned int major;
      6     unsigned int baseminor;
      7     int minorct;
      8     char name[64];
      9     struct cdev *cdev;        /* will die */
     10 } *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; 
     11 /*
     12  * Register a single major with a specified minor range.
     13  *
     14  * If major == 0 this functions will dynamically allocate a major and return
     15  * its number.
     16  *
     17  * If major > 0 this function will attempt to reserve the passed range of
     18  * minors and will return zero on success.
     19  *
     20  * Returns a -ve errno on failure.
     21  */
     22 static struct char_device_struct *
     23 __register_chrdev_region(unsigned int major, unsigned int baseminor,
     24                int minorct, const char *name)
     25 {
     26     struct char_device_struct *cd, **cp;
     27     int ret = 0;
     28     int i;
     29 
     30     cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
     31     if (cd == NULL)
     32         return ERR_PTR(-ENOMEM);
     33 
     34     mutex_lock(&chrdevs_lock);
     35 
     36     /*--当调用alloc_chrdev_region时,传入的major为0,自动分配设备,
     37     *但是有一个缺点就是其分配的设备号只能在255内,而且并没有使用hash表,
     38     *从而大大减少了linux所支持的设备号数。
     39     */
     40     if (major == 0) 
     41     {
     42         for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) 
     43         {
     44             if (chrdevs[i] == NULL)
     45                 break;
     46         }
     47 
     48         if (i == 0) 
     49         {
     50             ret = -EBUSY;
     51             goto out;
     52         }
     53         major = i;
     54         ret = major;
     55     }
     56 
     57     cd->major = major;
     58     cd->baseminor = baseminor;
     59     cd->minorct = minorct;
     60     strlcpy(cd->name, name, sizeof(cd->name));
     61 
     62     i = major_to_index(major);
     63 
     64     for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
     65         if (
     66                     (*cp)->major > major       ||
     67                     
     68                     (
     69                             (*cp)->major == major    &&
     70                              (
     71                                      (*cp)->baseminor >= baseminor    ||
     72                                       (*cp)->baseminor + (*cp)->minorct > baseminor
     73                              )
     74                     )
     75            )
     76             break;
     77 
     78     /* Check for overlapping minor ranges.  */
     79     if (*cp && (*cp)->major == major) 
     80     {
     81         int old_min = (*cp)->baseminor;
     82         int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
     83         int new_min = baseminor;
     84         int new_max = baseminor + minorct - 1;
     85 
     86         /* New driver overlaps from the left.  */
     87         if (new_max >= old_min && new_max <= old_max) {
     88             ret = -EBUSY;
     89             goto out;
     90         }
     91 
     92         /* New driver overlaps from the right.  */
     93         if (new_min <= old_max && new_min >= old_min) {
     94             ret = -EBUSY;
     95             goto out;
     96         }
     97     }
     98 
     99     cd->next = *cp;
    100     *cp = cd;
    101     mutex_unlock(&chrdevs_lock);
    102     return cd;
    103 out:
    104     mutex_unlock(&chrdevs_lock);
    105     kfree(cd);
    106     return ERR_PTR(ret);
    107 }

    2. 在分析函数前先参考关于hash表的知识

    从上图我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。

    3. 分析如何指定分配设备号

    其中用的的一个函数定义如下,该函数获得数组的索引,其中major相当于hash表的key,len=255。

    下面分析的major=128,383,638都对应数组的第128项,添加在chardevs[128]开始的链表中。

    1 static inline int major_to_index(unsigned major)
    2 {
    3     return major % CHRDEV_MAJOR_HASH_SIZE;
    4 }

     3.1 指定major=128,分配链表中的第一个设备号,对应循环中*cp==NULL退出循环。

    3.2 指定major=638,分配第二个设备号,对应循环中*cp==NULL退出循环。

    3.3 指定major=383,对应循环中(*cp)->major > major 的条件,跳出循环。

    4 注销设备号,设备号是系统中宝贵的资源,模块卸载时要注销设备号。即根据主次设备号找到对应的char_device_struct结构体并从链表中删除

     1 static struct char_device_struct *
     2 __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
     3 {
     4     struct char_device_struct *cd = NULL, **cp;
     5     int i = major_to_index(major);
     6 
     7     mutex_lock(&chrdevs_lock);
     8     for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
     9     {
    10         if ((*cp)->major == major &&
    11             (*cp)->baseminor == baseminor &&
    12             (*cp)->minorct == minorct)
    13             break;
    14     }
    15     if (*cp) {
    16         cd = *cp;
    17         *cp = cd->next;
    18     }
    19     mutex_unlock(&chrdevs_lock);
    20     return cd;
    21 }
  • 相关阅读:
    【未完待续】MVC 之HTML辅助方法
    【部分】ASP.NET MVC5
    【总结】Github通过Git Bash上传文件到仓库
    【总结】委托和匿名委托的比较
    函数进化到Lambda表达式的三过程
    C# 常用linq、lambda表达式整理 【转】
    Lambda表达式用在什么地方?如何使用?
    【错误】fatal: destination path already exists and is not an empty directory. 错误及解决办法
    GIT更换连接方式
    Github中添加SSH key
  • 原文地址:https://www.cnblogs.com/yangjiguang/p/6032431.html
Copyright © 2011-2022 走看看