转自:qian-xu-feng

file_operation就是把系统调用和驱动程序关联起来的关键数据结构。这个结构的每一个成员都对应着一个系统调用。读取file_operation中相应的函数指针,接着把控制权转交给函数,从而完成了Linux设备驱动程序的工作。

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

传统上,一个file_operation结构或者其一个指针称为fops(或者它的一些变体).结构中的每个成员必须指向驱动中的函数,这些函数实现一个特别的操作,或者对于不支持的操作留置为NULL.当指定为NULL指针时内核的确切的行为是每个函数不同的。

在你通读file_operations方法的列表时,你会注意到不少参数包含字串__user.这种注解是一种文档形式,注意,一个指针是一个不能被直接解引用的用户空间地址.对于正常的编译, __user没有效果,但是它可被外部检查软件使用来找出对用户空间地址的错误使用。

---------------------------------------------------------------------

注册设备编号仅仅是驱动代码必须进行的诸多任务中的第一个。首先需要涉及一个别的,大部分的基础性的驱动操作包括3个重要的内核数据结构,称为file_operations,file,和inode。需要对这些结构的基本了解才能够做大量感兴趣的事情。

struct file_operations是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,这就是file_operations,用来执行一系列的系统调用。

struct file代表一个打开的文件,在执行file_operation中的open操作时被创建,这里需要注意的是与用户空间inode指针的区别,一个在内核,而file指针在用户空间,由c库来定义。
   struct inode被内核用来代表一个文件,注意和struct file的区别,struct inode一个是代表文件,struct file一个是代表打开的文件
struct inode包括很重要的二个成员:
dev_t       i_rdev  设备文件的设备号
struct cdev *i_cdev代表字符设备的数据结构

struct inode结构是用来在内核内部表示文件的.同一个文件可以被打开好多次,所以可以对应很多struct file,但是只对应一个struct inode.

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

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

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

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

int (*flush) (struct file *, fl_owner_t id);

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

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

int (*check_flags)(int);

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

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

ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

};

File_operations的数据结构如下:

struct module *owner

第一个file_operations成员根本不是一个操作;它是一个指向拥有这个结构的模块的指针.这个成员用来在它的操作还在被使用时阻止模块被卸载.几乎所有时间中,它被简单初始化为THIS_MODULE,一个在中定义的宏.

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

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

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

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

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

初始化一个异步读--可能在函数返回前不结束的读操作.如果这个方法是NULL,所有的操作会由read代替进行(同步地).

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

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

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

初始化设备上的一个异步写.

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

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

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

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

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

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

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

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

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

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

int (*flush) (struct file *);

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

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

在文件结构被释放时引用这个操作.如同open, release可以为NULL.

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

这个方法是fsync系统调用的后端,用户调用来刷新任何挂着的数据.如果这个指针是NULL,系统调用返回-EINVAL.

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

这是fsync方法的异步版本.

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

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

int (*check_flags)(int)

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

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

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

---------------------------------------------------------------------

struct file {

/*

* fu_list becomes invalid after file_free is called and queued via

* fu_rcuhead for RCU freeing

*/

union {

struct list_head     fu_list;

struct rcu_head  fu_rcuhead;

} f_u;

struct dentry        *f_dentry;

struct vfsmount        *f_vfsmnt;

const struct file_operations *f_op;

atomic_t       f_count;

unsigned int        f_flags;

mode_t                f_mode;

loff_t                   f_pos;

struct fown_structf_owner;

unsigned int          f_uid, f_gid;

struct file_ra_statef_ra;

unsigned long       f_version;

void              *f_security;

/* needed for tty driver, and maybe others */

void              *private_data;

#ifdef CONFIG_EPOLL

/* Used by fs/eventpoll.c to link all the hooks to this file */

struct list_head     f_ep_links;

spinlock_t            f_ep_lock;

#endif /* #ifdef CONFIG_EPOLL */

struct address_space  *f_mapping;

};

文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核创建和驱动源码中,struct file的指针通常被命名为file或filp。一下是对结构中的每个数据成员的解释:
一、
union {
    struct list_head fu_list;
    struct rcu_head rcuhead;
}f_u;
其中的struct list_head定义在linux/include/linux/list.h中,原型为:
struct list_head {
        struct list_head *next, *prev;
};
用于通用文件对象链表的指针。
struct rcu_head定义在linux/include/linux/rcupdate.h中,其原型为:
/**
* struct rcu_head - callback structure for use with RCU
* @next: next update requests in a list
* @func: actual update function to call after the grace period.
*/
struct rcu_head {
        struct rcu_head *next;
        void (*func)(struct rcu_head *head);
};
RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制,具体在这里有介绍:
http://www.ibm.com/developerworks/cn/linux/l-rcu/
二、
struct path             f_path;
被定义在linux/include/linux/namei.h中,其原型为:
struct path {
        struct vfsmount *mnt;
        struct dentry *dentry;
};
在早些版本的内核中并没有此结构,而是直接将path的两个数据成员作为struct file的数据成员,
struct vfsmount *mnt的作用是指出该文件的已安装的文件系统,
struct dentry *dentry是与文件相关的目录项对象。
三、
const struct file_operations    *f_op;
被定义在linux/include/linux/fs.h中,其中包含着与文件关联的操作,如:
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
等。当打开一个文件时,内核就创建一个与该文件相关联的struct file结构,其中的*f_op就指向的是
具体对该文件进行操作的函数。例如用户调用系统调用read来读取该文件的内容时,那么系统调用read最终会陷入内核调用sys_read函数,而sys_read最终会调用于该文件关联的struct file结构中的f_op->read函数对文件内容进行读取。
四、
atomic_t                f_count;
atomic_t被定义为:
typedef struct { volatile int counter; } atomic_t;
volatile修饰字段告诉gcc不要对该类型的数据做优化处理,对它的访问都是对内存的访问,而不是对寄存器的访问。
本质是int类型,之所以这样写是让编译器对基于该类型变量的操作进行严格的类型检查。此处f_count的作用是记录对文件对象的引用计数,也即当前有多少个进程在使用该文件。
五、
unsigned int            f_flags;
当打开文件时指定的标志,对应系统调用open的int flags参数。驱动程序为了支持非阻塞型操作需要检查这个标志。
六、
mode_t                  f_mode;
对文件的读写模式,对应系统调用open的mod_t mode参数。如果驱动程序需要这个值,可以直接读取这个字段。
mod_t被定义为:
typedef unsigned int __kernel_mode_t;
typedef __kernel_mode_t         mode_t;
七、
loff_t                  f_pos;
当前的文件指针位置,即文件的读写位置。
loff_t被定义为:
typedef long long       __kernel_loff_t;
typedef __kernel_loff_t         loff_t;
八、
struct fown_struct      f_owner;
struct fown_struct在linux/include/linux/fs.h被定义,原型为:
struct fown_struct {
        rwlock_t lock;          /* protects pid, uid, euid fields */
        struct pid *pid;        /* pid or -pgrp where SIGIO should be sent */
        enum pid_type pid_type; /* Kind of process group SIGIO should be sent to */
        uid_t uid, euid;        /* uid/euid of process setting the owner */
        int signum;             /* posix.1b rt signal to be delivered on IO */
};
该结构的作用是通过信号进行I/O时间通知的数据。
九、
unsigned int            f_uid, f_gid;
标识文件的所有者id,所有者所在组的id.
十、
struct file_ra_state    f_ra;
struct file_ra_state结构被定义在/linux/include/linux/fs.h中,原型为:
struct file_ra_state {
        pgoff_t start;                  /* where readahead started */
        unsigned long size;             /* # of readahead pages */
        unsigned long async_size;       /* do asynchronous readahead when
                                           there are only # of pages ahead */
                                          
        unsigned long ra_pages;         /* Maximum readahead window */
        unsigned long mmap_hit;         /* Cache hit stat for mmap accesses */
        unsigned long mmap_miss;        /* Cache miss stat for mmap accesses */
        unsigned long prev_index;       /* Cache last read() position */
        unsigned int prev_offset;       /* Offset where last read() ended in a page */
};
文件预读状态,文件预读算法使用的主要数据结构,当打开一个文件时,f_ra中出了perv_page(默认为-1)和ra_apges(对该文件允许的最大预读量)这两个字段外,其他的所有西端都置为0。
十一、
unsigned long           f_version;
记录文件的版本号,每次使用后都自动递增。
十二、
#ifdef CONFIG_SECURITY
        void                    *f_security;

------------------------------------------------------------------------------------------------------

struct inode {

struct hlist_node   i_hash;

struct list_head     i_list;

struct list_head     i_sb_list;

struct list_head     i_dentry;

unsigned long       i_ino;

atomic_t       i_count;

umode_t              i_mode;

unsigned int          i_nlink;

uid_t                    i_uid;

gid_t                    i_gid;

dev_t                   i_rdev;

loff_t                   i_size;

struct timespec            i_atime;

struct timespec            i_mtime;

转载于:https://www.cnblogs.com/ganrui/p/4700895.html

file_operation(文件操作)file(文件)inode(节点)相关推荐

  1. .NET基础-11-ArrayList|Hashtable|File文件操作|Dircetioy文件夹操作|Path路径操作

    集合 ArrayList与Hashtable应为存在拆箱与装箱,所以性能不怎么好,尽量不要使用,而使用泛型集合 可以使用下面的方式输出所消耗的时间 //ArrayList arl = new Arra ...

  2. Python零基础速成班-第8讲-Python文件操作File IO、高级文件处理模块shutil、CSV、JSON、多线程基础

    Python零基础速成班-第8讲-Python文件操作File I&O.高级文件处理模块shutil.CSV.JSON.多线程基础 学习目标 文件操作File I/O 高级文件处理模块shut ...

  3. linux c文件操作,C语言文件操作(FILE)与常用文件操作函数

    文件 1.文件基本概念 C程序把文件分为ASCII文件和二进制文件,ASCII文件又称文本文件,二进制文件和文本文件(也称ASCII码文件)二进制文件中,数值型数据是以二进制形式存储的, 而在文本文件 ...

  4. C++ 学习笔记之——文件操作和文件流

    1. 文件的概念 对于用户来说,常用到的文件有两大类:程序文件和数据文件.而根据文件中数据的组织方式,则可以将文件分为 ASCII 文件和二进制文件. ASCII 文件,又称字符文件或者文本文件,它的 ...

  5. python关闭读写的所有的文件-Python文件操作:文件的打开关闭读取写入

    Python文件操作:文件的打开关闭读取写入 一.文件的打开关闭 Python能以文本和二进制两种方式处理文件,本文主要讨论在Python3中文本文件的操作. 文件操作都分为以下几个步骤: 1.打开文 ...

  6. java删除相对路径文件夹_Java IO,io,文件操作,删除文件,删除文件夹,获取文件父级目录...

    Java IO,io,文件操作,删除文件,删除文件夹,获取文件父级目录 这里先简单的贴下常用的方法: File.separator //当前系统文件分隔符 File.pathSeparator// F ...

  7. python以写模式打开的文件无法进读操作_Python文件操作:文件的打开关闭读取写入...

    Python文件操作:文件的打开关闭读取写入 一.文件的打开关闭 Python能以文本和二进制两种方式处理文件,本文主要讨论在Python3中文本文件的操作. 文件操作都分为以下几个步骤: 1.打开文 ...

  8. 今天的码农女孩做了关于文件操作和文件拖拽的笔记 2022/1/21

    文件操作和文件拖拽 文件操作: js有两种机制:一个是事件机制,一个是io机制 文件操作对象: Blob通过二进制数据读取 file读取单个文件对象 fileList读取多个文件对象 fileRead ...

  9. Kettle使用_15 文件操作复制文件到结果

    Kettle使用_15 文件操作复制文件到结果 需求: 通过Kettle的组件实现文件夹下所有文件的复制到另外个文件夹. 解决方法:结合获取文件名.复制文件名到结果.执行作业.添加到结果文件列表.复制 ...

  10. JAVA SE学习day_03:包装类、文件操作——file

    一.包装类 包装类是为了解决基本类型不能直接参与面向对象开发的问题,实际就是将基本类型以"对象"的形式表示 Integer 1.1基本类型转换为对应的包装类(引用类型) int i ...

最新文章

  1. 双网卡双ip实现双线路共用
  2. leetcode116. 填充每个节点的下一个右侧节点指针(dfs)
  3. 0网卡开启_中标麒麟Linux v7系统下设置双网卡bond或team绑定详细过程
  4. 真正的高情商,从学会麻烦别人开始
  5. tab页签切换----bootstrap
  6. IT男,程序猿在婚恋市场有多受欢迎?
  7. 思科模拟器Cisco Packet Tracer安装指南
  8. 发现Chrome小恐龙彩蛋的第n+1个使用者
  9. 如何评价单片机大神郭天祥?
  10. LTE之3GPP_协议下载_协议命名
  11. 1971旗舰cpu intel_这就是近年来Intel最良心CPU!我彻底服了
  12. Keil5使用AC6编译器
  13. 【饥荒】关于随机地图生成的方式
  14. 小说文学行业之“盛大文学”
  15. sync.Map详解
  16. arduino tft 方向_Arduino库教程-TFT Library
  17. 【bosun】查询表达式
  18. Unity动画暂停的方法
  19. 星座时代:一家企业的自白
  20. 实习工作日记【8500字】【原创】

热门文章

  1. gpu云服务器运行游戏_99元起!华为云鲲鹏云手机正式发布:流畅运行大型游戏...
  2. nginx启动vue_nginx下部署vue项目的方法步骤
  3. STM32——HAL版——定时器ms和us延时函数
  4. java ee自学_自学JavaEE难度大不大?
  5. php三元运算符应用举例,php – 使用嵌套三元运算符
  6. tree 先序遍历 叶子结点_编程:按先序序列输出二叉树的叶子结点
  7. 佛说剖腹产的孩子_选择好的剖腹产时间会改变孩子的命运吗?
  8. 阿里云云计算 35 RDS的产品选型
  9. java生成gif_Java生成动态GIF图片
  10. JSP概述--JSP的起源,本质,运行原理