重要的数据结构-file_operations、file、inode
更多内容请参考Linux设备驱动程序学习----目录
重要的数据结构
上一节中设备编号的注册仅仅是驱动程序代码必须完成的许多工作中的第一件事。大部分基本大的驱动程序操作涉及到三个重要的内核数据结构,分别是file_operations、file、inode。
文件操作file_operations结构
file_operations结构用来将所有驱动程序操作连接到设备编号。该结构定义在<linux/fs.h>中,其中包含了一组函数指针。每个打开的文件,和一组函数关联;文件在内部由一个file结构表示,函数组包含在指向一个file_operations结构的fops字段。这些操作主要用来实现系统调用,如:read、write等。可以认为文件是一个“对象”,而操作它的函数是“方法”。
file_operations结构或者指向这类结构的指针称为fops,这个结构中的每一个字段都必须指向驱动程序中实现特定操作的函数,对于不支持的操作,对应的字段可置为NULL值。
__user字符串,表明指针是一个用户空间地址,因此不能被直接引用,对编译来说,并没有任何效果。
。。。。。
scull设备驱动程序所实现的只是几个重要的设备方法,如下初始化操作:
struct file_operations fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
这个声明操作采用了标准C的标记化结构初始化语法。标记化的初始化方法允许对结构成员进行重新排序。某些场合下,将频繁被访问的成员放在相同的硬件缓存行上,将大大提高性能。
file结构
file结构代表一个打开的文件,它并不仅限定于设备驱动程序,系统中每个打开的文件在内核空间都有一个对应的file结构。file结构由内核在open时创建,并传递给在该文件上进行操作的所有函数,直到最后的close函数。在文件的所有实例都被关闭之后,内核会释放这个file数据结构。定义在头文件<linux/fs.h>中,struct file是一个内核结构,不会出现在用户程序中。
常用的结构成员如下所示:
mode_t f_mode;
文件模式。通过FMODE_READ和FMODE_WRITE位来标识文件是否可读或可写或可读写。由于内核在调用驱动程序的read和write前已经检查了访问权限,所以驱动程序不必为这两个方法检查权限。
loff_t f_ops;
当前的读/写位置。loff_t是一个64位的数。用来修改文件位置。驱动程序可以读取这个值来获取文件中的当前位置。
unsigned int f_flags;
文件标志。如:O_RDONLY、O_NONBLOCK、O_SYNC。为了检查用户请求的是否是阻塞式操作。驱动程序需要检查O_NONBLOCK标志。注意,检查读/写权限应该查看f_mode而不是f_flags。标志定义在头文件<linux/fcntl.h>中。
struct file_operations *f_op;
与文件相关的操作。内核在执行open操作时对这个指针赋值,以后需要处理这些操作时就读取这个指针。
void *private_data;
open系统调用在调用驱动程序的open方法前将这个指针置为NULL。private_data是跨系统调用时保存状态信息的非常有用的资源。
inode结构
内核用inode结构在内部表示文件,和file结构不同,file结构表示打开的文件描述符。对单个文件,可能会由许多个表示打开的文件描述符的file结构,但它们都指向单个inode结构。
inode结构中以下两个字段对编写驱动程序代码比较重要:
dev_t i_rdev;
// 对表示设备文件的inode结构,该字段包含了真正的设备编号;
struct cdev \*i_cdev;
// struct cdev是表示字符设备的内核的内部结构。当inode指向一个字符设备文件时,该字段包含了指向struct cdev结构的指针;
从一个inode中获得主设备号和次设备号:
unsigned int imajor(struct inode *inode);
unsigned int iminor(struct inode *inode);
为了防止因为内核版本升级引起的不兼容问题,应该使用上面的两个宏,而不是直接操作i_rdev。
更多内容请参考Linux设备驱动程序学习----目录