参考链接如下:
https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/#fig2
https://blog.csdn.net/Ohmyberry/article/details/80427492
https://blog.csdn.net/shanshanpt/article/details/38943731
https://www.cnblogs.com/LittleHann/p/3865490.html
https://www.cnblogs.com/LittleHann/p/4305892.html
https://www.cnblogs.com/fengkang1008/p/4691231.html
https://www.cnblogs.com/linux-xin/p/8126999.html
1. 图示文件系统
1.1经典的文件系统
需要注意几点:
- dentry 本身目录块也属于数据块
- inode指向实际block,其属于i节点区
- 目录块本身包含inode指针和文件名
1.2 对文件系统结构体分析
精力有限,凑合看看,结构体很长,选了一些,例如:
标准IO的fread函数, size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) ,实际调用read,其中需要注意FILE 与 fd 的差异,fd为fd_array的数组索引号,实际存放file指针,通过file指针调用f_op的函数
2. VFS
如下两张图能直观反映Linux下文件系统中VFS所起的作用,上接系统调用,下接真实文件系统,类似于autosar里的RTE,使得不同的文件系统在Linux里共存,且同一个API可以完成对文件系统的操作。
VFS抽象了文件、目录项 (dentry)、索引节点 (inode) 及挂载点,其主要有如下四个部分组成:
- 超级块对象 (superblock object)
- 索引节点对象 (inode object)、
- 目录项对象 (dentry object) 、
- 文件对象 (file object)
上述四个模块在VFS中联系如下图所示:
2.1 superblock
超级块就是对所有文件系统的管理机构,主要管理block和inode等,记录保存文件系统的类型、大小、状态等等,每种文件系统都要把自己的信息挂到super_blocks这么一个全局链表上,超级块与文件系统一一对应,同一个文件系统类型可能含有多个超级块。
- block 与inode 的总量;
- 未使用与已使用的inode / block 数量;
- block 与inode 的大小(block 为1, 2, 4K,inode 为128bytes 或256bytes);
- filesystem 的挂载时间、最近一次写入资料的时间、最近一次检验磁盘(fsck) 的时间等档案系统的相关信息;
- 一个valid bit 数值,若此档案系统已被挂载,则valid bit 为0 ,若未被挂载,则valid bit 为1 。
“文件系统”和“文件系统类型”不一样!一个文件系统类型下可以包括很多文件系统即很多的super_block
具体代码和解释部分,参考https://www.cnblogs.com/LittleHann/p/3865490.html
struct super_block
{
/*
Keep this first
指向超级块链表的指针,用于将系统中所有的超级块聚集到一个链表中,该链表的表头是全局变量super_blocks
*/
struct list_head s_list;
/*
search index; _not_ kdev_t
设备标识符
*/
dev_t s_dev;
//以字节为单位的块大小
unsigned long s_blocksize;
//以位为单位的块大小
unsigned char s_blocksize_bits;
//修改脏标志,如果以任何方式改变了超级块,需要向磁盘回写,都会将s_dirt设置为1,否则为0
unsigned char s_dirt;
//文件大小上限 Max file size
loff_t s_maxbytes;
//文件系统类型
struct file_system_type *s_type;
/*
struct super_operations
{
//给定的超级块下创建和初始化一个新的索引节点对象;
struct inode *(*alloc_inode)(struct super_block *sb);
//用于释放给定的索引节点;
void (*destroy_inode)(struct inode *);
//VFS在索引节点脏(被修改)时会调用此函数,日志文件系统(如ext3,ext4)执行该函数进行日志更新;
void (*dirty_inode) (struct inode *);
//用于将给定的索引节点写入磁盘,wait参数指明写操作是否需要同步;
int (*write_inode) (struct inode *, struct writeback_control *wbc);
//在最后一个指向索引节点的引用被释放后,VFS会调用该函数,VFS只需要简单地删除这个索引节点后,普通Uinx文件系统就不会定义这个函数了;
void (*drop_inode) (struct inode *);
//用于从磁盘上删除给定的索引节点;
void (*delete_inode) (struct inode *);
//在卸载文件系统时由VFS调用,用来释放超级块,调用者必须一直持有s_lock锁;
void (*put_super) (struct super_block *);
//用给定的超级块更新磁盘上的超级块。VFS通过该函数对内存中的超级块和磁盘中的超级块进行同步。调用者必须一直持有s_lock锁;
void (*write_super) (struct super_block *);
//使文件系统的数据元与磁盘上的文件系统同步。wait参数指定操作是否同步;
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
//VFS通过调用该函数获取文件系统状态。指定文件系统县官的统计信息将放置在statfs中;
int (*statfs) (struct dentry *, struct kstatfs *);
//当指定新的安装选项重新安装文件系统时,VFS会调用该函数。调用者必须一直持有s_lock锁;
int (*remount_fs) (struct super_block *, int *, char *);
//VFS调用该函数释放索引节点,并清空包含相关数据的所有页面;
void (*clear_inode) (struct inode *);
//VFS调用该函数中断安装操作。该函数被网络文件系统使用,如NFS;
void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct vfsmount *);
int (*show_stats)(struct seq_file *, struct vfsmount *);
#ifdef CONFIG_QUOTA
ssize_t (*quota_read)(struct super_block *,
int, char *, size_t, loff_t);
ssize_t (*quota_write)(struct super_block *,
int, const char *, size_t, loff_t);
#endif
int (*bdev_try_to_free_page)(struct super_block*,
struct page*, gfp_t);
};
*/
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;
//目录挂载点,s_root将超级块与全局根目录的dentry项关联起来,只有通常可见的文件系统的超级块,才指向/(根)目录的dentry实例。具有特殊功能、不出现在通常的目录层次结构中的文件系统(例如管道或套接字文件系统),指向专门的项,不能通过普通的文件命令访问。处理文件系统对象的代码经常需要检查文件系统是否已经装载,而s_root可用于该目的,如果它为NULL,则该文件系统是一个伪文件系统,只在内核内部可见。否则,该文件系统在用户空间中是可见的
struct dentry *s_root;
//卸载信号量
struct rw_semaphore s_umount;
//超级块信号量
struct mutex s_lock;
//引用计数
int s_count;
//尚未同步标志
int s_need_sync;
//活动引用计数
atomic_t s_active;
#ifdef CONFIG_SECURITY
//安全模块
void *s_security;
#endif
struct xattr_handler **s_xattr;
//all inodes
struct list_head s_inodes;
//匿名目录项 anonymous dentries for (nfs) exporting
struct hlist_head s_anon;
//被分配文件链表,列出了该超级块表示的文件系统上所有打开的文件。内核在卸载文件系统时将参考该列表,如果其中仍然包含为写入而打开的文件,则文件系统仍然处于使用中,卸载操作失败,并将返回适当的错误信息
struct list_head s_files;
/* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */
struct list_head s_dentry_lru;
//unused dentry lru of dentry on lru
int s_nr_dentry_unused;
//指向了底层文件系统的数据所在的相关块设备
struct block_device *s_bdev;
struct backing_dev_info *s_bdi;
struct mtd_info *s_mtd;
//该类型文件系统
struct list_head s_instances;
//限额相关选项 Diskquota specific options
struct quota_info s_dquot;
int s_frozen;
wait_queue_head_t s_wait_unfrozen;
//文本名字 Informational name
char s_id[32];
//Filesystem private info
void *s_fs_info;
fmode_t s_mode;
/*
* 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 */
/* Granularity of c/m/atime in ns. Cannot be worse than a second 指定了文件系统支持的各种时间戳的最大可能的粒度 */
u32 s_time_gran;
/*
* 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;
};
2.2 inode
inode保存着文件大小,设备标识符,用户标识符,用户组标识符,文件模式,扩展属性,文件读取或修改的时间戳,链接数量,指向存储该内容的磁盘区块的指针,文件分类等等。
inode分磁盘inode和vfs内存inode,内核会把磁盘inode读入内存,加上点东西形成内存inode,i_links_count为磁盘inode计数,i_nlink为内存inode计数。
inode 号(inode 是文件元数据的一部分但其并不包含文件名,inode 号即索引节点号)是文件的唯一标识而非文件名。文件名仅是为了方便人们的记忆和使用,系统或程序通过 inode 号寻找正确的文件数据块。
inode需要注意两个参数,i_count 与 i_nlink,i_count 为引用计数,i_nlink为硬链接计数,每个dentry都有一个唯一的inode,而每个inode则可能有多个dentry,由于硬链接是有着相同 inode 号仅文件名不同的文件,因此硬链接存在以下几点特性:
- 文件有相同的 inode 及 data block;
- 只能对已存在的文件进行创建;
- 不能交叉文件系统进行硬链接的创建;
- 不能对目录进行创建,只可对文件创建;
- 删除一个硬链接文件并不影响其他有相同 inode 号的文件。
软链接与硬链接不同,若文件用户数据块中存放的内容是另一文件的路径名的指向,则该文件就是软连接
struct inode
{
/*
哈希表
*/
struct hlist_node i_hash;
/*
索引节点链表(backing dev IO list)
*/
struct list_head i_list;
struct list_head i_sb_list;
/*
目录项链表
*/
struct list_head i_dentry;
/*
节点号
*/
unsigned long i_ino;
/*
引用记数
*/
atomic_t i_count;
/*
硬链接数
*/
unsigned int i_nlink;
/*
使用者id
*/
uid_t i_uid;
/*
使用者所在组id
*/
gid_t i_gid;
/*
实设备标识符
*/
dev_t i_rdev;
/*
版本号
*/
u64 i_version;
/*
以字节为单位的文件大小
*/
loff_t i_size;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
/*
最后访问时间
*/
struct timespec i_atime;
/*
最后修改(modify)时间
*/
struct timespec i_mtime;
/*
最后改变(change)时间
*/
struct timespec i_ctime;
/*
文件的块数
*/
blkcnt_t i_blocks;
/*
以位为单位的块大小
*/
unsigned int i_blkbits;
/*
使用的字节数
*/
unsigned short i_bytes;
/*
访问权限控制
*/
umode_t i_mode;
/*
自旋锁
*/
spinlock_t i_lock;
struct mutex i_mutex;
/*
索引节点信号量
*/
struct rw_semaphore i_alloc_sem;
/*
索引节点操作表
索引节点的操作inode_operations定义在linux/fs.h
struct inode_operations
{
/*
1. VFS通过系统调用create()和open()来调用该函数,从而为dentry对象创建一个新的索引节点。在创建时使用mode制定初始模式
*/
int (*create) (struct inode *, struct dentry *,int);
/*
2. 该函数在特定目录中寻找索引节点,该索引节点要对应于dentry中给出的文件名
*/
struct dentry * (*lookup) (struct inode *, struct dentry *);
/*
3. 该函数被系统调用link()调用,用来创建硬连接。硬链接名称由dentry参数指定,连接对象是dir目录中ld_dentry目录想所代表的文件
*/
int (*link) (struct dentry *, struct inode *, struct dentry *);
/*
4. 该函数被系统调用unlink()调用,从目录dir中删除由目录项dentry制动的索引节点对象
*/
int (*unlink) (struct inode *, struct dentry *);
/*
5. 该函数被系统调用symlik()调用,创建符号连接,该符号连接名称由symname指定,连接对象是dir目录中的dentry目录项
*/
int (*symlink) (struct inode *, struct dentry *, const char *);
/*
6. 该函数被mkdir()调用,创建一个新路径。创建时使用mode制定的初始模式
*/
int (*mkdir) (struct inode *, struct dentry *, int);
/*
7. 该函数被系统调用rmdir()调用,删除dir目录中的dentry目录项代表的文件
*/
int (*rmdir) (struct inode *, struct dentry *);
/*
8. 该函数被系统调用mknod()调用,创建特殊文件(设备文件、命名管道或套接字)。要创建的文件放在dir目录中,其目录项问dentry,关联的设备为rdev,初始权限由mode指定
*/
int (*mknod) (struct inode *, struct dentry *, int, dev_t);
/*
9. VFS调用该函数来移动文件。文件源路径在old_dir目录中,源文件由old_dentry目录项所指定,目标路径在new_dir目录中,目标文件由new_dentry指定
*/
int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *);
/*
10. 该函数被系统调用readlink()调用,拷贝数据到特定的缓冲buffer中。拷贝的数据来自dentry指定的符号链接,最大拷贝大小可达到buflen字节
*/
int (*readlink) (struct dentry *, char *, int);
/*
11. 该函数由VFS调用,从一个符号连接查找他指向的索引节点,由dentry指向的连接被解析
*/
int (*follow_link) (struct dentry *, struct nameidata *);
/*
12. 在follow_link()调用之后,该函数由vfs调用进行清楚工作
*/
int (*put_link) (struct dentry *, struct nameidata *);
/*
13. 该函数由VFS调用,修改文件的大小,在调用之前,索引节点的i_size项必须被设置成预期的大小
*/
void (*truncate) (struct inode *);
/*
该函数用来检查inode所代表的文件是否允许特定的访问模式,如果允许特定的访问模式,返回0,否则返回负值的错误码。多数文件系统都将此区域设置为null,使用VFS提供的通用方法进行检查,这种检查操作仅仅比较索引
及诶但对象中的访问模式位是否和mask一致,比较复杂的系统, 比如支持访问控制链(ACL)的文件系统,需要使用特殊的permission()方法
*/
int (*permission) (struct inode *, int);
/*
该函数被notify_change调用,在修改索引节点之后,通知发生了改变事件
*/
int (*setattr) (struct dentry *, struct iattr *);
/*
在通知索引节点需要从磁盘中更新时,VFS会调用该函数
*/
int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *);
/*
该函数由VFS调用,向dentry指定的文件设置扩展属性,属性名为name,值为value
*/
int (*setxattr) (struct dentry *, const char *, const void *, size_t, int);
/*
该函数被VFS调用,向value中拷贝给定文件的扩展属性name对应的数值
*/
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
/*
该函数将特定文件所有属性别表拷贝到一个缓冲列表中
*/
ssize_t (*listxattr) (struct dentry *, char *, size_t);
/*
该函数从给定文件中删除指定的属性
*/
int (*removexattr) (struct dentry *, const char *);
};
*/
const struct inode_operations *i_op;
/*
默认的索引节点操作
former ->i_op->default_file_ops
*/
const struct file_operations *i_fop;
/*
相关的超级块
*/
struct super_block *i_sb;
/*
文件锁链表
*/
struct file_lock *i_flock;
/*
相关的地址映射
*/
struct address_space *i_mapping;
/*
设备地址映射
address_space结构与文件的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个 address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面
*/
struct address_space i_data;
#ifdef CONFIG_QUOTA
/*
节点的磁盘限额
*/
struct dquot *i_dquot[MAXQUOTAS];
#endif
/*
块设备链表
*/
struct list_head i_devices;
union
{
//管道信息
struct pipe_inode_info *i_pipe;
//块设备驱动
struct block_device *i_bdev;
struct cdev *i_cdev;
};
/*
索引节点版本号
*/
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
/*
目录通知掩码
all events this inode cares about
*/
__u32 i_fsnotify_mask;
struct hlist_head i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif
#ifdef CONFIG_INOTIFY
struct list_head inotify_watches; /* watches on this inode */
struct mutex inotify_mutex; /* protects the watches list */
#endif
/*
状态标志
*/
unsigned long i_state;
/*
首次修改时间
jiffies of first dirtying
*/
unsigned long dirtied_when;
/*
文件系统标志
*/
unsigned int i_flags;
/*
写者记数
*/
atomic_t i_writecount;
#ifdef CONFIG_SECURITY
/*
安全模块
*/
void *i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
void *i_private; /* fs or device private pointer */
};
2.3 dentry 目录
目录项是描述文件的逻辑属性,只存在于内存中,并没有实际对应的磁盘上的描述,更确切的说是存在于内存的目录项缓存,为了提高查找性能而设计。注意不管是文件夹还是最终的文件,都是属于目录项,所有的目录项在一起构成一颗庞大的目录树。例如:open一个文件/home/xxx/yyy.txt,那么/、home、xxx、yyy.txt都是一个目录项,VFS在查找的时候,根据一层一层的目录项找到对应的每个目录项的inode,那么沿着目录项进行操作就可以找到最终的文件。
目录也是一种文件(所以也存在对应的inode)
struct dentry
{
//目录项引用计数器
atomic_t d_count;
/*
目录项标志 protected by d_lock
#define DCACHE_AUTOFS_PENDING 0x0001 // autofs: "under construction"
#define DCACHE_NFSFS_RENAMED 0x0002 // this dentry has been "silly renamed" and has to be eleted on the last dput()
#define DCACHE_DISCONNECTED 0x0004 //指定了一个dentry当前没有连接到超级块的dentry树
#define DCACHE_REFERENCED 0x0008 //Recently used, don't discard.
#define DCACHE_UNHASHED 0x0010 //该dentry实例没有包含在任何inode的散列表中
#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 // Parent inode is watched by inotify
#define DCACHE_COOKIE 0x0040 // For use by dcookie subsystem
#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 // Parent inode is watched by some fsnotify listener
*/
unsigned int d_flags;
//per dentry lock
spinlock_t d_lock;
//当前dentry对象表示一个装载点,那么d_mounted设置为1,否则为0
int d_mounted;
/*
文件名所属的inode,如果为NULL,则表示不存在的文件名
如果dentry对象是一个不存在的文件名建立的,则d_inode为NULL指针,这有助于加速查找不存在的文件名,通常情况下,这与查找实际存在的文件名同样耗时
*/
struct inode *d_inode;
/*
The next three fields are touched by __d_lookup. Place them here so they all fit in a cache line.
*/
//用于查找的散列表 lookup hash list
struct hlist_node d_hash;
/*
指向当前的dentry实例的父母了的dentry实例 parent directory
当前的dentry实例即位于父目录的d_subdirs链表中,对于根目录(没有父目录),d_parent指向其自身的dentry实例
*/
struct dentry *d_parent;
/*
d_iname指定了文件的名称,qstr是一个内核字符串的包装器,它存储了实际的char*字符串以及字符串长度和散列值,这使得更容易处理查找工作
要注意的是,这里并不存储绝对路径,而是只有路径的最后一个分量,例如对/usr/bin/emacs只存储emacs,因为在linux中,路径信息隐含在了dentry层次链表结构中了
*/
struct qstr d_name;
//LRU list
struct list_head d_lru;
/*
* d_child and d_rcu can share memory
*/
union
{
/* child of parent list */
struct list_head d_child;
//链表元素,用于将dentry连接到inode的i_dentry链表中
struct rcu_head d_rcu;
} d_u;
//our children 子目录/文件的目录项链表
struct list_head d_subdirs;
/*
inode alias list 链表元素,用于将dentry连接到inode的i_dentry链表中
d_alias用作链表元素,以连接表示相同文件的各个dentry对象,在利用硬链接用两个不同名称表示同一文件时,会发生这种情况,对应于文件的inode的i_dentry成员用作该链表的表头,各个dentry对象通过d_alias连接到该链表中
*/
struct list_head d_alias;
//used by d_revalidate
unsigned long d_time;
/*
d_op指向一个结构,其中包含了各种函数指针,提供对dentry对象的各种操作,这些操作必须由底层文件系统实现
struct dentry_operations
{
//在把目录项对象转换为一个文件路径名之前,判定该目录项对象是否依然有效
int (*d_revalidate)(struct dentry *, struct nameidata *);
//生成一个散列值,用于目录项散列表
int (*d_hash) (struct dentry *, struct qstr *);
//比较两个文件名
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
//当对目录项对象的最后一个引用被删除,调用该方法
int (*d_delete)(struct dentry *);
//当要释放一个目录项对象时,调用该方法
void (*d_release)(struct dentry *);
//当一个目录对象变为负状态时,调用该方法
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
};
*/
const struct dentry_operations *d_op;
//The root of the dentry tree dentry树的根,超级块
struct super_block *d_sb;
//fs-specific data 特定文件系统的数据
void *d_fsdata;
/*
短文件名small names存储在这里
如果文件名由少量字符组成,则只保存在d_iname中,而不是dnanme中,用于加速访问
*/
unsigned char d_iname[DNAME_INLINE_LEN_MIN];
};
2.4 file结构
件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构
struct file
{
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union
{
/*
定义在 linux/include/linux/list.h中
struct list_head
{
struct list_head *next, *prev;
};
用于通用文件对象链表的指针,所有打开的文件形成一个链表
*/
struct list_head fu_list;
/*
定义在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内核中新的锁机制
*/
struct rcu_head fu_rcuhead;
} f_u;
/*
定义在linux/include/linux/namei.h中
struct path
{
/*
struct vfsmount *mnt的作用是指出该文件的已安装的文件系统,即指向VFS安装点的指针
*/
struct vfsmount *mnt;
/*
struct dentry *dentry是与文件相关的目录项对象,指向相关目录项的指针
*/
struct dentry *dentry;
};
*/
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
/*
指向文件操作表的指针
定义在linux/include/linux/fs.h中,其中包含着与文件关联的操作,例如
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 (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, 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 (*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 (*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);
int (*setlease)(struct file *, long, struct file_lock **);
};
当打开一个文件时,内核就创建一个与该文件相关联的struct file结构,其中的*f_op就指向的是具体对该文件进行操作的函数
例如用户调用系统调用read来读取该文件的内容时,那么系统调用read最终会陷入内核调用sys_read函数,而sys_read最终会调用于该文件关联的struct file结构中的f_op->read函数对文件内容进行读取
*/
const struct file_operations *f_op;
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
/*
typedef struct { volatile int counter; } atomic_t;
volatile修饰字段告诉gcc不要对该类型的数据做优化处理,对它的访问都是对内存的访问,而不是对寄存器的访问
f_count的作用是记录对文件对象的引用计数,也即当前有多少个进程在使用该文件
*/
atomic_long_t f_count;
/*
当打开文件时指定的标志,对应系统调用open的int flags参数。驱动程序为了支持非阻塞型操作需要检查这个标志
*/
unsigned int f_flags;
/*
对文件的读写模式,对应系统调用open的mod_t mode参数。如果驱动程序需要这个值,可以直接读取这个字段。
mod_t被定义为:
typedef unsigned int __kernel_mode_t;
typedef __kernel_mode_t mode_t;
*/
fmode_t f_mode;
/*
当前的文件指针位置,即文件的读写位置
loff_t被定义为:
typedef long long __kernel_loff_t;
typedef __kernel_loff_t loff_t;
*/
loff_t f_pos;
/*
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时间通知的数据
*/
struct fown_struct f_owner;
const struct cred *f_cred;
/*
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
*/
struct file_ra_state f_ra;
/*
记录文件的版本号,每次使用后都自动递增
*/
u64 f_version;
#ifdef CONFIG_SECURITY
/*
#ifdef CONFIG_SECURITY
void *f_security;
#endif
如果在编译内核时配置了安全措施,那么struct file结构中就会有void *f_security数据项,用来描述安全措施或者是记录与安全有关的信息。
*/
void *f_security;
#endif
/*
系统在调用驱动程序的open方法前将这个指针置为NULL。驱动程序可以将这个字段用于任意目的,也可以忽略这个字段。驱动程序可以用这个字段指向已分配的数据,但是一定要在内核释放file结构前的release方法中清除它
*/
void *private_data;
#ifdef CONFIG_EPOLL
/*
被用在fs/eventpoll.c来链接所有钩到这个文件上。其中
1) f_ep_links是文件的事件轮询等待者链表的头
2) f_ep_lock是保护f_ep_links链表的自旋锁
*/
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
/*
struct address_space被定义在/linux/include/linux/fs.h中,此处是指向文件地址空间的指针
*/
struct address_space *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};