|
本帖最后由 batou 于 2020-8-14 12:44 编辑
linux字符设备驱动学的是云里雾里,一团乱麻,剪不断理还乱… 放弃?
不,反正闲着也是闲着,打王者还老遇到坑,免不了上火!
那就一点一点来吧,弄一点是一点
=============================================================
开始可能很多同学跟我一样,从代码入手,点亮一个led,急于看到结果,然后一顿操作猛如虎,驱动学的特别苦,面试一问二百五!
因此,现在我想改变下策略,从基本概念开始,不急着去写代码,先去了解下字符设备驱动框架,然后去弄懂如下几个概念:
1、啥是设备号,用来干嘛的
2、file_operations结构体的作用
3、cdev是啥
上面这三个应该就是字符设备驱动的关键了,把它们弄熟了才能更好的编写字符设备驱动
=============================================================
概念理解
1、字符设备驱动框架
字符设备是Linux三大设备之一,另外两种我就不提了,字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,所以学好它是我们嵌入式开发人员势在必行的。
常见的字符设备包括鼠标、键盘、显示器、串口等,用ls -l /dev指令可以查看很多设备文件,c就是字符设备, 后面的数值就是主设备号和次设备号,这个特别重要!
2、啥是设备号,用来干嘛的?
设备号又分为主设备号和次设备号
主设备号用来区分不同硬件设备类型,如串口和USB之间的区别,表示对应的驱动程序,也就是说一个主设备号对应一个驱动程序。
次设备号用来区分同一类型的多个设备,如串口1和串口2之间的区别,由内核使用,用于确定/dev下的设备文件对应的具体设备。
作用:有句话应该大家都知道,linux下兼文件,各种设备都以文件的形式存放在/dev目录下,称为设备文件
应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了
号,所以每个设备号就分为了主设备号和次设备号。
其中与设备号相关的比较重要的三个宏【代码中尽量使用宏,减少系统兼容性问题,不同系统主次设备号位数可能不同】
#define MAJOR(dev) ((dev)>>8)
#define MINOR(dev) ((dev) & 0xff)
#define MKDEV(ma,mi) ((ma)<<8 | (mi))
3、file_operations结构体的作用
file_operations结构体是字符设备驱动与内核的接口,是用户空间对Linux进行系统调用最终的落实者, 这个结构体包含对文件打开,关闭,读写,控制的一系列成员函数。
//结构体定义如下:
static struct file_operations chrdev_fops = {
.owner = THIS_MODULE, //指向拥有这个结构的模块的指针.用来在它的操作还在被使用时阻止模块被卸载
.open = chrdev_open, //以下都是些常用接口,应用层调用时对应的驱动层实现
.release = chrdev_release,
.write = chrdev_write,
.read = chrdev_read,
};
4、cdev是啥
cdev是字符设备对象结构体,是linux用来管理字符设备的,其在内核中采用数组结构设计,这样系统中有多少个主设备号就约定了数组大小,此设备号采用链表管理,同一主设备号下可以有多个子设备。
cdev 结构体中包含 设备号 dev_t 和 file_operations 结构指针
说白了,cdev 就是用来描述一个字符设备的,结构体如下:
struct cdev {
struct kobject kobj; //内嵌的内核对象.
struct module *owner; //该字符设备所在的内核模块的对象指针.
const struct file_operations *ops; //该结构描述了字符设备所能实现的方法,是非常关键的一个结构体.
struct list_head list; //用来将已经向内核注册的所有字符设备形成链表.
dev_t dev; //字符设备的设备号,由主设备号和次设备号构成.
unsigned int count; //同一主设备号下的次设备号的个数.
};
跟cdev相关的有四个比较重要的函数:
struct cdev *cdev_alloc(void); //申请一个cdev,也可以定义变量一样弄一个
void cdev_init(struct cdev*, const struct file_opeartions*); //初始化cdev设备对象,其实就是跟file_opeartions进行绑定, 相当于两个人私定终身了
int cdev_add(struct cdev* , dev_t, unsigned); //注册字符设备对象cdev到内核,相当于两人结婚领证了,从此系统可查,受国家法律保护
void cdev_del(struct cdev* ); //从内核注销cdev设备对象
扩展:应用程序是怎么跟驱动程序产生联系的呢,比如open函数,其调用大致过程如下:
1、应用程序open打开一个设备节点文件:int open("/dev/text", O_RDWR);
2、会产生open系统调用,然后进入内核,调用sys_open函数,就直接到VFS层了,并产生struct file表示一个打开的文件
3、然后VFS虚文件系统open根据传进来的路径转换为inode,通过 inode 节点获取到文件设备号
4、遍历 cdev 链表,与此文件的设备号进行比较,如果相同则表示匹配成功
5、将匹配成功的 cdev 结构体中的 file_operations 赋值给此文件的 struct file,从而关联到驱动层中的file_operations
6、最后根据file_operations中函数指针就可以找到该结构体中对该种文件操作的所有方法
大致过程就是:应用层open --> 系统调用sys_open --> VFS层 --> 得到inode --> 获取设备号 --> 遍历cdev --> 设备号匹配就进行关联
|
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|