
02.设备号详解一、为什么需要设备号在上一节中已经看到/dev目录下包含大量设备文件。例如ls -l /dev/null /dev/tty0 /dev/mmcblk0示例输出crw-rw-rw- 1 root root 1, 3 12月 9 19:02 /dev/null crw--w---- 1 root tty 4, 0 12月 9 19:02 /dev/tty0 brw-rw---- 1 root disk 179, 0 12月 9 19:02 /dev/mmcblk0其中有两个重要信息第一列首字符c表示字符设备b表示块设备。倒数第三、倒数第二列两个数字例如1, 3、4, 0、179, 0。这两个数字组合在一起构成该设备的设备号device number前一个数字是主设备号major number。后一个数字是次设备号minor number。当用户空间程序执行int fd open(/dev/tty0, O_RDWR);内核需要根据/dev/tty0找到对应的驱动以及具体设备实例。 设备号在这里起到了桥梁作用通过主设备号确定由哪个驱动负责处理通过次设备号确定该驱动下的哪一个设备实例。可以概括为设备号用于实现 “设备文件 ↔ 驱动程序 ↔ 具体设备实例” 的映射。二、主设备号与次设备号1. 主设备号(major number)主设备号的作用是标识设备所属的驱动或设备类别。在内核内部字符设备子系统会维护按主设备号索引的表结构。每个主设备号与某个驱动或某个子系统相关联。当内核解析/dev/xxx的设备号时会根据主设备号找到对应驱动的入口。例如以/proc/devices为例cat /proc/devices输出节选lckfblinaro-alip:~$ cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 7 vcs 10 misc 13 input 81 video4linux 89 i2c 90 mtd .......................说明字符设备主设备号4对应tty类设备。块设备主设备号13对应input设备鼠标/键盘。主设备号的范围和分配策略由内核维护部分主设备号在文档中有约定用途其他范围可供动态分配给各种驱动。2. 次设备号(minor number)次设备号用于标识同一主设备号下的不同设备实例。一个驱动对应某个主设备号可以管理多路设备。每一路设备具有不同次设备号用于区分。例如假设某字符设备驱动使用主设备号 240并管理两个实例/dev/mychar0主设备号 240次设备号 0 →240, 0/dev/mychar1主设备号 240次设备号 1 →240, 1驱动在处理open/read/write等操作时可以根据次设备号选择对应的数据结构或硬件资源。我们继续来看tty串口的例子lckfblinaro-alip:~$ ls -l /dev/tty* crw-rw-rw- 1 root tty 5, 0 12月 9日 19:02 /dev/tty crw--w---- 1 root tty 4, 0 12月 9日 19:02 /dev/tty0 crw--w---- 1 root tty 4, 1 12月 9日 19:02 /dev/tty1 crw--w---- 1 root tty 4, 10 12月 9日 19:02 /dev/tty10 crw--w---- 1 root tty 4, 11 12月 9日 19:02 /dev/tty11 crw--w---- 1 root tty 4, 12 12月 9日 19:02 /dev/tty12 crw--w---- 1 root tty 4, 13 12月 9日 19:02 /dev/tty13 crw--w---- 1 root tty 4, 14 12月 9日 19:02 /dev/tty14 crw--w---- 1 root tty 4, 15 12月 9日 19:02 /dev/tty15 ........我们的串口资源有这么多同一个串口类型的设备可能有几十上百个主次结合就能很快定位主设备号确定设备的类型为串口设备次设备号确定为串口设备下面的那个设备tty10主设备号为4次设备号为10tty11主设备号为4次设备号为11tty12主设备号为4次设备号为12……三、设备号的数据类型与布局在内核中设备号使用dev_t类型表示。 在多数架构上它是一个 32 位无符号整数其位布局大致为高 12 位主设备号低 20 位次设备号即主设备号理论范围0 ~ 2¹² - 10 ~ 4095次设备号理论范围0 ~ 2²⁰ - 10 ~ 1,048,575内核提供了一组宏用于操作dev_tMAJOR(dev_t dev); // 从 dev 中取得主设备号 MINOR(dev_t dev); // 从 dev 中取得次设备号 MKDEV(unsigned int major, unsigned int minor); // 构造 dev_t典型用法示例dev_t dev; /* 由主设备号 240、次设备号 0 构造一个 dev_t */ dev MKDEV(240, 0); /* 提取主、次设备号 */ unsigned int ma MAJOR(dev); unsigned int mi MINOR(dev);在字符设备驱动中通常会定义static dev_t dev_num; // 完整设备号 static int major; // 主设备号 static int minor 0; // 起始次设备号四、设备号的两种分配在编写字符设备驱动时驱动需要向内核登记自己要使用的设备号范围。 这一过程与后续的cdev注册、/dev节点创建是分离的本节仅讨论设备号本身的注册与释放。内核提供了两种常用方式静态注册指定主设备号register_chrdev_region动态分配自动分配主设备号alloc_chrdev_region1. 静态注册驱动可以直接指定期望使用的主设备号和起始次设备号然后向内核注册static dev_t dev_num; static int major 240; /* 希望使用主设备号 240 */ static int minor 0; /* 起始次设备号 */ static int __init mydrv_init(void) { int ret; dev_num MKDEV(major, minor); /* 注册从 dev_num 开始连续 1 个设备号 */ ret register_chrdev_region(dev_num, 1, mychar_static); if (ret 0) { pr_err(register_chrdev_region failed\n); return ret; } pr_info(mychar_static: registered with major%d, minor%d\n, MAJOR(dev_num), MINOR(dev_num)); return 0; } static void __exit mydrv_exit(void) { unregister_chrdev_region(dev_num, 1); }说明register_chrdev_region(dev_t from, unsigned count, const char *name);from起始设备号包含主、次设备号。count连续设备号的数量。name名称用于/proc/devices等位置显示。对于字符设备主设备号必须尚未被其他驱动使用。适用场景某些需要固定主设备号的旧系统或特定应用场景。局限性需要开发者手动选择主设备号并确保不与系统中已有设备冲突。在不同内核版本或不同平台上该主设备号可能已经被占用。对于一般新开发的驱动更推荐使用动态分配方式。2. 动态分配动态分配由内核选择尚未被使用的主设备号驱动只需指定起始次设备号需要的连续设备数量名称。static dev_t dev_num; static int major; static int minor 0; static int __init mydrv_init(void) { int ret; /* 请求动态分配 1 个设备号从次设备号 0 开始 */ ret alloc_chrdev_region(dev_num, minor, 1, mychar_dynamic); if (ret 0) { pr_err(alloc_chrdev_region failed\n); return ret; } major MAJOR(dev_num); minor MINOR(dev_num); pr_info(mychar_dynamic: registered with major%d, minor%d\n, major, minor); return 0; } static void __exit mydrv_exit(void) { unregister_chrdev_region(dev_num, 1); }说明alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);dev返回的起始dev_t。baseminor起始次设备号。count从baseminor开始连续申请多少个次设备号。name设备名称标识。优点不需要手动管理主设备号的分配。跨平台、跨内核版本时更不易产生冲突。是新驱动开发中推荐的做法。