虚拟文件系统——VFS

#超级块对象(superblock object)

存放已经安装文件系统的有关信息。对基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件系统控制块(filessystem control block)。所有超级块对象都以双向循环链表的形式链接在一起。链表中第一个元素用super_blocks变量来表示,而超级块对象的s_list字段存放指向链表相邻元素的指针。sb_lock自旋锁保护链表免受多处理器系统上的同时访问。s_fs_info字段指向属于具体文件系统的超级块信息。VFS允许这些文件系统直接对内存超级块的s_fs_info字段进行操作,而无需访问磁盘。这样会引入问题:内存和磁盘数据不同步。因此引入s_dirt标志来表示该超级块是否是脏的。linux是通过周期性的将所有脏的超级块写回到磁盘来减少该问题带来的危害的。

与超级块关系的操作就是所谓的超级块操作。这些操作时有数据结构super_operations来描述的,该结构的起始地址存放在超级块的s_op字段中。如read_inode,它的执行操作如下:sb->s_op->read_inode(inode);

struct super_block {
struct list_head        s_list;                         //超级快链表指针
        dev_t                   s_dev;                          //设备表示符
        unsigned char           s_dirt;                         //脏标志 
        unsigned char           s_blocksize_bits;               //以位为单位的块的大小
        unsigned long           s_blocksize;                    //以字节为单位的块大小
        loff_t                  s_maxbytes;                     //文件大小上限
        struct file_system_type *s_type;                        //指向文件系统的file_system_type 数据结构的指针
        const struct super_operations   *s_op;                  //超级块方法 
        const struct dquot_operations   *dq_op;                 //磁盘限额方法
        const struct quotactl_ops       *s_qcop;                //限额控制方法
        const struct export_operations *s_export_op;            //导出方法
        unsigned long           s_flags;                        //登录标志
        unsigned long           s_magic;                        //文件系统的魔数
        struct dentry           *s_root;                        //目录登录点
        struct rw_semaphore     s_umount;                       //卸载信号量
        struct mutex            s_lock;                         //超级块信号量 
        int                     s_count;                        //超级块引用计数
        atomic_t                s_active;                       //活动引用记数
#ifdef CONFIG_SECURITY                              
        void                    *s_security;                    //安全模块
#endif
        const struct xattr_handler **s_xattr;       


        struct list_head        s_inodes;                       //把所有索引对象链接在一起,存放的是头结点 
        struct hlist_head       s_anon;                         //匿名目录项
#ifdef CONFIG_SMP
        struct list_head __percpu *s_files;       
#else
        struct list_head        s_files;                        //链接所有打开的文件。 
#endif
        /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */
        struct list_head        s_dentry_lru;   /* unused dentry lru */
        int                     s_nr_dentry_unused;     /* # of dentry on lru */


        struct block_device     *s_bdev;                        //相关的块设备
        struct backing_dev_info *s_bdi;           
        struct mtd_info         *s_mtd;           
        struct list_head        s_instances;                    //该类型文件系统
        struct quota_info       s_dquot;                        //限额相关选项


        int                     s_frozen;         
        wait_queue_head_t       s_wait_unfrozen;


        char s_id[32];                                          //文本名字


        void                    *s_fs_info;                     //文件系统特设信息 
        fmode_t                 s_mode;         


        /* Granularity of c/m/atime in ns.
           Cannot be worse than a second */
        u32                s_time_gran;


        /*
         * The next field is for VFS *only*. No filesystems have any business
         * even looking at it. You had been warned.
         */
        struct mutex s_vfs_rename_mutex;        /* Kludge */


        /*
         * Filesystem subtype.  If non-empty the filesystem type field
         * in /proc/mounts will be "type.subtype"
         */
        char *s_subtype;


        /*
         * Saved mount options for lazy filesystems using
         * generic_show_options()
         */
        char *s_options;
};


struct super_operations {
        struct inode *(*alloc_inode) (struct super_block *sb);   //创建和初始化一个新的索引结点。
        void (*destroy_inode) (struct inode *);                //释放指定的索引结点 。
        void (*read_inode) (struct inode *);
        void (*dirty_inode) (struct inode *);//VFS在索引节点被修改时会调用此函数
        void (*write_inode) (struct inode *, int);//将指定的inode写回磁盘。
        void (*put_inode) (struct inode *);
        void (*drop_inode) (struct inode *);//删除索引节点。
        void (*delete_inode) (struct inode *);
        void (*put_super) (struct super_block *);//用来释放超级块。
        void (*write_super) (struct super_block *);//更新磁盘上的超级块。
        int (*sync_fs) (struct super_block *, int);//使文件系统的数据元素与磁盘上的文件系统同步,wait参数指定操作是否同步。
        void (*write_super_lockfs) (struct super_block *);
        void (*unlockfs) (struct super_block *);
        int (*statfs) (struct super_block *, struct statfs *);   //获取文件系统状态。把文件系统相关的统计信息放在statfs中
        int (*remount_fs) (struct super_block *, int *, char *);
        void (*clear_inode) (struct inode *);
        void (*umount_begin) (struct super_block *);
        int (*show_options) (struct seq_file *, struct vfsmount *);
};


#索引节点对象(inode object)
存放关于具体文件的一般信息。对基于磁盘的文件系统,这类对象同城对应于存放在磁盘上的文件控制块(file control block)。每一个索引节点对象都有一个索引节点号,这个节点号唯一的标识文件系统中的文件。
文件系统处理文件所需要的所有信息都放在一个名为索引节点的数据结构中。文件名可以随意更改,但是索引节点对文件是唯一的,并且随文件的存在而存在。内存中的索引节点对象由一个inode数据结构组成。

Linux struct inode结构
/*索引节点对象由inode结构体表示,定义文件在linux/fs.h中*/
struct inode {
struct hlist_node       i_hash;              /* 哈希表 为了提高查找inode的效率,每一个inode都会有一个hash值。该字段指向hash值相同的inode所形成的双链表该字段包含prev和next两个指针,分别指向上述链表的前一个元素和后一个元素;*/ 
struct list_head        i_list;              /* 索引节点链表 所有索引结点形成的双联表*/
struct list_head        i_dentry;            /* 目录项链表 所有引用该inode的目录项将形成一个双联表,该字段即为这个双联表的头结点*/
unsigned long           i_ino;               /* 节点号 通过ls -i命令可以查看文件的索引节点号*/
atomic_t                i_count;             /* 引用记数 */
umode_t                 i_mode;              /* 访问权限控制 */
unsigned int            i_nlink;             /* 硬链接数 当该inode描述一个目录时,这个值至少为2,代表.和..的数目*/
uid_t                   i_uid;               /* 使用者id 通过ls -n可查看拥有者id*/
gid_t                   i_gid;               /* 使用者id组 通过ls -n可查看组id*/
kdev_t                  i_rdev;              /* 实设备标识符 如果该inode描述的是一个设备文件,此值为设备号*/
loff_t                  i_size;              /* 以字节为单位的文件大小 */
struct timespec         i_atime;             /* 最后访问时间 通过ls -lu可查看该时间*/
struct timespec         i_mtime;             /* 最后修改(modify)时间 通过ls -l可查看该时间*/
struct timespec         i_ctime;             /* 最后改变(change)时间 这里的修改除了指文件内容被修改外,更强调的是文件的属性被修改。通过ls -lc可查看该时间*/
unsigned int            i_blkbits;           /* 以位为单位的块大小 */
unsigned long           i_blksize;           /* 以字节为单位的块大小 */
unsigned long           i_version;           /* 版本号 */
unsigned long           i_blocks;            /* 文件的块数 通过ls -s可以查看该某个文件的块使用数目*/
unsigned short          i_bytes;             /* 使用的字节数 */
spinlock_t              i_lock;              /* 自旋锁 */
struct rw_semaphore     i_alloc_sem;         /* 索引节点信号量 */
struct inode_operations *i_op;               /* 索引节点操作表 */
struct file_operations  *i_fop;              /* 默认的索引节点操作 */
struct super_block      *i_sb;               /* 相关的超级块 */
struct file_lock        *i_flock;            /* 文件锁链表 */
struct address_space    *i_mapping;          /* 相关的地址映射 */
struct address_space    i_data;              /* 设备地址映射 */
struct dquot            *i_dquot[MAXQUOTAS]; /* 节点的磁盘限额 */
struct list_head        i_devices;           /* 块设备链表 */
struct pipe_inode_info  *i_pipe;             /* 管道信息 */
struct block_device     *i_bdev;             /* 块设备驱动 */
unsigned long           i_dnotify_mask;      /* 目录通知掩码 */
struct dnotify_struct   *i_dnotify;          /* 目录通知 */
unsigned long           i_state;             /* 状态标志 若等于I_DIRTY_SYNC/I_DIRTY_DATASYNC/I_DIRTY_PAGES说明该索引节点是“脏”的,也就是数对应的磁盘索引节点必须被更新*/
unsigned long           dirtied_when;        /* 首次修改时间 */
unsigned int            i_flags;             /* 文件系统的安装标志 */
unsigned char           i_sock;              /* 可能是个套接字吧 */
atomic_t                i_writecount;        /* 写进程记数 */
void                    *i_security;         /* 安全模块 */
__u32                   i_generation;        /* 索引节点版本号 */
union {
void            *generic_ip;         /* 文件特殊信息 */
} u;
};


/*索引节点的操作inode_operations定义在linux/fs.h中*/
struct inode_operations {
int (*create) (struct inode *, struct dentry *,int);
/*VFS通过系统调用create()和open()来调用该函数,从而为dentry对象创建一个新的索引节点。在创建时使用mode制定初始模式*/
struct dentry * (*lookup) (struct inode *, struct dentry *);
/*该韩式在特定目录中寻找索引节点,该索引节点要对应于dentry中给出的文件名*/
int (*link) (struct dentry *, struct inode *, struct dentry *);
/*该函数被系统调用link()电泳,用来创建硬连接。硬链接名称由dentry参数指定,连接对象是dir目录中ld_dentry目录想所代表的文件*/
int (*unlink) (struct inode *, struct dentry *);
/*该函数被系统调用unlink()调用,从目录dir中删除由目录项dentry制动的索引节点对象*/
int (*symlink) (struct inode *, struct dentry *, const char *);
/*该函数被系统电泳symlik()调用,创建符号连接,该符号连接名称由symname指定,连接对象是dir目录中的dentry目录项*/
int (*mkdir) (struct inode *, struct dentry *, int);
/*该函数被mkdir()调用,创建一个新鲁姆。创建时使用mode制定的初始模式*/
int (*rmdir) (struct inode *, struct dentry *);
/*该函数被系统调用rmdir()调用,删除dir目录中的dentry目录项代表的文件*/
int (*mknod) (struct inode *, struct dentry *, int, dev_t);
/*该函数被系统调用mknod()调用,创建特殊文件(设备文件、命名管道或套接字)。要创建的文件放在dir目录中,其目录项问dentry,关联的设备为rdev,初始权限由mode指定*/
int (*rename) (struct inode *, struct dentry *,
  struct inode *, struct dentry *);
/*VFS调用该函数来移动文件。文件源路径在old_dir目录中,源文件由old_dentry目录项所指定,目标路径在new_dir目录中,目标文件由new_dentry指定*/
int (*readlink) (struct dentry *, char *, int);
/*该函数被系统调用readlink()调用,拷贝数据到特定的缓冲buffer中。拷贝的数据来自dentry指定的符号链接,最大拷贝大小可达到buflen字节*/
int (*follow_link) (struct dentry *, struct nameidata *);
/*该函数由VFS调用,从一个符号连接查找他指向的索引节点,由dentry指向的连接被解析*/
int (*put_link) (struct dentry *, struct nameidata *);
/*在follow_link()调用之后,该函数由vfs调用进行清楚工作*/
void (*truncate) (struct inode *);
/*该函数由VFS调用,修改文件的大小,在调用之前,索引节点的i_size项必须被设置成预期的大小*/
int (*permission) (struct inode *, int);
/*该函数用来检查给低昂的inode所代表的文件是否允许特定的访问模式,如果允许特定的访问模式,返回0,否则返回负值的错误码。多数文件系统 都将此区域设置为null,使用VFS提供的通用方法进行检查,这种检查操作仅仅比较索引及诶但对象中的访问模式位是否和mask一致,比较复杂的系统, 比如支持访问控制链(ACL)的文件系统,需要使用特殊的permission()方法*/
int (*setattr) (struct dentry *, struct iattr *);
/*该函数被notify_change调用,在修改索引节点之后,通知发生了改变事件*/
int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
/*在通知索引节点需要从磁盘中更新时,VFS会调用该函数*/
int (*setxattr) (struct dentry *, const char *,
const void *, size_t, int);
/*该函数由VFS调用,向dentry指定的文件设置扩展属性,属性名为name,值为value*/
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
/*该函数被VFS调用,向value中拷贝给定文件的扩展属性name对应的数值*/
ssize_t (*listxattr) (struct dentry *, char *, size_t);
/*该函数将特定文件所有属性别表拷贝到一个缓冲列表中*/
int (*removexattr) (struct dentry *, const char *);
/*该函数从给定文件中删除指定的属性*/
};



#文件对象(file object)
存放打开文件与进程之间进行交互的有关信息。这类信息仅当进程访问文件期间存在与内核内存中。
文件对象描述进程怎么样与一个打开的文件进行交互。文件对象是在文件被打开时创建的,由一个file结构组成。注意,文件对象在磁盘上没有对应的映像,因此file结构中没有设置“脏”字段来表示文件对象是否已经被修改。
存放在文件对象中的主要信息是文件指针,即文件中当前位置,下一个操作将在该位置发生。由于几个进程可能同时访问同一个文件,因此文件指针必须存放在文件对象而不是索引节点对象中。
每个超级块对象将文件对象链表的头存放在f_files字段中;因此,属于不同文件系统的文件对象就包含在不同的链表中(因为不同的文件系统有不同的超级块)。files_lock自旋锁保护超级块的s_files链表免受多处理器系统上的同时访问。
文件对象的f_count字段是一个引用计数器:它记录使用文件对象的进程数(注意:以CLONE_FILES标创建的轻量级进程共享打开文件表,因此他们可以使用相同的文件对象)。当内核本身使用该文件对象时也要增加计数器的值--例如,把对象插入链表中或者发出dup()系统调用时。
当VFS打开一个文件时通过调用get_empty_filp()函数来分配一个新的文件对象。该函数调用kmem_cache_alloc()从filp高速缓存中获得一个空闲的文件对象,然后初始化这个对象的字段。
struct file结构体定义在/linux/include/linux/fs.h(Linux 2.6.11内核)中,其原型是:
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 path             f_path;
#define f_dentry        f_path.dentry
#define f_vfsmnt        f_path.mnt
const struct file_operations    *f_op;
atomic_t                f_count;
unsigned int            f_flags;
mode_t                  f_mode;
loff_t                  f_pos;
struct fown_struct      f_owner;
unsigned int            f_uid, f_gid;
struct file_ra_state    f_ra;
unsigned long           f_version;
#ifdef CONFIG_SECURITY
void                    *f_security;
#endif
/* 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 {
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参数。如果驱动程序需要这个值,可以直接读取这个字段。
mode_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;
/**************************************************************************************************************/


当内核将一个索引节点从磁盘装入内存时,就会把指向这个文件操作的指针存放在file_operations结构中,而该结构的地址存在该索引节点对象的i_fop字段中。当进程打开这个文件时,VFS就用存放在索引节点中的这个地址初始化新文件对象的f_op字段,使得对文件操作的后续调用能够使用这些函数。VFS也可以通过在f_op字段存在一个新值而修改文件操作的集合。
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); 
  }; 
/*


struct module *owner


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

#目录项对象(dentry object)
存放目录项(也就是文件的特定名称)与对应文件进行链接的有关信息。每个磁盘文件系统都以自己特有的方式将该类信息存在磁盘上。


VFS除了能为所有文件系统的实现提供一个通用的接口外,还有具有一个与系统性能相关的中要作用。最近最常使用的目录项对象被放在所谓目录项高速缓存(dentry cache)的磁盘告诉缓存中,以加速从文件路径名到最优一个路径分量的索引节点的转换过程。
磁盘高速缓存(disk cache)属于软件机制,但是不同于硬件高速缓存或者内存高速缓存。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值