前言

在Linux内核中设备号的作用是用来区分不同的设备类型。

比如:

设备号23,对应的是LED

设备号17,对应的是某个存储设备

等等...

主次设备号

主设备号:对应设备的主号码

次设备号:对应设备的子号码

比如:你有两个LED,你注册了主设备号14,代表使用这个设备号的驱动都是LED设备,那么怎么区分1和2呢?就是子设备号,通过子设备号来区分是LED1还是LED2。

内核只认主设备号,最终调用时内核会通过主设备号找到索引,这个索引就是子设备号,然后去调用这个索引指向的驱动模块接口。

有很多设备号Linux内核已经自己定义了,你可以在你的发行版上输入如下命令可以查看到Linux内核定义好的设备类型,或者在Linux内核源码目录:/include/linux/major.h

cat /proc/devices

输出:

  1 mem4 /dev/vc/04 tty4 ttyS5 /dev/tty5 /dev/console5 /dev/ptmx5 ttyprintk6 lp7 vcs10 misc13 input14 sound/midi14 sound/dmmidi21 sg29 fb89 i2c99 ppdev

上面不是已经在当前Linux内核中注册的设备驱动,仅是Linux内核为我们开发人员定义好了的一个设备号,我们可以在后续注册时使用这个设备号,也可以自己自定义一个设备号。

已经注册好的设备都会放在/dev这个目录下。

我们可以使用ls命令查看:

ls -l /dev/*

输出:

1 root root     10, 175 Apr 21 00:00 agpgart
crw-r--r--  1 root root     10, 235 Apr 21 00:00 autofs
drwxr-xr-x  2 root root         300 Apr 21 00:00 block
drwxr-xr-x  2 root root          80 Apr 21 00:00 bsg
crw-------  1 root root     10, 234 Apr 21 00:00 btrfs-control
drwxr-xr-x  3 root root          60 Apr 21 00:00 bus
lrwxrwxrwx  1 root root           3 Apr 21 00:00 cdrom -> sr0
lrwxrwxrwx  1 root root           3 Apr 21 00:00 cdrw -> sr0
drwxr-xr-x  2 root root        3840 Apr 21 17:40 char
crw--w----  1 root tty       5,   1 Apr 21 00:01 console
lrwxrwxrwx  1 root root          11 Apr 21 00:00 core -> /proc/kcore
crw-------  1 root root     10,  59 Apr 21 00:00 cpu_dma_latency
crw-------  1 root root     10, 203 Apr 21 00:00 cuse
drwxr-xr-x  6 root root         120 Apr 21 00:00 disk
drwxr-xr-x  2 root root          60 Apr 21 00:00 dma_heap
crw-rw----+ 1 root audio    14,   9 Apr 21 00:00 dmmidi

就拿第一个来说吧

10,175 其中10代表主设备号,175代表次设备号,Apr 21 00:00代表注册日期,这个日期是内核启动加载时候的日期,agpgart设备类型

我们在注册设备时也可以使用10这个主设备号,但是不能使用175这个次设备号,因为这个次设备号已经被注册使用,Linux内核允许多个驱动共享一个主设备号但不允许共享一个次设备号。

同时也可以写一个驱动管理多个次设备。

VFS虚拟文件系统

要想了解用户态是如何在操作驱动时不关心物理媒介和不同文件系统协议规范,而使用统一接口就能抽象完成所有事情,使得用户无需关心具体底层实现,就可以使用通用接口完成不同设备的控制。

Linux内核是如何检索主次设备的?

当我们调用open函数时,会从用户态发生一个中断为10的系统中断,然后转到内核态的SYSCALL_DEFINE3函数开始执行,在经过一系列的查找调用确定最终调用的open函数。

最终的调用可能是驱动实现的open,或由VFS子系统完成的open函数,VFS子系统的open函数实现就像普通的fopen一样,会返回一个未使用的文件描述符,这个文件描述符仅对当前进程有效,不同的系统位数对大小有限制,其中当打开只有VFS会把它加入到文件监视系统中去,记录这个文件当前的状态。

open会一环接一环尝试去在对应的链表里寻找对应的文件名,查看在哪个文件设备表中注册,是普通文件还是驱动文件还是其它文件,则调用对应的open函数。

同时设备驱动是用一个表存储的:

struct kobj_map {struct probe {struct probe *next;dev_t dev;unsigned long range;struct module *owner;kobj_probe_t *get;int (*lock)(dev_t, void *);void *data;} *probes[255];struct mutex *lock;
};

这是我在Linux内核2.4里取的,最大限制为255,也就是说最多只能注册255个设备驱动,且设备驱动的主设备号不能小于0或大于255,其中0和255被Linux内核保留,不允许使用。

最新版的Linux内核已经不受限于255了,这里我以这个版本为列,让大家知道Linux是怎么来存储与找到设备模块的。

当找到是设备文件时,会去遍历kobj_map这个结构体里的probe这个表。

它是哈希表,首先内核有两种遍历方式。

因为在我们写驱动时注册主设备号时,可以手动指定设备号也可以动态分配,哈希表有一个特点就是能把一组字符算成一个固定取值范围大小的一个值,当我们是动态分配时,Linux内核会算成一个Key,然后插到对应表项里且哈希表有一个特点就是不会重复,当你的设备名字是重复的时候Linux内核也不会让你注册。

如已经有设备叫LED了,你不能在注册一个LED的设备。

当你是静态分配时Linux内核则按你给的设备号往里插,若以这个设备号为键值的表项里有数据则插入失败。

当然查找这个表项也非常简单,首先open到了__dentry_oepn这个函数,它会去遍历kobj_map->probe这个表,首先拿着用户态传进来的name,遍历probe,然后在owner取出name比对,若一致则返回当前probe的指针,后续的read,write可以根据这个指针进行调用对用的read和write。

其中dev是一个32位的变量,它按位存储,其中12位存储主设备号,20位存储次设备号,也就是说主设备号最大也不能超过12次方的大小。

这里可以看下owner这个结构体:

可以清楚的看到name这个变量。

struct module{enum module_state state;struct list_head list;char name[MODULE_NAME_LEN];struct module_kobject mkobj;struct module_param_attrs *param_attrs;const char *version;const char *srcversion;const struct kernel_symbol *syms;unsigned int num_syms;const unsigned long *crcs;const struct kernel_symbol *gpl_syms;unsigned int num_gpl_syms;const unsigned long *gpl_crcs;unsigned int num_exentries;const struct exception_table_entry *extable;int (*init)(void);void *module_init;void *module_core;unsigned long init_size, core_size;unsigned long init_text_size, core_text_size;struct mod_arch_specific arch;int unsafe;int license_gplok;#ifdef CONFIG_MODULE_UNLOADstruct module_ref ref[NR_CPUS];struct list_head modules_which_use_me;struct task_struct *waiter;void (*exit)(void);
#endif#ifdef CONFIG_KALLSYMSElf_Sym *symtab;unsigned long num_symtab;char *strtab;struct module_sect_attrs *sect_attrs;
#endifvoid *percpu;char *args;};

总结

通俗易懂的来说,主设备号在Linux内核看来就是设备文件表的KEY,静态分配与动态分配有的差别在于是否使用哈希算法,同时非常建议大家使用动态分配。

次设备号就是用来标明是哪个设备,比如多个LED设备,次设备号就是用来区分LED1还是LED2,同时在底层内核态在检索时,是不关心次设备号的,只关心主设备号,而我们会关心次设备号。

因为内核最终会把dev传递给驱动模块,我们在代码里把dev里的次设备号取出来,判断是哪个次设备然后做对应的工作,这样就完成在一个模块里实现对多个设备的管理,或者你也可以在为每个子设备都注册一个驱动,也是可以的。

Linux嵌入式开发_主设备号与次设备号详解相关推荐

  1. Linux嵌入式开发_修改机器码

    在嵌入式开发过程中Linux认为每一个硬件都是定制化的,所以都需要一个唯一的编码,就是机器码,机器码的主要工作就是为了uboot与linux内核进行适配,若uboot引导时发现目标机器码与自己定义的机 ...

  2. Linux嵌入式开发_设置时钟频率

    在核心板上若工作的时钟频率不同则无法正常驱动工作,这是很关键的知识点. 频率我们可以通过开发板的原理图查看,或者在soc(CPU)的原理图上得到也可以. 这里我用的是开发板的原理图,我使用的是TQ21 ...

  3. Linux嵌入式开发_修改镜像文件输出路径

    一般情况下存在于arch/架构/boot/makefile文件下 如我的是arm架构,修改ZIMAGE的输出路径 vim arch/arm/boot/Makefile $(obj)/zImage: $ ...

  4. Linux驱动开发_设备文件系统详解

    目录 何为设备管理器? Linux下dev的作用 Devfs sysfs kobject udev proc 何为设备管理器? 设备管理器就是负责管理这台电脑上的外设,当我们通过电脑提供的USB口插入 ...

  5. linux主设备编号从0到多少,Linux驱动开发之主设备号找驱动,次设备号找设备

    一.引言 很久前接触linux驱动就知道主设备号找驱动,次设备号找设备.这句到底怎么理解呢,如何在驱动中实现呢,在介绍该实现之前先看下内核中主次设备号的管理: 二.Linux内核主次设备号的管理 Li ...

  6. Linux驱动开发之主设备号找驱动,次设备号找设备

    原创作品,转载时请务必以超链接形式标明文章原始出处:http://blog.csdn.net/gqb666/article/details/8805179,作者:gqb666 一.引言   最近成都地 ...

  7. 基于c语言的linux嵌入式开发入门

    前言 本文主要包含,c语言基本结构与语法.make及makefile的使用.main函数参数与返回值的说明.标准输入.输出.错误流的介绍以及linux管道的应用. 语言数据类型 联合体也有翻译为共用体 ...

  8. Linux嵌入式开发——C编程

    文章目录 Linux嵌入式开发--C编程 一.编写C程序 1.1.设置vim编辑器 1.2.编写C程序 二.编译C程序 三.make工具和Makefile文件 3.1.编写C程序 C文件 H文件 3. ...

  9. LINUX嵌入式开发书籍推荐(附WINCE部分)

    LINUX嵌入式开发书籍推荐(附WINCE部分) LINUX嵌入式开发书籍推荐(附WINCE部分) 一,编程语言部分 1,C语言 <C语言大全(第四版)> 市场价 :¥48.00 [原 书 ...

最新文章

  1. CornerNet-Lite:CornerNet粗暴优化,加速6倍还提点了 | BMVC 2020
  2. doc es 中type_ES系列07:match_phrase与match_phrase_prefix query
  3. 【专访】KDD2018主席熊辉教授:数据挖掘与深度学习结合新趋势
  4. Prism4文档翻译(第四章 第一部分) 转载bluesky234
  5. Java基础-String、StringBuffer、StringBuilder的区别
  6. Spark案例:Scala版统计单词个数
  7. 5000字“肝”了这篇IP协议
  8. 大家可以放心了!Redmi K20将配备双频GPS
  9. win8+sdk8+vs2012+freeglut+glew开发opengl
  10. 剑指Offer之翻转单词顺序列
  11. Python自动化课之Day3篇
  12. 校园热水供应系统设计思路
  13. VBA编程之ODBC连接数据库
  14. AdventureWorksCycle案例分析
  15. 大兴机场临空区爆出大规划!
  16. 盘点7款常用的数据分析工具
  17. python计算机器人运动学分析_orocos_kdl学习(二):KDL Tree与机器人运动学
  18. 成功解决android 网络视频边下载变播放。
  19. 安卓App自启动,两种不同的方式!!!支持到安卓4.4
  20. Windows10 22H2 19045.2130推送了!

热门文章

  1. linux uwsgi 非root,ubuntu-除非root用户,否则uWSGI Emperor权限被拒...
  2. java 整数加减乘除_java实现超大整数加减乘除四则运算
  3. vscode 分析c代码_vs code(C语言)配置教程
  4. ubuntu 安装显卡后调整分辨率卡死 解决:禁用掉nouveau
  5. Java面试易错题精选
  6. 安卓开发 底部导航图标切换时动画效果_体验安卓 10:好用百倍都不止!
  7. python怎么更改背景颜色_python中绘图时怎么改背景颜色?
  8. mybatis中concat的用法
  9. php7 捕获语法错误,PHP7 method_exists未捕获错误:函数名称必须是字符串
  10. 调用某个按钮事件_高级UI晋升之触摸事件分发机制(一)