首页 > 系统相关 >linux——file_operations

linux——file_operations

时间:2023-04-18 15:00:57浏览次数:35  
标签:operations 文件 调用 struct int file linux 设备

结构体源码

[[03.file_operations结构体源码]]
![[Pasted image 78.png]]

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


Linux的设备驱动程序工作的基本原理

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


成员解析:

1、struct module *owner

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


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" 类型, 常常是目标平台本地的整数类型).


5、ssize_t (*write)

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

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

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

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


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

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


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

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


9、int (*ioctl)

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

v2.6.36 后使用unlocked_ioctl替代ioctl,原先的ioctl使用了内核的大锁。由于失去了大内核锁的保护,所以必须在unlocked_ioctl方法中自行实现锁机制,以保证不会在操作设备的时候(特别在SMP系统中)产生竞态。(也就实现了用小锁替换大锁)
另外,unlocked_ioctl中不包含inode

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

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



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 结构" 一节中描述).

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设备驱动开发详解》中给出了详细的解答)


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, 设备假定为不阻塞地可读可写.

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

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

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

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

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

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

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

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

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);

    这个函数是系统支持异步通知的设备驱动,下面是这个函数的模板:
static int ***_fasync(int fd,struct file *filp,int mode)  
{  
struct ***_dev * dev=filp->private_data;  
return fasync_helper(fd,filp,mode,&dev->async_queue);//第四个参数为 fasync_struct结构体指针的指针。  
//这个函数是用来处理FASYNC标志的函数。(FASYNC:表示兼容BSD的fcntl同步操作)当这个标志改变时,驱动程序中的fasync()函数将得到执行。  (注:感觉这个‘标志'词用的并不恰当)  
}  

此操作用来通知设备它的 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.

标签:operations,文件,调用,struct,int,file,linux,设备
From: https://www.cnblogs.com/lattelover/p/17329568.html

相关文章

  • linux——I2C驱动
    HKA_S32ISPWriteI2CData(HKA_U8u8I2cDev,HKA_U8u8DevAddr,HKA_U32u32RegAddr,HKA_U32u32RegAddrByteNum,HKA_U32u32Data,HKA_U32u32DataByteNum){intret=0;structi2c_adapter*padap=NULL;structi2c_msgmsg;......
  • linux蚁剑入门到熟悉文档地址整理
    linux蚁剑入门到熟悉文档地址收录蚁剑GetHub地址帮助文档kali安装出现问题的解决方式kali安装记录linuxdebian下载解压后进入文件内,使用root身份执行./AntSword后选择下载运行环境,新建并选择位置即可之后再次./AntSword即可......
  • npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本
    在新建项目时候遇到一个问题如上图,安装cnpm或者node都会报这个错误找了半天发现解决方法如下(操作如上图)1、打开终端2、在终端执行:get-ExecutionPolicy,显示Restricted(表示状态是禁止的)3、在终端执行:set-ExecutionPolicyRemoteSigned4、在终端执行:get-ExecutionPolicy,显示RemoteSig......
  • linux——堆、栈、内存映射
    C的虚拟内存区域划分CodeArea(代码区):程序代码指令、常量字符串,只可读。StaticArea(静态区、全局区):存放全局变量/常量、静态变量/常量。该区域的大小在程序一加载进内存的时候就已固定,但是静态变量的值是可以改的。Heap(堆):由程序员控制,使用malloc/free来操作。(空间最大)Stack(栈......
  • Linux服务器怎么关闭防火墙
    Linux服务器怎么关闭防火墙在很多情况下,防火墙都会组织一些端口号的通讯。比如我们的tomcat,nginx,redis明明安装的没问题,但在外部就是访问不了,那很有可能就是防护墙的原因了。我是艾西,今天跟大家分享下Linux服务器怎么关闭防火墙一、重启后永久性生效:开启:chkconfigiptableson关闭:c......
  • Linux服务器怎么修改系统时间
    Linux服务器怎么修改系统时间linux服务器的系统时间,有的时候会产生误差,导致我们的程序出现一些延迟,或者其他的一些错误,那么怎么修改linux的系统时间呢?我是艾西,今天又是跟linux小白分享小知识的时间具体操作:我们一般使用“date-s”命令来修改Linux系统时间。比如将系统时间设定成......
  • Linux基础
    1、Linux目录结构1.1Linux系统的目录结构是一颗到撞树。Linux只有一个顶级目录,称之为:根目录Windows系统有多个顶级目录,即各个盘符在Linux系统中表示出现在开头的/表示:根目录出现在后面的/表示:层次关系1.2常见目录说明①./bin:存放常用命令(即二进制可执行程序)......
  • Linux服务器如何清除dns缓存
    DNS缓存是一个临时数据库,用于存储已解释的DNS查询信息。换句话说,每当你访问网站时,你的操作系统和网络浏览器都会保留域名和相应IP地址的记录。这消除对远程DNS服务器重复查询,并允许你的操作系统或浏览器快速解析网站的域名。但是在某些情况下,例如对网络问题进行故障排除或者在更改D......
  • Linux系统上如何禁用
    Linux系统上如何禁用为了保护数据不被泄漏,我们使用软件和硬件防火墙来限制外部未经授权的访问,但是数据泄露也可能发生在内部。为了消除这种可能性,机构会限制和监测访问互联网,同时禁用我是艾西,今天我将聊聊三种不同的方法来禁用方法方法方法方法一、伪安装在本方法中,我们往配置文件......
  • telnet远程管理linux主机及Zlib、openssl、openssh升级1
    一、telent远程管理主机1.安装telent服务服务端:yuminstalltelnet-server-y#安装服务端useradddaipasswddai#创建lhj用户,设置lhj密码,密码有规则要求,大小写数字加符号,输2次即可客户端:yuminstalltelnet-y#安装客户端2.测试telent登录客户端:telnet192.......