开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 30 天,点击查看活动详情。
一、Linux内核对设备的分类
-
linux的文件种类:
- -:普通文件 ,这里面包括了文件内容、文件名、元信息(iNode)
- d:目录文件
- p:管道文件
- s:本地socket
- l:链接文件
- c:字符设备
- b:块设备
其中
字符设备
和块设备
就是熟知的驱动开发;特别强调一点,在这些文件种类中除了普通文件和目录文件,其他的文件都被归为“伪文件”,也就是没有文件内容的,但是在一些情况下也是必需的。
-
Linux内核按驱动程序实现模型框架的不同,将设备分为三类:
-
字符设备:按字节流形式进行数据读写的设备,一般情况下按顺序访问,数据量不大,一般不设缓存;这里的
按顺序访问
意思是出来的数据里面就不存在了,也称为流式数据
。 -
块设备:按整块进行数据读写的设备,最小的块大小为512字节(一个扇区),块的大小必须是扇区的整数倍,Linux系统的块大小一般为4096字节,随机访问,设缓存以提高效率
-
网络设备:针对网络数据收发的设备,例如日常接触到的蓝牙、网卡等,但是这里它不是文件,对于那个
Linux系统里一切皆是文件
的说法,只是满足大部分情况而已。
-
总体框架图
其中的字符设备文件
和字符设备驱动
,它们的设备号是绑定在一起的,当打开设备文件时便会调用相关的驱动程序。
二、设备号--内核中同类设备的区分
内核用设备号来区分同类里不同的设备,设备号是一个无符号32位整数,数据类型为dev_t ,设备号分为两部分:
-
主设备号:占高12位,用来表示驱动程序相同的一类设备,例如USB那个接口
-
次设备号:占低20位,用来表示被操作的哪个具体设备
应用程序打开一个设备文件时,通过设备号来查找定位内核中管理的设备。
- MKDEV宏用来将主设备号和次设备号组合成32位完整的设备号,用法:
dev_t devno;
int major = 251;//主设备号
int minor = 2;//次设备号
devno = MKDEV(major,minor);
- MAJOR宏用来从32位设备号中分离出主设备号,用法:
dev_t devno = MKDEV(249,1);
int major = MAJOR(devno);
- MINOR宏用来从32位设备号中分离出次设备号,用法:
dev_t devno = MKDEV(249,1);
int minor = MINOR(devno);
如果已知一个设备的主次设备号,应用层指定好设备文件名,那么可以用mknod命令在/dev目录创建代表这个设备的文件,即此后应用程序对此文件的操作就是对其代表的设备操作,mknod用法如下:
@ cd /dev
@ mknod 设备文件名 设备种类(c为字符设备,b为块设备) 主设备号 次设备号 //ubuntu下需加
sudo执行
三、申请和注销设备号
字符驱动开发的第一步是通过模块的入口函数向内核添加本设备驱动的代码框架,主要完成:
-
申请设备号
-
定义、初始化、向内核添加代表本设备的结构体元素
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:手动分配设备号,先验证设备号是否被占用,如果没有则申请占用该设备号
参数:
from:自己指定的设备号
count:申请的设备数量
name:/proc/devices文件中与该设备对应的名字,方便用户层查询主设备号
返回值: 成功为0,失败负数,绝对值为错误码
入口函数
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count, count char, *name)
功能:动态分配设备号,查询内核里未被占用的设备号,如果找到则占用该设备号
参数:
dev:分配设备号成功后用来存放分配到的设备号 baseminior:起始的次设备号,一般为0
count:申请的设备数量
name:/proc/devices文件中与该设备对应的名字,方便用户层查询主次设备号
返回值: 成功为0,失败负数,绝对值为错误码
分配成功后在/proc/devices 可以查看到申请到主设备号和对应的设备名,mknod时参数可以参考查到的此设备信息
出口函数
void unregister_chrdev_region(dev_t from, unsigned count)
功能:释放设备号
参数:
from:已成功分配的设备号将被释放
count:申请成功的设备数量
释放后/proc/devices文件对应的记录消失。