Linux下的文件系统2
2017-03-13
上文针对VFS的基本信息做了介绍,并简单介绍了VFS涉及的几个数据机构,本节结合LInux源码,对各个结构之间的关系进行分析。
一、总体架构图

总体架构图如上图所示,结合进程访问文件的实际情况,根据上图进行细节化的描述。进程通过其结构中的files_struct结构和文件建立联系,看戏files_struct结构
struct files_struct {
/*
* read mostly part
*/
atomic_t count;
struct fdtable __rcu *fdt;
struct fdtable fdtab;
/*
* written part on a separate cache line in SMP
*/
spinlock_t file_lock ____cacheline_aligned_in_smp;
int next_fd;
unsigned long close_on_exec_init[];
unsigned long open_fds_init[];
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
首先是一个原子变量,记录打开文件的个数,注意这里不只是普通文件,还包括设备文件等其他文件。next_fd记录当前下一个可用的文件描述符,用于在下次进程打开文件时快速分配。而close_on_exec_init和open_fds_init是位图。fd_array是初始化状态的文件描述符数组,而fdtab是真正管理文件描述符的结构。看下fdtable结构
struct fdtable {
unsigned int max_fds;
struct file __rcu **fd; /* current fd array */
unsigned long *close_on_exec;
unsigned long *open_fds;
struct rcu_head rcu;
};
max_fds表示最大的打开文件数,可以更改。fd是一个指向文件描述符数组的指针,初始化为files_struct结构中fd_array数组的地址,close_on_exec指向files_struct结构中的位域。open_fds是一个指向位域的指针,管理着当前打开的所有描述符,如果位域中的对应位被置位表示该描述符在使用中。
关于描述符表扩展的情况,最后进行解释。上面的描述符表中,都是指向file结构的指针。进程每打开一个文件,就会有一个file结构与之对应。换句话说,file结构记录的某次进程对文件的某一次操作信息。看下file结构
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
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
/*
* Protects f_ep_links, f_flags, f_pos vs i_size in lseek SEEK_CUR.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
#ifdef CONFIG_SMP
int f_sb_list_cpu;
#endif
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
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;
#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;
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};
同一个超级块下打开的所有文件都会通过双链表连接起来。另外,file结构中主要由对应文件的inode缓存,在下次访问不需要通过dentry查找inode了。还有一个path结构,该结构记录当前文件的emulation项和vfsmount结构。其余记录文件的权限模式、读写位置等信息,还有一个重要的函数表,保存操作文件的一些函数的指针。关键是一个进程在这里可以根据dentry查找到inode。究竟是如何查找的呢?看下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 */
unsigned int d_count; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
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 */
struct list_head d_lru; /* LRU list */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* our children */
struct hlist_node d_alias; /* inode alias list */
};
dentry结构中有指向当前操作文件的inode指针,父目录的dentry,,当然还包括文件名信息。注意这里文件名并没有作为属性保存在inode节点中,而是保存在dentry结构中。因为文件名对于系统来讲主要来查找inode,而通过dentry可以查找到inode,所以这里其实dentry之后就不需要文件名了。通过dentry还可以定位所属 的超级块。该结构中也有个函数表dentry_operations,主要是针对dentry的操作,如增加、删除dentry。一个目录下的所有子目录会形成一个链表,d_subdirs是链表头。而d_child作为一个节点,连接到父目录的子链表中。上节已经提到,系统中所有的dentry通过一个hash表维护起来,以便于查找。表头是全局变量dentry_hashtable.而对于未使用的dentry,内核使用dentry_unused全局链表来组织。因为每个父目录均会有一条自己子目录的链表,所以系统中还存在一个dentry树。
到目前为止已经找到了具体的inode,inode记录文件的真实属性信息,修改时间,是否是脏,以及在内存中的映射信息。看下inode结构
struct inode {
umode_t i_mode;
unsigned short i_opflags;
kuid_t i_uid;
kgid_t i_gid;
unsigned int i_flags;
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
const struct inode_operations *i_op;
struct super_block *i_sb;
struct address_space *i_mapping;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
/* Stat data, not accessed from path walking */
unsigned long i_ino;
/*
* Filesystems may only read i_nlink directly. They shall use the
* following functions for modification:
*
* (set|clear|inc|drop)_nlink
* inode_(inc|dec)_link_count
*/
union {
const unsigned int i_nlink;
unsigned int __i_nlink;
};
dev_t i_rdev;
loff_t i_size;
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned short i_bytes;
unsigned int i_blkbits;
blkcnt_t i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
/* Misc */
unsigned long i_state;
struct mutex i_mutex;
unsigned long dirtied_when; /* jiffies of first dirtying */
struct hlist_node i_hash;
struct list_head i_wb_list; /* backing dev IO list */
struct list_head i_lru; /* inode LRU list */
struct list_head i_sb_list;
union {
struct hlist_head i_dentry;
struct rcu_head i_rcu;
};
u64 i_version;
atomic_t i_count;
atomic_t i_dio_count;
atomic_t i_writecount;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct file_lock *i_flock;
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
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct hlist_head i_fsnotify_marks;
#endif
#ifdef CONFIG_IMA
atomic_t i_readcount; /* struct files open RO */
#endif
void *i_private; /* fs or device private pointer */
};
inode是一个比较庞大的结构,开头记录了文件的权限信息,如用户、用户组等。该结构中有个函数表inode_operations,记录针对inode的一些操作。inode中还有指向当前文件所属文件系统的超级块结构。当然一个至关重要的就是address_space 类型的i_mapping指针了。其指向一个address_space 结构,记录当前文件在内存中的映射情况。这点等会在分析。除此之外,记录文件的一些时间信息。前文说过,inode在内存中有三种类型:位于内存中但未使用的;位于内存中正在使用的;位于内存中已经发生变化即需要写会到磁盘的,前两种都是全局链表,第三种特定于超级块结构。除此之外,inode还在一个hash表中出现,表头是inode_hashtable,支持根据inode编号和超级块快速访问inode。
到此进程已经找到了具体的inode节点,后来又是如何把文件映射到内存中呢?一个核心结构就是address_space,先看下该结构
struct address_space {
struct inode *host; /* owner: inode, block_device */
struct radix_tree_root page_tree; /* radix tree of all pages */
spinlock_t tree_lock; /* and lock protecting it */
unsigned int i_mmap_writable;/* count VM_SHARED mappings */
struct rb_root i_mmap; /* tree of private and shared mappings */
struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
struct mutex i_mmap_mutex; /* protect tree, count, list */
/* Protected by tree_lock together with the radix tree */
unsigned long nrpages; /* number of total pages */
pgoff_t writeback_index;/* writeback starts here */
const struct address_space_operations *a_ops; /* methods */
unsigned long flags; /* error bits/gfp mask */
struct backing_dev_info *backing_dev_info; /* device readahead, etc */
spinlock_t private_lock; /* for use by the address_space */
struct list_head private_list; /* ditto */
void *private_data; /* ditto */
} __attribute__((aligned(sizeof(long))));
该结构特定于inode节点存在,多个进程可以共享同一个文件,所以在特定于进程的file结构中,有一个指向该inode address_space的指针f_mapping。具体的访问位置记录在file结构中。address_space仅仅负责对文件的映射,该结构管理了对应文件映射的所有内存区域vm_area_struct实例。上面的i_map作为一个红黑树根,关联所有的vm_area_struct,而i_mmap_nonliner是一个双向链表,关联所有非线性映射的vm_area_struct实例。该结构中还记录了所属inode节点的指针host,区域包含的虚拟页面的数量nrpages,当然还有一组操作函数,用于和设备交互,如读取一个页或者写入一个页,设置页面为脏等。关于进程虚拟内存的管理,参考另一篇文章:
Linux下的文件系统2的更多相关文章
- 全面了解Linux下Proc文件系统
全面了解Linux下Proc文件系统 Proc是一个虚拟文件系统,在Linux系统中它被挂载于/proc目录之上.Proc有多个功能 ,这其中包括用户可以通过它访问内核信息或用于排错,这其中一个非 ...
- Linux 下EXT2文件系统 —— 如何将蚂蚁和大象优雅的装进冰箱里
这一阵子真是偷懒,无时无刻不和自己身体中的懒癌做斗争.最终我还是被打败了,星期天两天几乎都是荒废过去的,在空闲的时候实际上我内心也是有点焦虑的,不知道去怎么度过这时间.学习吧又不想学习,看电视娱乐吧也 ...
- linux下的文件系统
转http://www.cnblogs.com/yyyyy5101/articles/1901842.html 谈谈个人对于文件系统的认识,其实这也体现了计算机操作系统的抽象:你不用管计算机中的文件如 ...
- Linux下删除文件系统空间不释放的问题
删除了Linux下的一个文件,但是系统空间并没有被释放. 如下:/home/hadmin/data/hadoop 使用了1.3T的空间,但是实际只使用了600多G 原因是我删除了一个600多G的文件, ...
- <解说linux下proc文件系统>
proc文件系统的作用是访问系统内核信息 proc不是一个真实的文件系统,它不占系统的外存空间,只是以文件的形式为用户访问linux内核数据提供接口,因为系统内核总是动态的变化,所以我们所捕捉到的也只 ...
- linux下查看文件系统类型
1. df -hT命令 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G) -T, --pr ...
- Linux下查看文件系统磁盘使用
[root@localhost ~]# df -h 可以查看所有文件系统的磁盘使用情况 du --max-depth=1 -h 可以查看当前目录下各子目录的磁盘使用情况 参考:http://www.2 ...
- Linux下网络文件系统NFS服务搭建易错点总结
一.环境准备: 1 [root@czh ~]# cat /etc/redhat-release 2 CentOS release 6.7 (Final) 3 [root@czh ~]# uname - ...
- Linux下识别分区文件系统类型
Linux下挂载文件系统有时候需要填写文件系统.但有的设备拿到手还不知道文件系统,这种情况,可以用 parted命令 # parted /dev/vda GNU Parted 3.2 Using /d ...
随机推荐
- blender show normals
https://blenderartists.org/forum/showthread.php?193096-Blender-2-5-how-to-show-normals-in-viewport
- maven and jwt
以目前浅薄的理解,jwt就是一种加密token的手段,这个token也只有自己能解开,如果客户端以cookie存这个token,可能会存在cookie被窃取的情况. 另外,jwt这中加密方式因为有过期 ...
- mac 上使用 zip 版的mysql
1. 下载: 2. 解压,然后复制到需要的目录下 3. 修改 /usr/local/mysql的所有者为mysql: chown -R mysql:mysql mysql (这一步我是没做,爱做不做. ...
- Quartz.net设置任务中同时最多运行一个实例 [DisallowConcurrentExecution]
Quartz定时任务默认都是并发执行的,不会等待上一次任务执行完毕,只要间隔时间到就会执行, 如果定时任执行太长,会长时间占用资源,导致其它任务堵塞. 比如Job设置1分钟跑一次,每次获取50条短信发 ...
- PXE(preboot execution environment):【网络】预启动执行环节:安装 debian 9系列:成功
PXE 安装的必要点和之前一样. 这里只着重说一下debian系列 特殊的地方: 第一:Release.gpg问题 该问题解决方式一:要求官方的dvd.cd中提供,貌似不太可能实现...... 该问题 ...
- linux windows安装python的最佳方式,miniconda
1.在linux安装python文章很多,但是步骤很多,没搞好还会把yum命令弄坏,要修复.这件事就发生在我身上,准确说不是我造成的,是总监自己安装python造成yum损坏的,然后需要运维去百度修改 ...
- cdh 安装调研
解决:No module named site http://blog.csdn.net/amgang/article/details/7030642 因为安装greenplum导致yum报如下错误: ...
- js中的运算符优先级
运算符有何很多,基本的可能都比较熟,单有些优先级很难记住.建议使用“()”将复杂的运算表达式区分好优先级. 我给运算符优先级做了一首小打油诗. 括号成员new函数 直new后置累计数 单目幂算乘除模 ...
- nodejs & npm & gulp 安装和配置
熟悉 Hellolily的过程中,了解了这个. 环境: ubuntu 14.04 LTS 64bit 源码安装方式: 下载最新源码:如果被和谐请自行想办法. 解压并编译安装: cd node-xxx ...
- web.py框架之高级应用
二.高级应用 2.1 web.ctx 获取客户端信息,比如:来源页面.客户端浏览器类型等. web.ctx基于 threadeddict类,又被叫做 ThreadDict.这个类创建了一个类似字典(d ...