Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析

前面在 Linux 字符设备驱动开发基础 (三)—— 字符设备驱动结构(中) ,我们已经介绍了两种重要的数据结构 struct inode{...} struct file{...} ,下面来介绍另一个比较重要数据结构

struct _file_operations

struct _file_operations在Fs.h这个文件里面被定义的,如下所示:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct file_operations {   
  2.   
  3.     struct module *owner;//拥有该结构的模块的指针,一般为THIS_MODULES   
  4.   
  5.   
  6.     loff_t (*llseek) (struct file *, loff_t, int);//用来修改文件当前的读写位置   
  7.   
  8.     ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//从设备中同步读取数据   
  9.   
  10.     ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//向设备发送数据  
  11.   
  12.     ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的读取操作   
  13.   
  14.     ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的写入操作   
  15.   
  16.     int (*readdir) (struct file *, void *, filldir_t);//仅用于读取目录,对于设备文件,该字段为NULL   
  17.   
  18.     unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询函数,判断目前是否可以进行非阻塞的读写或写入   
  19.   
  20.     int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //执行设备I/O控制命令   
  21.   
  22.     long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系统,将使用此种函数指针代替ioctl   
  23.   
  24.     long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系统上,32位的ioctl调用将使用此函数指针代替   
  25.   
  26.   
  27.     int (*mmap) (struct file *, struct vm_area_struct *); //用于请求将设备内存映射到进程地址空间  
  28.    
  29.     int (*open) (struct inode *, struct file *); //打开   
  30.   
  31.     int (*flush) (struct file *, fl_owner_t id);   
  32.   
  33.     int (*release) (struct inode *, struct file *); //关闭   
  34.   
  35.     int (*fsync) (struct file *, struct dentry *, int datasync); //刷新待处理的数据   
  36.   
  37.     int (*aio_fsync) (struct kiocb *, int datasync); //异步刷新待处理的数据   
  38.   
  39.     int (*fasync) (intstruct file *, int); //通知设备FASYNC标志发生变化   
  40.   
  41.     int (*lock) (struct file *, intstruct file_lock *);   
  42.   
  43.     ssize_t (*sendpage) (struct file *, struct page *, intsize_t, loff_t *, int);   
  44.   
  45.     unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);   
  46.   
  47.     int (*check_flags)(int);   
  48.   
  49.     int (*flock) (struct file *, intstruct file_lock *);  
  50.    
  51.     ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);  
  52.    
  53.     ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);   
  54.   
  55.     int (*setlease)(struct file *, longstruct file_lock **);   
  56.   
  57. };  

      Linux使用file_operations结构访问驱动程序的函数,这个结构的每一个成员的名字都对应着一个调用。

     用户进程利用在对设备文件进行诸如read/write操作的时候,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是Linux的设备驱动程序工作的基本原理。


下面是各成员解析:

1、struct module *owner

       第一个 file_operations 成员根本不是一个操作,它是一个指向拥有这个结构的模块的指针。

      这个成员用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间中, 它被简单初始化为 THIS_MODULE, 一个在 <linux/module.h> 中定义的宏.这个宏比较复杂,在进行简单学习操作的时候,一般初始化为THIS_MODULE。


2、loff_t (*llseek) (struct file * filp , loff_t p, int orig);

      (指针参数filp为进行读取信息的目标文件结构体指针;参数 p 为文件定位的目标偏移量;参数orig为对文件定位的起始地址,这个值可以为文件开头(SEEK_SET,0,当前位置(SEEK_CUR,1),文件末尾(SEEK_END,2))

      llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值.

loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示;如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述).


3、ssize_t (*read) (struct file * filp, char __user * buffer, size_t    size , loff_t * p);

    (指针参数 filp 为进行读取信息的目标文件,指针参数buffer 为对应放置信息的缓冲区(即用户空间内存地址),参数size为要读取的信息长度,参数 p 为读的位置相对于文件开头的偏移,在读取信息后,这个指针一般都会移动,移动的值为要读取信息的长度值)

     这个函数用来从设备中获取数据。在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败。一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型).


4、ssize_t (*aio_read)(struct kiocb * , char __user * buffer, size_t size , loff_t   p);

      可以看出,这个函数的第一、三个参数和本结构体中的read()函数的第一、三个参数是不同 的,异步读写的第三个参数直接传递值,而同步读写的第三个参数传递的是指针,因为AIO从来不需要改变文件的位置。异步读写的第一个参数为指向kiocb结构体的指针,而同步读写的第一参数为指向file结构体的指针,每一个I/O请求都对应一个kiocb结构体);初始化一个异步读 -- 可能在函数返回前不结束的读操作.如果这个方法是 NULL, 所有的操作会由 read 代替进行(同步地).(有关linux异步I/O,可以参考有关的资料,《linux设备驱动开发详解》中给出了详细的解答)


5、ssize_t (*write) (struct file * filp, const char __user *   buffer, size_t count, loff_t * ppos);

     (参数filp为目标文件结构体指针,buffer为要写入文件的信息缓冲区,count为要写入信息的长度,ppos为当前的偏移位置,这个值通常是用来判断写文件是否越界)

     发送数据给设备.。如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数。

     (注:这个操作和上面的对文件进行读的操作均为阻塞操作


6、ssize_t (*aio_write)(struct kiocb *, const char __user * buffer, size_t count, loff_t * ppos);

      初始化设备上的一个异步写.参数类型同aio_read()函数;


7、int (*readdir) (struct file * filp, void *, filldir_t);

      对于设备文件这个成员应当为 NULL; 它用来读取目录, 并且仅对文件系统有用.


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

     (这是一个设备驱动中的轮询函数,第一个参数为file结构指针,第二个为轮询表指针)

     这个函数返回设备资源的可获取状态,即POLLIN,POLLOUT,POLLPRI,POLLERR,POLLNVAL等宏的位“或”结果。每个宏都表明设备的一种状态,如:POLLIN(定义为0x0001)意味着设备可以无阻塞的读,POLLOUT(定义为0x0004)意味着设备可以无阻塞的写。

     (poll 方法是 3 个系统调用的后端: poll, epoll, 和 select, 都用作查询对一个或多个文件描述符的读或写是否会阻塞.poll 方法应当返回一个位掩码指示是否非阻塞的读或写是可能的, 并且, 可能地, 提供给内核信息用来使调用进程睡眠直到 I/O 变为可能. 如果一个驱动的 poll 方法为 NULL, 设备假定为不阻塞地可读可写.

    (这里通常将设备看作一个文件进行相关的操作,而轮询操作的取值直接关系到设备的响应情况,可以是阻塞操作结果,同时也可以是非阻塞操作结果)


9、int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

       (inode 和 filp 指针是对应应用程序传递的文件描述符 fd 的值, 和传递给 open 方法的相同参数.cmd 参数从用户那里不改变地传下来, 并且可选的参数 arg 参数以一个 unsigned long 的形式传递, 不管它是否由用户给定为一个整数或一个指针.如果调用程序不传递第 3 个参数, 被驱动操作收到的 arg 值是无定义的.因为类型检查在这个额外参数上被关闭, 编译器不能警告你如果一个无效的参数被传递给 ioctl, 并且任何关联的错误将难以查找.)

      ioctl 系统调用提供了发出设备特定命令的方法(例如格式化软盘的一个磁道, 这不是读也不是写). 另外, 几个 ioctl 命令被内核识别而不必引用 fops 表.如果设备不提供 ioctl 方法, 对于任何未事先定义的请求(-ENOTTY, "设备无这样的 ioctl"), 系统调用返回一个错误.


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

        mmap 用来请求将设备内存映射到进程的地址空间。 如果这个方法是 NULL, mmap 系统调用返回 -ENODEV.

       (如果想对这个函数有个彻底的了解,那么请看有关“进程地址空间”介绍的书籍)


11、int (*open) (struct inode * inode , struct file * filp ) ;

        (inode 为文件节点,这个节点只有一个,无论用户打开多少个文件,都只是对应着一个inode结构;但是filp就不同,只要打开一个文件,就对应着一个file结构体,file结构体通常用来追踪文件在运行时的状态信息)

       尽管这常常是对设备文件进行的第一个操作, 不要求驱动声明一个对应的方法. 如果这个项是 NULL, 设备打开一直成功, 但是你的驱动不会得到通知.与open()函数对应的是release()函数。


12、int (*flush) (struct file *);

        flush 操作在进程关闭它的设备文件描述符的拷贝时调用; 

       它应当执行(并且等待)设备的任何未完成的操作.这个必须不要和用户查询请求的 fsync 操作混淆了. 当前, flush 在很少驱动中使用;SCSI 磁带驱动使用它, 例如, 为确保所有写的数据在设备关闭前写到磁带上. 如果 flush 为 NULL, 内核简单地忽略用户应用程序的请求.


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

        release ()函数当最后一个打开设备的用户进程执行close()系统调用的时候,内核将调用驱动程序release()函数:

       void release(struct inode inode,struct file *file),release函数的主要任务是清理未结束的输入输出操作,释放资源,用户自定义排他标志的复位等。在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL.


14、int(*synch)(struct file *,struct dentry *,int datasync);

       刷新待处理的数据,允许进程把所有的脏缓冲区刷新到磁盘。


15、int (*aio_fsync)(struct kiocb *, int);

        这是 fsync 方法的异步版本.所谓的fsync方法是一个系统调用函数。系统调用fsync把文件所指定的文件的所有脏缓冲区写到磁盘中(如果需要,还包括存有索引节点的缓冲区)。相应的服务例程获得文件对象的地址,并随后调用fsync方法。通常这个方法以调用函数__writeback_single_inode()结束,这个函数把与被选中的索引节点相关的脏页和索引节点本身都写回磁盘


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

        这个函数是系统支持异步通知的设备驱动,下面是这个函数的模板:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static int ***_fasync(int fd,struct file *filp,int mode)  
  2. {  
  3.     struct ***_dev * dev=filp->private_data;  
  4.     return fasync_helper(fd,filp,mode,&dev->async_queue);//第四个参数为 fasync_struct结构体指针的指针。  
  5. //这个函数是用来处理FASYNC标志的函数。(FASYNC:表示兼容BSD的fcntl同步操作)当这个标志改变时,驱动程序中的fasync()函数将得到执行。  (注:感觉这个‘标志'词用的并不恰当)  
  6. }  
此操作用来通知设备它的 FASYNC 标志的改变. 异步通知是一个高级的主题, 在第 6 章中描述.这个成员可以是NULL 如果驱动不支持异步通知.


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

        lock 方法用来实现文件加锁; 加锁对常规文件是必不可少的特性, 但是设备驱动几乎从不实现它.


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

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

这些方法实现发散/汇聚读和写操作. 应用程序偶尔需要做一个包含多个内存区的单个读或写操作;这些系统调用允许它们这样做而不必对数据进行额外拷贝. 如果这些函数指针为 NULL, read 和 write 方法被调用( 可能多于一次 ).


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

       这个方法实现 sendfile 系统调用的读, 使用最少的拷贝从一个文件描述符搬移数据到另一个.

       例如, 它被一个需要发送文件内容到一个网络连接的 web 服务器使用. 设备驱动常常使 sendfile 为 NULL.


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

        sendpage 是 sendfile 的另一半; 它由内核调用来发送数据, 一次一页, 到对应的文件. 设备驱动实际上不实现 sendpage.


21、unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

         这个方法的目的是在进程的地址空间找一个合适的位置来映射在底层设备上的内存段中。这个任务通常由内存管理代码进行; 这个方法存在为了使驱动能强制特殊设备可能有的任何的对齐请求. 大部分驱动可以置这个方法为 NULL.[10]


22、int (*check_flags)(int)

       这个方法允许模块检查传递给 fnctl(F_SETFL...) 调用的标志.


23、int (*dir_notify)(struct file *, unsigned long);

        这个方法在应用程序使用 fcntl 来请求目录改变通知时调用. 只对文件系统有用; 驱动不需要实现 dir_notify.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402127.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android开发群

为什么80%的码农都做不了架构师&#xff1f;>>> 我的自建Android应用开发群&#xff0c;欢迎大家来聊聊呀&#xff01;201427584 转载于:https://my.oschina.net/catia/blog/176384

Linux 字符设备驱动结构(三)—— file、inode结构体及chardevs数组等相关知识解析

前面我们学习了字符设备结构体cdev Linux 字符设备驱动开发 &#xff08;一&#xff09;—— 字符设备驱动结构&#xff08;上&#xff09; 下面继续学习字符设备另外几个重要的数据结构。 先看下面这张图&#xff0c;这是Linux 中虚拟文件系统、一般的设备文件与设备驱动程序…

技术人生:三亚之行

人生收获 此次是公司组团去的三亚&#xff0c;地接的导游非常热情&#xff0c;如同大多数人一样&#xff0c;导游也会在这短短的几天内&#xff0c;尽可能的表现自己&#xff0c;此文聊聊导游说的三句话。 旅游三不“较”&#xff1a; 不比较不计较不睡觉人生何尝不是如此&…

Linux 字符设备驱动结构(二)—— 自动创建设备节点

上一篇我们介绍到创建设备文件的方法&#xff0c;利用cat /proc/devices查看申请到的设备名&#xff0c;设备号。 第一种是使用mknod手工创建&#xff1a;mknod filename type major minor 第二种是自动创建设备节点&#xff1a;利用udev&#xff08;mdev&#xff09;来实现设备…

Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

一、字符设备基础知识 1、设备驱动分类 linux系统将设备分为3类&#xff1a;字符设备、块设备、网络设备。使用驱动程序&#xff1a; 字符设备&#xff1a;是指只能一个字节一个字节读写的设备&#xff0c;不能随机读取设备内存中的某一数据&#xff0c;读取数据需要按照先后数…

Linux 驱动开发之内核模块开发(四)—— 符号表的导出

Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用,应当使用下面的宏定义: EXPORT_SYMBOL(name); EXPORT_SYMBO…

Linux 驱动开发之内核模块开发 (三)—— 模块传参

一、module_param() 定义 通常在用户态下编程&#xff0c;即应用程序&#xff0c;可以通过main()的来传递命令行参数&#xff0c;而编写一个内核模块&#xff0c;则通过module_param() 来传参。 module_param()宏是Linux 2.6内核中新增的&#xff0c;该宏被定义在include/linux…

Exynos4412 文件系统制作(二)—— 文件系统简介

一、Linux磁盘分区和目录 Linux发行版本之间的差别很少&#xff0c;差别主要表现在系统管理的特色工具以及软件包管理方式的不同。目录结构基本上都是一样的。 Windows的文件结构是多个并列的树状结构&#xff0c;最顶部的是不同的磁盘&#xff08;分区&#xff09;&#xff0c…

MM引擎新应用——爱车加油记

基于MM应用引擎开发的EXTJS应用&#xff0c;车主每次加完汽油后&#xff0c;记录加油时的里程数以及加油金额和汽油价格&#xff0c;就可计算出上次加油后行驶的里程数、上次加油的平均油耗。点击刷新按钮&#xff0c;即可计算有记录以来的行驶公里数和再次期间加油金额和平均油…

### 阅读之痕-2013/11

2019独角兽企业重金招聘Python工程师标准>>> 阅读之痕-2013/11 Andy erpingwugmail.com 2013/11/01-2013/11/30 2013/11/01-2013/11/30 The story of rocksdb - Embedded key-value store for Flash and RAM http://rocksdb.org/intro.pdf High Performance Network…

Exynos4412 内核移植(六)—— 设备树解析

一、描述ARM Device Tree起源于OpenFirmware (OF)&#xff0c;在过去的Linux中&#xff0c;arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码&#xff0c;相当多数的代码只是在描述板级细节&#xff0c;而这些板级细节对于内核来讲&#xff0c;不过是垃圾&#xff…

Github Page创建个人主页以及绑定域名

2019独角兽企业重金招聘Python工程师标准>>> 在github中 我们可以通过github page创建个人主页 以及绑定域名 据说有300m空间 以及无限流量 不过只能支持静态html 以及一些脚本语言 顺便吐槽一下 阿里云最低配那个云空间服务器 512m内存 启动web服务器后 mys…

Exynos4412 内核移植(五)—— 驱动的移植

以移植自己制作的驱动&#xff0c;学习内核移植中的驱动移植&#xff0c;及 驱动程序的动态编译和静态编译 硬件环境&#xff1a; Linux 内核版本&#xff1a;Linux 3.14 主机&#xff1a;Ubuntu 12.04发行版 目标机&#xff1a;FS4412平台 交叉编译工具&#xff1a;arm-none-l…

FTP文件共传输服务

FTP文件共传输服务一&#xff0c;vsftpd服务基础&#xff08;1&#xff09;&#xff0c;FTP服务概述FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;是典型的C/S结构的应用层协议&#xff0c;需要由服务端软件、客户端软件共同实现文件传达输功能…

Exynos4412 内核移植(四)—— MMU 相关知识解析

一、MMU的产生 许多年以前&#xff0c;当人们还在使用DOS或是更古老的操作系统的时候&#xff0c;计算机的内存还非常小&#xff0c;一般都是以K为单位进行计算&#xff0c;相应的&#xff0c;当时的程序规模也不大&#xff0c;所以内存容量虽然小&#xff0c;但还是可以容纳当…

Mysql limit 子查询

为什么80%的码农都做不了架构师&#xff1f;>>> &#xff08;1&#xff09;mysql limit 不支持子查询像下面这条语句无法执行 SELECT * FROM b_c_s1 where CT_ID IN (SELECT DISTINCT CT_ID FROM b_c_s1 LIMIT 0,2); &#xff08;2&#xff09;应该在只查询外面再…

Exynos4412 内核移植(二)—— 内核编译过程分析

内核的编译同样是从Makefile 来分析&#xff1a; 一、内核源码结构 Linux内核文件数目近2万&#xff0c;出去其他架构CPU的相关文件&#xff0c;他们分别位于顶层目录下的17个子目录&#xff0c;各个目录功能独立&#xff0c;下面是常用目录&#xff1a; arch:体系结构相关代码…

深入理解Java:注解(Annotation)

2019独角兽企业重金招聘Python工程师标准>>> 一、概述 1.什么是注解&#xff08;Annotation&#xff09; Annotation&#xff08;注解&#xff09;就是Java提供了一种元程序中的元素关联任何信息和着任何元数据&#xff08;metadata&#xff09;的途径和方法。Ann…

Exynos4412 内核移植(三)—— 内核启动过程分析

内核启动所用函数如下&#xff1a; 与移植U-Boot 的过程相似&#xff0c;在移植Linux 之前&#xff0c;先了解它的启动过程。Linux 的过程可以分为两部分&#xff1a;架构/开发板相关的引导过程、后续的通用启动过程。对于uImage、zImage ,它们首先进行自解压得到vmlinux &…