注:学了这么长时间了,还没有好好看看 file_operations机构体,这其中还有很多的东西,当你学着学着的时候,就会用到这里面的一些系统调用对应的函数了,我在网上搜索之后,记录如下,一边将来查看。。。。。
 
前沿:这些东西估计对你有用
  linux驱动程序中最重要的涉及3个重要的内核数据结构,分别为file_operations,file和inode。

在linux中inode结构用于表示文件,而file结构则表示打开的文件的描述,因为对于单个文件而言可能会有许多个表示打开的文件的描述符,因而就可能会的对应有多个file结构,但是都指向单个inode结构。

在系统内部,I/O设备的存取操作通过特定的的入口来进行,而这组特定的入口由驱动程序来提供的。通常这组设备驱动的接口是由结构体file_operations向系统说明的

 
file_operations结构体详细分析
整体结构如下:
linux-2.6.22/include/linux/fs.h
struct file_operations {
        struct module *owner;    //防止模块还在被使用的时候被卸载
        loff_t        (*llseek) ();
        ssize_t       (*read) ();
        ssize_t       (*write) ();
        ssize_t       (*aio_read) ();
        ssize_t       (*aio_write) ();
        int           (*readdir) ();
        unsigned int (*poll) ();
        int           (*ioctl) ();
        long          (*unlocked_ioctl) ();
        long          (*compat_ioctl) ();
        int           (*mmap) ();
        int           (*open) ();
        int           (*flush) ();
        int           (*release) ();
        int           (*fsync) ();
        int           (*aio_fsync) ();
        int           (*fasync) ();
        int           (*lock) ();
        ssize_t       (*sendfile) ();
        ssize_t       (*sendpage) ();
        unsigned long (*get_unmapped_area) ();
        int           (*check_flags) ();
        int           (*dir_notify) ();
        int           (*flock) ();
        ssize_t       (*splice_write) ();
        ssize_t       (*splice_read) ();
};
以下解释摘自:
第一个 file_operations 成员根本不是一个操作; 它是一个指向拥有这个结构的模块的指针.
这个成员用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间中, 它被简单初始化为 
THIS_MODULE, 一个在 中定义的宏.这个宏比较复杂,在进行简单学习操作的时候,一般初始化为THIS_MODULE。

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

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

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

ssize_t (*write) (struct file * filp, const char __user *   buffer, size_t count, loff_t * ppos);
(参数filp为目标文件结构体指针,buffer为要写入文件的信息缓冲区,count为要写入信息的长度,
ppos为当前的偏移位置,这个值通常是用来判断写文件是否越界)
发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数.
(注:这个操作和上面的对文件进行读的操作均为阻塞操作)

ssize_t (*aio_write)(struct kiocb *, const char __user * buffer, size_t count, loff_t * ppos);
      初始化设备上的一个异步写.参数类型同aio_read()函数;

int (*readdir) (struct file * filp, void *, filldir_t);
对于设备文件这个成员应当为 NULL; 它用来读取目录, 并且仅对文件系统有用.

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, 设备假定为不阻塞地可读可写.
(这里通常将设备看作一个文件进行相关的操作,而轮询操作的取值直接关系到设备的响应情况,可以是阻塞操作结果,同时也可以是非阻塞操作结果)

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"), 系统调用返回一个错误.

int (*mmap) (struct file *, struct vm_area_struct *);
mmap 用来请求将设备内存映射到进程的地址空间. 如果这个方法是 NULL, mmap 系统调用返回 -ENODEV.
(如果想对这个函数有个彻底的了解,那么请看有关“进程地址空间”介绍的书籍)

int (*open) (struct inode * inode , struct file * filp ) ;
(inode 为文件节点,这个节点只有一个,无论用户打开多少个文件,都只是对应着一个inode结构;
但是filp就不同,只要打开一个文件,就对应着一个file结构体,file结构体通常用来追踪文件在运行时的状态信息)
尽管这常常是对设备文件进行的第一个操作, 不要求驱动声明一个对应的方法. 如果这个项是 NULL, 设备打开一直成功, 但是你的驱动不会得到通知.
与open()函数对应的是release()函数。

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

int (*release) (struct inode *, struct file *);
release ()函数当最后一个打开设备的用户进程执行close()系统调用的时候,内核将调用驱动程序release()函数:
void release(struct inode inode,struct file *file),release函数的主要任务是清理未结束的输入输出操作,释放资源,用户自定义排他标志的复位等。
    在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL.

int(*synch)(struct file *,struct dentry *,int datasync);
刷新待处理的数据,允许进程把所有的脏缓冲区刷新到磁盘。

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

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 如果驱动不支持异步通知.

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

ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);
这个方法实现 sendfile 系统调用的读, 使用最少的拷贝从一个文件描述符搬移数据到另一个.
例如, 它被一个需要发送文件内容到一个网络连接的 web 服务器使用. 设备驱动常常使 sendfile 为 NULL.

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
sendpage 是 sendfile 的另一半; 它由内核调用来发送数据, 一次一页, 到对应的文件. 设备驱动实际上不实现 sendpage.

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

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

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

一般情况下,进行设备驱动程序的设计只是比较注重下面的几个方法:
struct file_operations ***_ops={
.owner = THIS_MODULE,
.llseek = ***_llseek,
.read = ***_read,
.write = ***_write,
.ioctl = ***_ioctl,
.open = ***_open,
.release = ***_release, 
};

file_operations结构体解析 1的更多相关文章

  1. Golang通过结构体解析和封装XML

    Golang解析和封装XML 解析XML成结构体Demo package main import ( "encoding/xml" "fmt" ) //我们通过 ...

  2. golang 兼容不同json结构体解析实践

    线上服务器,同一个web接口有时需要兼容不同版本的结构体.这种情况思路是使用interface{}接收任意类型数据,结合reflect包处理. 如下,http接口调用者会传入不同的json结构数据(单 ...

  3. Go语言使用匿名结构体解析JSON数据

    手机拥有屏幕.电池.指纹识别等信息,将这些信息填充为 JSON 格式的数据.如果需要选择性地分离 JSON 中的数据则较为麻烦.Go 语言中的匿名结构体可以方便地完成这个操作. 首先给出完整的代码,然 ...

  4. struct 结构体解析(原)

    (一)基本概念 结构体是一个或是多个变量的集合,这些变量可能为不同的类型,为了处理的方便而将这些变量组合在一个名字之下.我们将关键字struct引入了结构声明中.结构声明包含在花括号内的一系列声明组成 ...

  5. 用结构体解析Pascal字符串

    来源:https://www.cnblogs.com/qiuyuwutong/p/8708844.html 1.什么是柔性数组? 柔性数组既数组大小待定的数组, C语言中结构体的最后一个元素可以是大小 ...

  6. linux驱动开发( 五) 字符设备驱动框架的填充file_operations结构体中的操作函数(read write llseek unlocked_ioctl)

    例子就直接使用宋宝华的书上例子. /* * a simple char device driver: globalmem without mutex * * Copyright (C) 2014 Ba ...

  7. Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】

    本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...

  8. Linux字符设备中的两个重要结构体(file、inode)

    对于Linux系统中,一般字符设备和驱动之间的函数调用关系如下图所示 上图描述了用户空间应用程序通过系统调用来调用程序的过程.一般而言在驱动程序的设计中,会关系 struct file 和 struc ...

  9. file_operations结构2

    对与应用层的每个系统调用,驱动程序都有一个与之对应的函数.对于字符设备驱动程序,这些函数集合在一个file_operations类型的数据结构中,该结构体在Linux内核的include/linux/ ...

随机推荐

  1. Ext.Net学习笔记18:Ext.Net 可编辑的GridPanel

    Ext.Net学习笔记18:Ext.Net 可编辑的GridPanel Ext.Net GridPanel 有两种编辑模式:编辑单元格和编辑行. 单元格编辑: 行编辑: 可以看出,单元格编辑的时候,只 ...

  2. Android系统简介(中):系统架构

    Android的系统架构栈分为4层,从上往下分别是Applications.Application framework.Libraries  & Android Runtime.Linux  ...

  3. IOS 高级开发 KVC(二)

    前一篇博客最后介绍了KVC 再json 转模型时遇到一些问题.今天接着来介绍KVC 的其他用法.其实我们在一开始的时候就一直再强调命名的重要性.命名规范是KVC 存活的基础.如果没有这个条件支撑,那么 ...

  4. NSArray函数

    1.判断是否包含某一个元素,返回1则表示有 - (BOOL)countainsObject:(id)anObject BOOL isContain = [arrayboy containsObject ...

  5. CUDA_矢量相加

    #include<iostream> #define N 10 _ _global_ _ void add(*a,*b,*c) { int tid=blockIdx.x; if(tid&l ...

  6. 回溯算法之n皇后问题

    今天在看深度优先算法的时候,联想到DFS本质不就是一个递归回溯算法问题,只不过它是应用在图论上的.OK,写下这篇博文也是为了回顾一下回溯算法设计吧. 学习回溯算法问题,最为经典的问题我想应该就是八皇后 ...

  7. 【HeadFirst设计模式】8.模板方法模式

    模板方法 定义: 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.模板方法使用得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤. 策略模式: 定义一个算法家族,并让这些算法可以互 ...

  8. 让hyper-v调整console的大小

    在hyper-v中centos的console一直都是1024x768的分辨率,后来找到一种修改分辨率的解决方法 grubby --update-kernel=ALL --args="vid ...

  9. jquery图片无缝滚动代码左右 上下无缝滚动图片

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. c#个人记录常用方法(更新中)

    1.日期毫秒转换为标准的C#日期格式 //使用时,先将秒Convert.ToInt64,返回格式2015-2-10 14:03:33 public DateTime JavaTimeToC(long ...