参考文献:
https://blog.csdn.net/tiankong_/article/details/76904222
https://www.cnblogs.com/smartjourneys/p/7260911.html
https://www.cnblogs.com/smartjourneys/p/7260911.html
https://blog.csdn.net/mxgsgtc/article/details/58676106
https://www.linuxidc.com/Linux/2017-06/145065.htm
https://www.cnblogs.com/linghuchong0605/p/4216219.html
文章目录
1.概述
在LINUX系统中有一个重要的概念:一切都是文件。 其实这是UNIX哲学的一个体现,而Linux是重写UNIX而来,所以这个概念也就传承了下来。在UNIX系统中,把一切资源都看作是文件,包括硬件设备。UNIX系统把每个硬件都看成是一个文件,通常称为设备文件,这样用户就可以用读写文件的方式实现对硬件的访问。
为了支持多种系统,并对上层应用屏蔽底层特性,linux 提供了一个抽象层:VFS。
2.VFS相关数据结构
2.1 super_block
(1)super_block: 超级块
超级块就是对所有文件系统的管理机构,每种文件系统都要把自己的信息挂到super_blocks这么一个全局链表上。
超级块代表了整个文件系统,超级块是文件系统的控制块,有整个文件系统信息,一个文件系统所有的inode都要连接到超级块上,可以说,一个超级块就代表了一个文件系统。
注意:一个文件系统类型下可以包括很多文件系统即很多的super_block
struct super_block
{
// 连接到系统全局的super_blocks链指针
struct list_head s_list;/* Keep this first */
//设备标识符
dev_t s_dev;/* search index; _not_ kdev_t */
//块大小、块大小的位数,是否文件系统脏,最大文件大小。
unsignedlong s_blocksize;
unsignedlong s_old_blocksize;
unsignedchar s_blocksize_bits;
unsignedchar s_dirt;
unsignedlonglong s_maxbytes; /* Max file size */
struct file_system_type *s_type; // 所表示的文件系统的类型
// 超级块内的指向根目录的dentry结构体
struct dentry *s_root;
// 文件相关操作函数集合表
struct super_operations *s_op;
//文件系统的配额操作函数集合,文件系统的配额控制操作函数集合,网络文件系统的导出操作函数集合。
struct dquot_operations *dq_op;//
struct quotactl_ops *s_qcop;//
struct export_operations *s_export_op;
unsignedlong s_flags;//
unsignedlong s_magic;//
struct rw_semaphore s_umount;//文件系统卸载时候用到的读写信号量。
struct semaphore s_lock;//
int s_count;//
int s_syncing;//
int s_need_sync_fs;//
atomic_t s_active; //
void*s_security;//
struct xattr_handler **s_xattr;//
struct list_head s_inodes;/* all inodes */// 链接文件系统的inode
struct list_head s_dirty;/* dirty inodes */
struct list_head s_io;/* parked for writeback */
struct hlist_head s_anon;/* anonymous dentries for (nfs) exporting */
struct list_head s_files;// 对于每一个打开的文件,由file对象来表示。链接文件系统中file
struct block_device *s_bdev;//
struct list_head s_instances;//
struct quota_info s_dquot;/* Diskquota specific options */
int s_frozen;//
wait_queue_head_t s_wait_unfrozen;//
char s_id[32];/* Informational name */
void*s_fs_info;/* Filesystem private info */
/**
* The next field is for VFS *only*. No filesystems have any business
* even looking at it. You had been warned.
*/
struct semaphore s_vfs_rename_sem;/* Kludge */
/* Granuality of c/m/atime in ns.
Cannot be worse than a second */
u32 s_time_gran;
};
2.1.1 重要成员分析
(1)基本元数据
- long s_blocksize:块大小;
- long s_maxbytes:最大文件大小。
- char s_blocksize_bits:块大小的位数
- long s_magic:每一个超级块有一个唯一的魔术数标记
- dev_t s_dev : 对应设备标识;
(2)指针类型,用于作为某种连接的头节点或是 连接到某一链表上的指针
类型 | 成员名称 | 描述 |
---|---|---|
struct list_head | s_list | 连接到系统全局的super_blocks链指针。所有的super_block成员都会挂到super_blocks这么一个全局链表上 |
struct list_head | s_inodes | 该文件系统上的inode链表头指针; |
struct list_head | s_dirty | 该文件系统上 打开的 脏的 inode 链表头指针 |
struct list_head | s_io | 所有这个文件系统的将要写入的inode都在这个队列上。 |
list_head | s_files | 打开的文件集合 |
(3)操作函数型
类型 | 成员名称 | 描述 |
---|---|---|
struct super_operations | *s_op | super_block的操作函数集合。 |
struct dquot_operations | *dq_op | 文件系统的配额操作函数集合 |
struct export_operations | *s_export_op | 网络文件系统的导出操作函数集合 |
(4)同步、引用计数相关
类型 | 成员名称 | 描述 |
---|---|---|
struct rw_semaphore | s_umount | 文件系统卸载时候用到的读写信号量。 |
struct mutex | s_lock | 专用的互斥量 |
int | s_count | 引用计数 |
int | s_syncing | 文件系统的同步标记位 |
2.2 inode索引节点
文件系统由子目录和文件构成。每个子目录和文件只能由唯一的inode 描述。inode 是Linux管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。
VFS inode的内容取自物理设备上的文件系统,由文件系统指定的操作函数(i_op 属性指定)填写。VFS inode只存在于内存中,可通过inode缓存访问。
inode有两种,一种是VFS的inode,一种是具体文件系统的inode。前者在内存中,后者在磁盘中。所以每次其实是将磁盘中的inode调进填充内存中的inode,这样才是算使用了磁盘文件inode。
这里说的VFS的inode
struct inode {
struct hlist_node i_hash; /* 哈希表 */
struct list_head i_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; /* 使用者id */
gid_t i_gid; /* 使用者id组 */
kdev_t i_rdev; /* 实设备标识符 */
loff_t i_size; /* 以字节为单位的文件大小 */
struct timespec i_atime; /* 最后访问时间 */
struct timespec i_mtime; /* 最后修改(modify)时间 */
struct timespec i_ctime; /* 最后改变(change)时间 */
unsigned int i_blkbits; /* 以位为单位的块大小 */
unsigned long i_blksize; /* 以字节为单位的块大小 */
unsigned long i_version; /* 版本号 */
unsigned long i_blocks; /* 文件的块数 */
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; /* 状态标志 */
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;
};
2.2.1 主要成员
(1)基本元数据
- unsigned long i_ino : inode 编号
- uid_t i_uid : 使用者id
- gid_t i_gid; :使用者id组
- struct timespec i_atime : 最后访问时间
- struct timespec i_mtime :最后修改(modify)时间
- struct timespec i_ctime : 最后改变(change)时间
- unsigned int i_nlink : 硬链接数
- unsigned long i_blocks :文件的块数
(2)指针类型,用于作为某种连接的头节点或是 连接到某一链表上的指针
类型 | 成员名称 | 描述 |
---|---|---|
struct list_head | i_hash | 哈希表 |
struct list_head | i_list | 索引节点链表 |
struct list_head | i_dentry | 目录项链表 |
struct list_head | i_devices | 块设备链表 |
(3)操作函数型
类型 | 成员名称 | 描述 |
---|---|---|
struct inode_operations | *i_op | 索引节点操作表 |
struct file_operations | *i_fop | 对应文件操作表 |
(4)存储相关
类型 | 成员名称 | 描述 |
---|---|---|
struct address_space | *i_mapping | 相关的地址映射 |
struct address_space | i_data | 设备地址映射 |
loff_t | i_size | 以字节为单位的文件大小 |
unsigned long | i_blocks | 文件的块数 |
(5)同步、引用计数相关
类型 | 成员名称 | 描述 |
---|---|---|
struct rw_semaphore | i_alloc_sem | 索引节点信号量 |
int | s_count | 引用计数 |
int | s_syncing | 文件系统的同步标记位 |
spinlock_t | i_lock | 自旋锁 |
2.3 dentry
每个文件除了一个struct inode结构体外,还要一个目录项struct dentry结构。dentry代表的逻辑意义上的文件,描述的是文件逻辑上的属性,目录项对象在磁盘上并没有对应的映像(inode代表的是物理意义上的文件,记录的是物理上的属性,对于一个具体的文件系统,其inode在磁盘上有对应的映像)
另外,一个索引节点可能对应多个目录项对象(如建立了软链的文件,多个目录项对应通过一个inode )。所以在inode结构中有一个队列i_dentry,凡是代表着同一个文件的所有目录项都通过其dentry结构中的d_alias域挂入相应inode结构中的i_dentry队列。
struct dentry {
/* RCU lookup touched fields */
unsigned int d_flags; /* protected by d_lock */
seqcount_t d_seq; /* per dentry seqlock */
struct hlist_bl_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name;
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
/* Ref lookup also touches following */
struct lockref d_lockref; /* per-dentry lock and refcount */
const struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
unsigned long d_time; /* used by d_revalidate */
void *d_fsdata; /* fs-specific data */
union {
struct list_head d_lru; /* LRU list */
wait_queue_head_t *d_wait; /* in-lookup ones only */
};
struct list_head d_child; /* child of parent list */
struct list_head d_subdirs; /* our children */
/*
* d_alias and d_rcu can share memory
*/
union {
struct hlist_node d_alias; /* inode alias list */
struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */
struct rcu_head d_rcu;
} d_u;
};
2.3.1 主要成员
(1)基本元数据
- unsigned int d_flags : 目录项标志
- struct inode * d_inode : 与文件名关联的索引节点
- struct dentry * d_parent : 父目录的目录项
- struct super_block * d_sb : 目录项树的根 (即文件的超级块)
(2)指针类型,用于作为某种连接的头节点或是 连接到某一链表上的指针
类型 | 成员名称 | 描述 |
---|---|---|
struct list_head | d_hash | 目录项形成的哈希表 |
struct list_head | d_lru | 未使用的 LRU 链表 |
struct list_head | d_child | 父目录的子目录项所形成的链表 |
struct list_head | d_subdirs | 该目录项的子目录所形成的链表 |
在内核中有一个哈希表dentry_hashtable ,是一个list_head的指针数组。一旦在内存中建立起一个目录节点的dentry 结构,该dentry结构就通过其d_hash域链入哈希表中的某个队列中。
内核中还有一个队列dentry_unused,凡是已经没有用户(count域为0)使用的dentry结构就通过其d_lru域挂入这个队列。
Dentry结构中除了d_alias 、d_hash、d_lru三个队列外,还有d_vfsmnt、d_child及d_subdir三个队列。其中d_vfsmnt仅在该dentry为一个安装点时才使用。另外,当该目录节点有父目录时,则其dentry结构就通过d_child挂入其父节点的d_subdirs队列中,同时又通过指针d_parent指向其父目录的dentry结构,而它自己各个子目录的dentry结构则挂在其d_subdirs域指向的队列中。
从上面的叙述可以看出,一个文件系统中所有目录项结构或组织为一个哈希表,或组织为一颗树,或按照某种需要组织为一个链表,这将为文件访问和文件路径搜索奠定下良好的基础。
(3)操作函数型
类型 | 成员名称 | 描述 |
---|---|---|
struct dentry_operations | *d_op | 目录项的函数集 |
struct file_operations | *i_fop | 对应文件操作表 |
2.4 file
进程通过文件描述符来访问文件。系统中的每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核创建和驱动源码 中,struct file的指针通常被命名为file或filp。
进程会保存自己所有打开的文件,当fork子进程时,默认情况下,子进程会继承父进程中的文件描述符列表。
struct file {
union {
struct llist_node fu_llist;//文件对象链表
struct rcu_head fu_rcuhead; //释放之后的RCU链表
} f_u;
struct path f_path;
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
atomic_long_t f_count; //文件对象的使用计数
unsigned int f_flags; //当打开文件时所使用的标志
fmode_t f_mode; //文件的访问模式
struct mutex f_pos_lock;
loff_t f_pos; //文件当前的位移量
struct fown_struct f_owner; //拥有者通过信号量进行异步I/O传输
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security; //安全模块
#endif
/* needed for tty driver, and maybe others */
void *private_data; //tty 设备驱动的钩子
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links; //事件池锁
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping; //页缓存映射
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
2.4.1 主要成员
(1)基本元数据
- struct path f_path :包含dentry和mnt两个成员,用于确定文件路径
- atomic_t f_count :文件的引用计数(有多少进程打开该文件)
- unsigned int f_flags: 对应于open时指定的flag
- mode_t f_mode : 读写模式:open的mod_t mode参数
- off_t f_pos : 该文件在当前进程中的文件偏移量
(2)指针类型,用于作为某种连接的头节点或是 连接到某一链表上的指针
类型 | 成员名称 | 描述 |
---|---|---|
struct list_head | fu_list | 文件对象链表指针 |
struct rcu_head | fu_rcuhead | RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制 |
(3)操作函数型
类型 | 成员名称 | 描述 |
---|---|---|
struct file_operations | **f_op | 与该文件相关联的操作函数 |
struct file_operations | *i_fop | 对应文件操作表 |
(4)存储相关
类型 | 成员名称 | 描述 |
---|---|---|
struct address_space | *f_mapping | 页缓存映射 |
(5)同步、引用计数相关
类型 | 成员名称 | 描述 |
---|---|---|
struct spinlock_t | f_lock | 自旋锁 |
2.5 总结
-
在进程task_struct中,保存了进程打开的文件列表与进程的文件系统空间(为了支持容器概念,支持资源的隔离);
-
对于文件,分为:打开未修改的文件(inode_in_use)、不活跃(没有打开的文件,inode_unused )、打开修改了的文件。对于前两种,相关的文件对象都链到系统全局链表中。打开修改了的文件,链到文件的super_block中的dirty 链表中,以后要刷新同步时,遍历超级块的相关链表即可。
-
一个文件对应于一个inode,inode中记录了文件的大小、访问属性、修改时间、映射的地址(或是对于磁盘中的inode,记录了blocks信息)。但是inode中不包含文件名、路径信息(只要文件打开了后,内核都是通过文件描述符,找到对应的file 结构,inode结构,进行操作。文件名是给用户看的)
-
对于一个inode结构表示的文件操作通过 i_fop 指定。不同类型的文件,该 成员的值不同。inode根据文件类型,连接到所属的super block中。
-
super block定义了某个文件系统的总体特性,如最大的文件大小、块大小、文件系统安装目录的目录项等;