驱动实现设备操作函数 ----------- 做桩
insmod调用的init函数主要作用 --------- 钉桩
rmmod调用的exitt函数主要作用 --------- 拔桩
应用层通过系统调用函数间接调用这些设备操作函数 ------- 用桩
内核中->记录文件元信息的结构体
struct inode (存在于链表中) ---->对应着外存中的某一个文件 1对1对应
{
//....
dev_t ?i_rdev;//设备号
struct cdev ?*i_cdev;//如果是字符设备才有此成员,指向对应设备驱动程序中的加入系统的struct cdev对象
//....
}
/*
1. 内核中每个该结构体对象对应着一个实际文件,一对一
2. open一个文件时如果内核中该文件对应的inode对象已存在则不再创建,不存在才创建
3. 内核中用此类型对象关联到对此文件的操作函数集(对设备而言就是关联到具体驱动代码)
*/
读写文件内容过程中用到的一些控制性数据组合而成的对象------文件操作引擎(文件操控器)
struct file 由数组进行管理 与open函数1对1对应 数组的下标即为open函数的返回值
{
//...
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对象的地址
*/
驱动实现端:
?
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; }
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标记设置过程中调用的函数 */