【LV14 day4 字符设备驱动基础框架】

本文详细解析了Linux字符设备驱动中的关键概念,包括设备操作函数的角色、insmod和rmmod的作用,以及内核中文件元信息的管理。文章重点介绍了open、read、write等操作函数的工作原理和系统调用的交互过程。
摘要由CSDN通过智能技术生成

一、字符设备驱动框架解析

设备的操作函数如果比喻是桩的话(性质类似于设备操作函数的函数,在一些场合被称为桩函数),则:

驱动实现设备操作函数 ----------- 做桩

insmod调用的init函数主要作用 --------- 钉桩

rmmod调用的exitt函数主要作用 --------- 拔桩

应用层通过系统调用函数间接调用这些设备操作函数 ------- 用桩
1.1 两个操作函数中常用的结构体说明

内核中记录文件元信息的结构体
struct inode
{
//…
dev_t i_rdev;//设备号
struct cdev i_cdev;//如果是字符设备才有此成员,指向对应设备驱动程序中的加入系统的struct cdev对象
//…
}
/

1. 内核中每个该结构体对象对应着一个实际文件,一对一
2. open一个文件时如果内核中该文件对应的inode对象已存在则不再创建,不存在才创建
3. 内核中用此类型对象关联到对此文件的操作函数集(对设备而言就是关联到具体驱动代码)
*/

读写文件内容过程中用到的一些控制性数据组合而成的对象------文件操作引擎(文件操控器)
struct file
{
//…
mode_t f_mode;//不同用户的操作权限,驱动一般不用
loff_t f_pos;//position 数据位置指示器,需要控制数据开始读写位置的设备有用
unsigned int f_flags;//open时的第二个参数flags存放在此,驱动中常用
struct file_operations *f_op;//open时从struct inode中i_cdev的对应成员获得地址,驱动开发中用来协助理解工作原理,内核中使用
void *private_data;//本次打开文件的私有数据,驱动中常来在几个操作函数间传递共用数据
struct dentry f_dentry;//驱动中一般不用,除非需要访问对应文件的inode,用法flip->f_dentry->d_inode
int refcnt;//引用计数,保存着该对象地址的位置个数,close时发现refcnt为0才会销毁该struct file对象
//…
};
/

1. open函数被调用成功一次,则创建一个该对象,因此可以认为一个该类型的对象对应一次指定文件的操作
2. open同一个文件多次,每次open都会创建一个该类型的对象
3. 文件描述符数组中存放的地址指向该类型的对象
4. 每个文件描述符都对应一个struct file对象的地址
*/

1.2 字符设备驱动程序框架分析

驱动实现端:
在这里插入图片描述

驱动使用端:
在这里插入图片描述整体流程以open函数为例
应用层app系统调用open函数使用文件名,struct inode查询是否有该文件名的对象,若没有则创建,有则查询该设备的设备好devno对应的cdev对象,再通过cdev对象得到操作函数集fops,然后open函数创建struct file对象将操作函数集设置到该对象下面的成员f_ops里面,再把struc file对象填到描述符数组里面,得到文件描述符,同时调用驱动程序的drive_open函数,再通过system_open函数返回文件描述符给到应用层的open函数。

若要调用read函数则根据文件描述符找到对应的struct file 对象然后通过里面的f_ops操作函数集调用驱动程序对应的read函数。

syscall_open函数实现的伪代码:

int syscall_open(const char *filename,int flag)
{
dev_t devno;
struct inode *pnode = NULL;
struct cdev *pcdev = NULL;
struct file *pfile = NULL;
int fd = -1;

/*根据filename在内核中查找该文件对应的struct inode对象地址
    找到则pnode指向该对象
    未找到则创建新的struct inode对象,pnode指向该对象,并从文件系统中读取文件的元信息到该对象*/
if(/*未找到对应的struct inode对象*/)
{/*根据文件种类决定如何进行下面的操作,如果是字符设备则执行如下操作*/

	/*从pnode指向对象中得到设备号*/
    devno = pnode->i_rdev;

	/*用devno在字符设备链表查找对应节点,并将该节点的地址赋值给pcdev*/

	/*pcdev赋值给pnode的i_cdev成员*/
	pnode->i_cdev = pcdev;
}

/*创建struct file对象,并将该对象的地址赋值给pfile*/

pfile->f_op = pnode->i_cdev->ops;
pfile->f_flags = flag;

/*调用驱动程序的open函数*/
pfile->f_op->open(pnode,pfile,flag);

/*将struct file对象地址填入进程的描述符数组,得到对应位置的下标赋值给fd*/

return fd;

}

syscall_read函数实现的伪代码

int syscall_read(int fd,void *pbuf,int size)
{
struct file *pfile = NULL;
struct file_operations *fops = NULL;
int cnt;

/*将fd作为下标,在进程的描述符数组中获得struct file对象的地址赋值给pfile*/

/*从struct file对象的f_op成员中得到操作函数集对象地址赋值给fops*/

/*从操作函数集对象的read成员得到该设备对应的驱动程序中read函数,并调用之*/
cnt = fops->read(pfile,pbuf,size,&pfile->f_pos);

。。。。
return cnt;

}

1.3 参考原理图

字符设备驱动框架

Linux字符设备驱动工作原理图
1.4 常用操作函数说明

int (*open) (struct inode *, struct file ); //打开设备
/

指向函数一般用来对设备进行硬件上的初始化,对于一些简单的设备该函数只需要return 0,对应open系统调用,是open系统调用函数实现过程中调用的函数,
*/

int (*release) (struct inode *, struct file ); //关闭设备
/

,指向函数一般用来对设备进行硬件上的关闭操作,对于一些简单的设备该函数只需要return 0,对应close系统调用,是close系统调用函数实现过程中调用的函数
*/

ssize_t (*read) (struct file *, char __user *, size_t, loff_t ); //读设备
/

指向函数用来将设备产生的数据读到用户空间,对应read系统调用,是read系统调用函数实现过程中调用的函数
*/

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t ); //写设备
/

指向函数用来将用户空间的数据写进设备,对应write系统调用,是write系统调用函数实现过程中调用的函数
*/

loff_t (*llseek) (struct file , loff_t, int); //数据操作位置的定位
/

指向函数用来获取或设置设备数据的开始操作位置(位置指示器),对应lseek系统调用,是lseek系统调用函数实现过程中调用的函数
*/

long (*unlocked_ioctl) (struct file , unsigned int, unsigned long);//读写设备参数,读设备状态、控制设备
/

指向函数用来获取、设置设备一些属性或设备的工作方式等非数据读写操作,对应ioctl系统调用,是ioctl系统调用函数实现过程中调用的函数
*/

unsigned int (*poll) (struct file *, struct poll_table_struct );//POLL机制,实现对设备的多路复用方式的访问
/

指向函数用来协助多路复用机制完成对本设备可读、可写数据的监控,对应select、poll、epoll_wait系统调用,是select、poll、epoll_wait系统调用函数实现过程中调用的函数
*/

int (*fasync) (int, struct file , int); //信号驱动
/

指向函数用来创建信号驱动机制的引擎,对应fcntl系统调用的FASYNC标记设置,是fcntl系统调用函数FASYNC标记设置过程中调用的函数
*/

  • 17
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值