字符设备如何分配/注销设备号
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 }
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 }
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 }
从上图我们可以发现哈希表是由数组+链表组成的,一个长度为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 }