关于file_operations结构体

2024-02-22 06:08
文章标签 结构 file operations

本文主要是介绍关于file_operations结构体,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载自:http://www.cnblogs.com/sunyubo/archive/2010/12/22/2282079.html

结构体file_operations在头文件 linux/fs.h中定义,用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。该结构体的每个域都对应着驱动内核模块用来处理某个被请求的 事务的函数的地址。

举个例子,每个字符设备需要定义一个用来读取设备数据的函数。结构体 file_operations中存储着内核模块中执行这项操作的函数的地址。一下是该结构体 在内核2.6.5中看起来的样子:

struct file_operations {

  struct module *owner;

  loff_t(*llseek) (struct file *, loff_t, int);

  ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);

  ssize_t(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);

  ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);

  ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);

  int (*readdir) (struct file *, void *, filldir_t);

  unsigned int (*poll) (struct file *, struct poll_table_struct *);

  int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

  int (*mmap) (struct file *, struct vm_area_struct *);

  int (*open) (struct inode *, struct file *);

  int (*flush) (struct file *);

  int (*release) (struct inode *, struct file *);

  int (*fsync) (struct file *, struct dentry *, int datasync);

  int (*aio_fsync) (struct kiocb *, int datasync);

  int (*fasync) (int, struct file *, int);

  int (*lock) (struct file *, int, struct file_lock *);

  ssize_t(*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

  ssize_t(*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

  ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void __user *);

  ssize_t(*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

  unsigned long (*get_unmapped_area) (struct file *, unsigned long,

         unsigned long, unsigned long,

         unsigned long);

};

驱动内核模块是不需要实现每个函数的。像视频卡的驱动就不需要从目录的结构 中读取数据。那么,相对应的file_operations重的项就为 NULL。

gcc还有一个方便使用这种结构体的扩展。你会在较现代的驱动内核模块中见到。新的使用这种结构体的方式如下:

struct file_operations fops = {

 read: device_read,

 write: device_write,

 open: device_open,

 release: device_release

};

同样也有C99语法的使用该结构体的方法,并且它比GNU扩展更受推荐。我使用的版本为 2.95为了方便那些想移植你的代码的人,你最好使用这种语法。它将提高代码的兼容性:

struct file_operations fops = {

 .read = device_read,

 .write = device_write,

 .open = device_open,

 .release = device_release

};

这种语法很清晰,你也必须清楚的意识到没有显示声明的结构体成员都被gcc初始化为NULL。

指向结构体struct file_operations的指针通常命名为fops。

 

关于file结构体

每一个设备文件都代表着内核中的一个file结构体。该结构体在头文件linux/fs.h定义。注意,file结构体是内核空间的结构体, 这意味着它不会在用户程序的代码中出现。它绝对不是在glibc中定义的FILE。 FILE自己也从不在内核空间的函数中出现。它的名字确实挺让人迷惑的。它代表着一个抽象的打开的文件,但不是那种在磁盘上用结构体inode表示的文件。

指向结构体struct file的指针通常命名为filp。你同样可以看到struct file file的表达方式,但不要被它诱惑。

去看看结构体file的定义。大部分的函数入口,像结构体struct dentry没有被设备驱动模块使用,你大可忽略它们。这是因为设备驱动模块并不自己直接填充结构体file:它们只是使用在别处建立的结构体file中的数据。

 

注册一个设备

如同先前讨论的,字符设备通常通过在路径/dev下的设备文件进行访问。主设备号告诉你哪些驱动模块是用来操纵哪些硬件设备的。从设备号是驱动模块自己使用来区别它操纵的不同设备,当此驱动模块操纵不只一个设备时。

将内核驱动模块加载入内核意味着要向内核注册自己。这个工作是和驱动模块获得主设备号时初始化一同进行的。你可以使用头文件linux/fs.h中的函数register_chrdev来实现。

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

其中unsigned int major是你申请的主设备号,const char *name是将要在文件/proc/devices中显示的名称,struct file_operations *fops是指向你的驱动模块的file_operations表的指针。负的返回值意味着注册失败。注意注册并不需要提供从设备号。内核本身并不在意从设备号。

现在的问题是你如何申请到一个没有被使用的主设备号?最简单的方法是查看文件 Documentation/devices.txt从中挑选一个没有被使用的。这不是一劳永逸的方法因为你无法得知该主设备号在将来会被占用。最终的方法是让内核为你动态分配一个。

如果你向函数register_chrdev传递为0的主设备号,那么返回的就是动态分配的主设备号。副作用就是既然你无法得知主设备号,你就无法预先建立一个设备文件。有多种解决方法:第一种方法是新注册的驱动模块会输出自己新分配到的主设备号,所以我们可以手工建立需要的设备文件。第二种是利用文件/proc/devices新注册的驱动模块的入口,要么手工建立设备文件,要么编一个脚本去自动读取该文件并且生成设备文件。第三种是在我们的模块中,当注册成功时,使用mknod系统调用建立设备文件并且在驱动模块调用函数cleanup_module前,调用rm删除该设备文件。

 

注销一个设备

即使是root也不能允许随意卸载内核模块。当一个进程已经打开一个设备文件时我们卸载了该设备文件使用的内核模块,我们此时再对该文件的访问将会导致对已卸载的内核模块代码内存区的访问。幸运的话我们最多获得一个讨厌的错误警告。如果此时已经在该内存区加载了另一个模块,倒霉的你将会在内核中跳转执行意料外的代码。结果是无法预料的,而且多半是不那么令人愉快的。

平常,当你不允许某项操作时,你会得到该操作返回的错误值(一般为负值)。但对于无返回值的函数cleanup_module这是不可能的。然而,却有一个计数器跟踪着有多少进程正在使用该模块。你可以通过查看文件 /proc/modules的第三列来获取这些信息。如果该值非零,则卸载就会失败。你不需要在你模块中的函数cleanup_module中检查该计数器,因为该项检查由头文件linux/module.c中定义的系统调用sys_delete_module完成。你也不应该直接对该计数器进行操作。你应该使用在文件linux/modules.h定义的宏来增加,减小和读取该计数器:

try_module_get(THIS_MODULE): Increment the use count.

try_module_put(THIS_MODULE): Decrement the use count.

保持该计数器时刻精确是非常重要的;如果你丢失了正确的计数,你将无法卸载模块,那就只有重启了。不过这种情况在今后编写内核模块时也是无法避免的。

chardev.c

下面的代码示范了一个叫做chardev的字符设备。你可以用cat输出该设备文件的内容(或用别的程序打开它)时,驱动模块会将该设备文件被读取的次数显示。目前对设备文件的写操作还不被支持(像echo "hi" > /dev/hello),但会捕捉这些操作并且告诉用户该操作不被支持。不要担心我们对读入缓冲区的数据做了什么;我们什么都没做。我们只是读入数据并输出我们已经接收到的数据的信息。

为多个版本的内核编写内核模块

系统调用,也就是内核提供给进程的接口,基本上是保持不变的。也许会添入新的系统调用,但那些已有的不会被改动。这对于向下兼容是非常重要的。在多数情况下,设备文件是保持不变的。但内核的内部在不同版本之间还是会有区别的。

Linux内核分为稳定版本(版本号中间为偶数)和试验版本(版本号中间为奇数)。试验版本中可以试验各种各样的新而酷的主意,有些会被证实是一个错误,有些在下一版中会被完善。总之,你不能依赖这些版本中的接口(这也是我不在本文档中支持它们的原因, 它们更新的太快了)。在稳定版本中,我们可以期望接口保持一致,除了那些修改代码中错误的版本。

如果你要支持多版本的内核,你需要编写为不同内核编译的代码树。可以通过比较宏 LINUX_VERSION_CODE和宏KERNEL_VERSION在版本号为a.b.c 的内核中,该宏的值应该为 2^16×a+2^8×b+c

在上一个版本中该文档还保留了详细的如何向后兼容老内核的介绍,现在我们决定打破这个传统。 对为老内核编写驱动感兴趣的读者应该参考对应版本的LKMPG,也就是说,2.4.x版本的LKMPG对应2.4.x的内核,2.6.x版本的LKMPG对应2.6.x的内核。

这篇关于关于file_operations结构体的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/734332

相关文章

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C++11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使用了由[90]描述的第一个算法。开发者应该注意,由于数据点靠近包含的 Mat 元素的边界,返回的椭圆/旋转矩形数据

C语言程序设计(选择结构程序设计)

一、关系运算符和关系表达式 1.1关系运算符及其优先次序 ①<(小于) ②<=(小于或等于) ③>(大于) ④>=(大于或等于 ) ⑤==(等于) ⑥!=(不等于) 说明: 前4个优先级相同,后2个优先级相同,关系运算符的优先级低于算术运算符,关系运算符的优先级高于赋值运算符 1.2关系表达式 用关系运算符将两个表达式(可以是算术表达式或关系表达式,逻辑表达式,赋值表达式,字符

Science|癌症中三级淋巴结构的免疫调节作用与治疗潜力|顶刊精析·24-09-08

小罗碎碎念 Science文献精析 今天精析的这一篇综述,于2022-01-07发表于Science,主要讨论了癌症中的三级淋巴结构(Tertiary Lymphoid Structures, TLS)及其在肿瘤免疫反应中的作用。 作者类型作者姓名单位名称(中文)通讯作者介绍第一作者Ton N. Schumacher荷兰癌症研究所通讯作者之一通讯作者Daniela S. Thomm

oracle11.2g递归查询(树形结构查询)

转自: 一 二 简单语法介绍 一、树型表结构:节点ID 上级ID 节点名称二、公式: select 节点ID,节点名称,levelfrom 表connect by prior 节点ID=上级节点IDstart with 上级节点ID=节点值 oracle官网解说 开发人员:SQL 递归: 在 Oracle Database 11g 第 2 版中查询层次结构数据的快速

Open a folder or workspace... (File -> Open Folder)

问题:vscode Open with Live Server 时 显示Open a folder or workspace... (File -> Open Folder)报错 解决:不可以单独打开文件1.html ; 需要在文件夹里打开 像这样

android java.io.IOException: open failed: ENOENT (No such file or directory)-api23+权限受权

问题描述 在安卓上,清单明明已经受权了读写文件权限,但偏偏就是创建不了目录和文件 调用mkdirs()总是返回false. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_E

Tomcat下载压缩包解压后应有如下文件结构

1、bin:存放启动和关闭Tomcat的命令的路径。 2、conf:存放Tomcat的配置,所有的Tomcat的配置都在该路径下设置。 3、lib:存放Tomcat服务器的核心类库(JAR文件),如果需要扩展Tomcat功能,也可将第三方类库复制到该路径下。 4、logs:这是一个空路径,该路径用于保存Tomcat每次运行后产生的日志。 5、temp:保存Web应用运行过程中生成的临时文件

C和指针:结构体(struct)和联合(union)

结构体和联合 结构体 结构体包含一些数据成员,每个成员可能具有不同的类型。 数组的元素长度相同,可以通过下标访问(转换为指针)。但是结构体的成员可能长度不同,所以不能用下标来访问它们。成员有自己的名字,可以通过名字访问成员。 结构声明 在声明结构时,必须列出它包含的所有成员。 struct tag {member-list} variable-list ; 定义一个结构体变量x(包含