一、Linux内核字符设备号
每一个字符设备都需要设备号,Linux内核通过字符设备号来区分设备的唯一标识。
设备号:由 主设备号(major) + 次设备号(minor) 组成。
主设备号:关联到具体的驱动程序(如 1 对应 mem,5 对应 tty)。
次设备号:区分同一驱动管理的多个设备。
操作依赖设备号,以下是常用的设备号操作函数(配套使用):
| 函数 | 功能 | 原型 |
|---|---|---|
MKDEV(major, minor) |
由主 / 次设备号生成 dev_t |
dev_t MKDEV(unsigned int major, unsigned int minor); |
MAJOR(dev) |
从 dev_t 提取主设备号 |
unsigned int MAJOR(dev_t dev); |
MINOR(dev) |
从 dev_t 提取次设备号 |
unsigned int MINOR(dev_t dev); |
宏定义位于内核文件
@include/linux/kdev_t.h
7#define MINORBITS 20
8#define MINORMASK ((1U << MINORBITS) - 1)
9
10#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
11#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
12#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
主次设备号内存组成如下:
Linux内核提供的主要注册字符设备函数:register_chrdev()、register_chrdev_region()。
二、 Linux内核字符设备号管理规则
Linux内核字符设备号管理规则:
- 数组**chrdevs[CHRDEV_MAJOR_HASH_SIZE]**维护所有的字符设备主次设备号;
- 主设备号值区间[0 512],数组下标与主设备号值关系:小于255的主设备号值等于下标,大于254小于512的主设备号对255取余后的值等于小标(比如主设备号251和506共用同一个下标);
- 每一个主设备号(251和506虽然公用同一个数组下标,但是各自对应2^20个次设备号)对应的次设备号区间[0-1048575];
- 次设备号可以分块申请,块内次设备号必须连续,各个块之间的次设备号不能重叠,每一块次设备号由结构体struct char_device_struct表示,按照次设备号从小到大顺序,通过链表将各个次设备号块连接起来;
三、实例代码验证
为方便验证代码,彭老师从内核中将字符设备号申请注册的核心代码单独拎出来,去掉一些内核特定函数,
这样我们可以脱离内核进行测试:
四、测试
我们通过一组实例讲解字符设备号的分配:
register_chrdev_region(MKDEV(251,0),3,"cdev1"); /* 1 */
register_chrdev_region(MKDEV(251,3),4,"cdev2"); /* 2 */
register_chrdev_region(MKDEV(506,0),3,"cdev3"); /* 3 */
register_chrdev_region(MKDEV(506,3),4,"cdev4"); /* 4 */
register_chrdev(234,"cdevA"); /* 5 */
register_chrdev(489,"cdevB"); /* 6 */
- 申请字符设备:chardev1,主设备号:251,次设备号基值:0、个数3、区间:[0-2];
- 申请字符设备:chardev2,主设备号:251,次设备号基值:3、个数4、区间:[3-6];
- 申请字符设备:chardev3,主设备号:506,次设备号基值:0、个数3、区间:[0-2];
- 申请字符设备:chardev4,主设备号:506,次设备号基值:3、个数4、区间:[3-6];
- 申请字符设备:chardevA,主设备号:234,次设备号区间:[0-255];
- 申请字符设备:chardevB,主设备号:489,次设备号区间:[0-255];
申请成功之后,节点信息如下:
如果此时我们要再申请字符设备cdev5:
register_chrdev_region(MKDEV(506,4),4,"cdev5");
主设备号:506,次设备号基值:4、个数4、区间:[4-7],
那么就会和字符设备cdev4冲突,从而申请失败。
五、代码获取
完整代码获取,关注添加好友,或者后台回复:charno
157