文件系统这一词在不同上下文时有不同的含义:
Ext2
,MSDOS的文件系统是FAT16
,而Windows NT的文件系统是NTFS
或FAT32
事实上,Linux除了本身的Ext2
以外,还支持其他各种不同的文件系统。Linux是通过将自身的文件系统抽象为一个文件系统界面,用户通过这个文件系统界面(即一组系统调用),可以对不同的文件系统进行操作。在用户看来,这是一个虚拟的、同一的、抽象的文件系统,用户并不关注文件系统的实际细节(文件系统类型、相关操作如何进行)。这就是所谓的虚拟文件系统VFS
。这个抽象的界面为用户提供一组标准的、抽象的文件操作,以系统调用的形式存在于用户程序,如read()
、write()
、lseek()
等等。比如Linux操作系统中,可以将DOS格式的磁盘或者分区(即文件系统)安装到系统中,然后用户程序可以以相同的方式访问这些文件,好像它们也是Ext2
格式的文件一样。
内核与不同文件系统的接口是通过file_operation
这个数据结构实现的。
/*
* NOTE:
* read, write, poll, fsync, readv, writev can be called
* without the big kernel lock held in all filesystems.
*/
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);
};
可以看到该结构体内几乎全部是函数指针。每一个文件系统都要负责实现自己的file_operations
数据结构,比如这里的read
就指向该文件系统的用来实现读文件操作的入口函数。
每个进程通过打开文件open()
与具体的某一个文件建立起联系,或者说建立起一个读写上下文。这种联系的实际承载者为file
数据结构。
在代表进程的task_struct
数据结构中有两个指针:
/* filesystem information */
struct fs_struct *fs;
/* open file information */
struct files_struct *files;
fs_struct
代表文件系统的信息,files_struct
代表已打开文件的信息
struct fs_struct {
atomic_t count;
rwlock_t lock;
int umask;
struct dentry * root, * pwd, * altroot;
struct vfsmount * rootmnt, * pwdmnt, * altrootmnt;
};
前三个指针指向dentry
结构体,dentry
记录目录项,因此pwd
代表进程当前的目录,root
代表进程的根目录,就是当用户登录进入系统时的目录,altroot
是为用户设置的替换根目录。实际运行时这三个目录不一定在同一个文件系统中,比如root
通常是安装于/
节点上的Ext2
文件系统中,而当前工作目录则可能安装于/dosc
的一个DOS文件系统中。后三个指针就各自指向代表着这些安装的vfsmount
数据结构。
一个进程已经打开的文件相关的信息存储在file
结构体中,而files_struct
结构体的主要内容就是file
结构体数组。
struct files_struct {
atomic_t count;
spinlock_t file_lock; /* Protects all the below members. Nests inside tsk->alloc_lock */
int max_fds;
int max_fdset;
int next_fd;
struct file ** fd; /* current fd array */
fd_set *close_on_exec;
fd_set *open_fds;
fd_set close_on_exec_init;
fd_set open_fds_init;
struct file * fd_array[NR_OPEN_DEFAULT];
};
在file
结构体中:
struct file{
...
struct dentry *f_dentry;
struct vfsmount *f_vfsmnt;
struct file_operations *f_op;
...
}
f_op
指针指向该文件所属文件系统的file_operation
结构体,因此通过file
可以知道如何对该文件进行操作;f_dentry
指向该文件的dentry
数据结构,即记录了该文件的目录信息,为什么这里不将dentry
作为file
文件的成员而是用一个指针来指向dentry
呢?是因为同一个文件只有一个dentry
结构体,而该文件可能被多个进程打开,每当该文件被一个进程打开就会在该进程的task_struct
中创建一个file
结构体实例。
总之,Linux通过虚文件系统提供一组约定的数据结构,包括有dentry
、inode
、dentry_operations
、inode_operations
等。这些数据结构的内容在进程与某一个文件建立联系open
之后被初始化,初始化的结果与文件所处介质的文件系统类型有关。虽然不同文件系统的实现细节不同,比如不同的文件系统可能使用不同的索引机制,但是他们都会有实现类似功能的具体数据结构。因此只对于不同的文件系统,Linux利用函数指针数组去寻找对应文件系统设备驱动中的函数,来完成相似的任务。
struct super_block {
struct list_head s_list; /* list of all superblocks */
dev_t s_dev; /* identifier */
unsigned long s_blocksize; /* block size in bytes */
unsigned char s_blocksize_bits; /* block size in bits */
unsigned char s_dirt; /* dirty flag */
unsigned long long s_maxbytes; /* max file size */
struct file_system_type *s_type; /* filesystem type */
struct super_operations *s_op; /* superblock methods */
struct dquot_operations *dq_op; /* quota methods */
struct quotactl_ops *s_qcop; /* quota control methods */
struct export_operations *s_export_op; /* export methods */
unsigned long s_flags; /* mount flags */
unsigned long s_magic; /* filesystem’s magic number */
struct dentry *s_root; /* directory mount point */
struct rw_semaphore s_umount; /* unmount semaphore */
struct semaphore s_lock; /* superblock semaphore */
int s_count; /* superblock ref count */
int s_need_sync; /* not-yet-synced flag */
atomic_t s_active; /* active reference count */
void *s_security; /* security module */
struct xattr_handler **s_xattr; /* extended attribute handlers */
struct list_head s_inodes; /* list of inodes */
struct list_head s_dirty; /* list of dirty inodes */
struct list_head s_io; /* list of writebacks */
struct list_head s_more_io; /* list of more writeback */
struct hlist_head s_anon; /* anonymous dentries */
struct list_head s_files; /* list of assigned files */
struct list_head s_dentry_lru; /* list of unused dentries */
int s_nr_dentry_unused; /* number of dentries on list */
struct block_device *s_bdev; /* associated block device */
struct mtd_info *s_mtd; /* memory disk information */
struct list_head s_instances; /* instances of this fs */
struct quota_info s_dquot; /* quota-specific options */
int s_frozen; /* frozen status */
wait_queue_head_t s_wait_unfrozen; /* wait queue on freeze */
char s_id[32]; /* text name */
void *s_fs_info; /* filesystem-specific info */
fmode_t s_mode; /* mount permissions */
struct semaphore s_vfs_rename_sem; /* rename semaphore */
u32 s_time_gran; /* granularity of timestamps */
char *s_subtype; /* subtype name */
char *s_options; /* saved mount options */
};
内核中的超级块对象由函数alloc_super()
负责创建。当一个文件系统被mount时,alloc_super()
函数会通过设备驱动从disk中读出该文件系统的superblock
,用文件系统的superblock
来初始化内核中的superblock
对象。
superblock
对象中最重要的成员时s_op
,它是一个指向superblock operation table
的指针,superblock operation struct
中的函数指针规定了对一个文件系统可以执行的操作。
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 *);
void (*write_inode) (struct inode *, int);
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 *sb, int wait);
void (*write_super_lockfs) (struct super_block *);
void (*unlockfs) (struct super_block *);
int (*statfs) (struct super_block *, struct kstatfs *);
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 *);
};
当文件系统需要对其superblock执行操作时,就会从其super_block struct
中的指针找到super_operations
结构,再根据函数指针跳转到相应的处理程序。而相应的处理程序是由不同的on-disk filesystem
来决定的。
inode object
代表了内核管理一个文件或者目录所需要的全部信息。对于Unix-style filesystem来说,这些信息可以直接从on-disk inode
读取,如果一个文件系统不包含inode
那么它需要根据自己文件系统的设计,找出具有相似功能的信息填充kernel inode
。
struct inode {
struct hlist_node i_hash; /* hash list */
struct list_head i_list; /* list of inodes */
struct list_head i_sb_list; /* list of superblocks */
struct list_head i_dentry; /* list of dentries */
unsigned long i_ino; /* inode number */
atomic_t i_count; /* reference counter */
unsigned int i_nlink; /* number of hard links */
uid_t i_uid; /* user id of owner */
gid_t i_gid; /* group id of owner */
kdev_t i_rdev; /* real device node */
u64 i_version; /* versioning number */
loff_t i_size; /* file size in bytes */
seqcount_t i_size_seqcount; /* serializer for i_size */
struct timespec i_atime; /* last access time */
struct timespec i_mtime; /* last modify time */
struct timespec i_ctime; /* last change time */
unsigned int i_blkbits; /* block size in bits */
blkcnt_t i_blocks; /* file size in blocks */
unsigned short i_bytes; /* bytes consumed */
umode_t i_mode; /* access permissions */
spinlock_t i_lock; /* spinlock */
struct rw_semaphore i_alloc_sem; /* nests inside of i_sem */
struct semaphore i_sem; /* inode semaphore */
struct inode_operations *i_op; /* inode ops table */
struct file_operations *i_fop; /* default inode ops */
struct super_block *i_sb; /* associated superblock */
struct file_lock *i_flock; /* file lock list */
struct address_space *i_mapping; /* associated mapping */
struct address_space i_data; /* mapping for device */
struct dquot *i_dquot[MAXQUOTAS]; /* disk quotas for inode */
struct list_head i_devices; /* list of block devices */
union{
struct pipe_inode_info *i_pipe; /* pipe information */
struct block_device *i_bdev; /* block device driver */
struct cdev *i_cdev; /* character device driver */
};
unsigned long i_dnotify_mask; /* directory notify mask */
struct dnotify_struct *i_dnotify; /* dnotify */
struct list_head inotify_watches; /* inotify watches */
struct mutex inotify_mutex; /* protects inotify_watches */
unsigned long i_state; /* state flags */
unsigned long dirtied_when; /* first dirtying time */
unsigned int i_flags; /* filesystem flags */
atomic_t i_writecount; /* count of writers */
void *i_security; /* security module */
void *i_private; /* fs private pointer */
所有的inode
都代表了文件系统中的一个文件,但是inode bject
只会在文件被访问时才会在内存中被实例化。这包括了特殊文件,比如设备文件或者管道文件。因此,struct inode
中的某些域就会与这些特定的文件相关。比如,i_pipe
域指向一个命名管道的数据结构,i_bdev
指向一个块设备数据结构。
正如在superblock operation
中一样,inode_opeartion
成员非常重要。
It describes the filesystem’s implemented functions that the VFS can invoke on an inode.
比如:
i->i_op->truncate(i)
这里的 i 是对某个特定inode的指针,对这个inode我们执行了truncate()
操作,而这个操作的具体执行函数是由inode所在的文件系统提供的。
struct inode_operations {
int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);
int (*mkdir) (struct inode *,struct dentry *,int);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,int,dev_t);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
int (*readlink) (struct dentry *, char __user *,int);
void * (*follow_link) (struct dentry *, struct nameidata *);
void (*put_link) (struct dentry *, struct nameidata *, void *);
void (*truncate) (struct inode *);
int (*permission) (struct inode *, int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*removexattr) (struct dentry *, const char *);
void (*truncate_range)(struct inode *, loff_t, loff_t);
long (*fallocate)(struct inode *inode, int mode, loff_t offset, loff_t len);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
};
inode_create
:VFS通过create()
和open()
系统调用来call这个函数,根据specified initial access mode
来创建一个和特定dentry object
相关联的新的inode
对象实例在on-disk
文件系统中,通常将目录也理解为文件,目录文件也有对应的inode
,只是目录文件的数据块中存储的是名字到inode
的映射关系。不过在Linux VFS中则是将所有文件与目录都抽象为目录,在所有inode
之上都抽象出一个dentry
,文件系统对inode
操作首先需要找到inode
在文件系统中对应的dentry
。这个关系在前面的Linux 文件系统结构图
中很清楚。之所以这么做,是为了加速对目录的访问。
比如有路径/bin/vi
,bin
在磁盘中是目录文件,vi
是常规文件。这两个文件都有自己的inode
。尽管有这种有用的统一化,VFS经常需要去执行一些directory-specific operation
,比如一些路径查找。路径名查找需要翻译一个路径的各个部分,确保每一步都是合法的,然后从一个部分查找到下一个部分。为了加速这个过程,VFS引入了directory entry
的概念。A dentry is a specific component in a path. 在前面的例子中,/,bin,vi
都是dentry object
。Dentry objects are all components in a path, including files. 这样就使得解析路径变得简单。
struct dentry {
atomic_t d_count; /* usage count */
unsigned int d_flags; /* dentry flags */
spinlock_t d_lock; /* per-dentry lock */
int d_mounted; /* is this a mount point? */
struct inode *d_inode; /* associated inode */
struct hlist_node d_hash; /* list of hash table entries */
struct dentry *d_parent; /* dentry object of parent */
struct qstr d_name; /* dentry name */
struct list_head d_lru; /* unused list */
union {
struct list_head d_child; /* list of dentries within */
struct rcu_head d_rcu; /* RCU locking */
} d_u;
struct list_head d_subdirs; /* subdirectories */
struct list_head d_alias; /* list of alias inodes */
unsigned long d_time; /* revalidate time */
struct dentry_operations *d_op; /* dentry operations table */
struct super_block *d_sb; /* superblock of file */
void *d_fsdata; /* filesystem-specific data */
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* short name */
};
dentry
也可能表示一个挂载点。比如在路径/mnt/cdrom/foo
中,/,mnt,cdrom,foo
都是dentry object
。VFS在执行directory operations
时实时地创建dentry object
。dentry
并不对应任何on-disk data structure
。
注意在dentry
中有
struct dentry *d_parent; /* dentry object of parent */
struct list_head d_subdirs; /* subdirectories */
可以先将这两个变量简单理解为指向父目录和自身所有子目录的指针。子目录是以一个d_subdirs
队列的形式组织的。因此,可以想到,dentry
就是在vfs
内部用来模拟on-disk files and directires
组织结构的数据结构。通过dentry
操作系统无需直接访问磁盘便可以快速知道磁盘文件和目录的逻辑结构。当然了,对dentry
的建立是基于磁盘数据组织结构的,因此起码需要对disk数据进行一次访问才能在内存中缓存
磁盘数据逻辑结构。
总结dentry
的主要作用是:
dentry
就可以得到磁盘上文件路径的信息。而不需要每次都要从磁盘读取目录文件进行路径解析;dentry cache
。一个合法的dentry对象可以有三种状态:used, unused, negative
dentry
对应着一个inode
,但是并没有被任何进行所使用。通常会将这种dentry
缓存起来,使得path name lookups
变得更快。negative dentry
的主要作用是将失败的访问路径也缓存下来。dentry cache
由三部分组成:
inode
会有不同的dentry
,因此inode
中的i_dentry
队列保存了所有指向该inode
的目录项。lru
队列保存了未被使用的以及negative dentry objects.Hash 表存储在dentry_hashtable
数组中。每一个元素都是一个指针,指向一组具有相同hash值的dentries
(链表形式组织起来),通过d_lookup()
函数可以查询hash表。
比如:现在正在编辑一个位于home文件夹下的源文件/home/dracula/src/the_sun_sucks.c
,每当访问这个文件时,为了解析全部路径:/,home,dracula,src,the_sun_sucks.c
,VFS必须follow each directory entry
。为了避免这种很费时间的操作,VFS可以首先尝试在dentry cache
中查找路径名。如果查询成功,那就可以直接得到所需要的dentry object
,如果失败了,那么VFS需要去向文件系统查找目录文件。在完成以后,内核将这些新的目录项加入dchche
来加速未来的查询。
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);
};
d_hash()
的作用是根据规定的dentry产生一个hash值,每当VFS将一个dentry加入哈希表时就会调用这个函数。
The file object is used to represent a file opened by a process. 当我们从用户空间的角度考虑VFS时,第一个遇到的就是file object。进程直接处理文件,而不是superblocks, inodes, 或者dentries.
The file object is the in-memory representation of an open file. The object is created in reponse to the open() system call and destroyed in response to the close() system call. 所有与文件相关的操作都被定义在file operation table
中。因为有可能多个进程会打开和管理同一个文件,那么对于同一个文件来说有可能会有多个对应的file objects
。The file object points back to the dentry that actually represents the open file. The inode and dentry objects, of course, are unique.(dentry 和 inode是多对一的关系,因为一个文件可能具有不同的路径)。
struct file {
union {
struct list_head fu_list; /* list of file objects */
struct rcu_head fu_rcuhead; /* RCU list after freeing */
} f_u;
struct path f_path; /* contains the dentry */
struct file_operations *f_op; /* file operations table */
spinlock_t f_lock; /* per-file struct lock */
atomic_t f_count; /* file object’s usage count */
unsigned int f_flags; /* flags specified on open */
mode_t f_mode; /* file access mode */
loff_t f_pos; /* file offset (file pointer) */
struct fown_struct f_owner; /* owner data for signals */
const struct cred *f_cred; /* file credentials */
struct file_ra_state f_ra; /* read-ahead state */
.u64 f_version; /* version number */
void *f_security; /* security module */
void *private_data; /* tty driver hook */
struct list_head f_ep_links; /* list of epoll links */
spinlock_t f_ep_lock; /* epoll lock */
struct address_space *f_mapping; /* page cache mapping */
unsigned long f_mnt_write_state; /* debugging state */
};
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 **);
};
loff_t llseek(struct file *file, loof_t offset, int origin)
用来将文件指针移动一个给定的距离。llseek()
系统调用最后会调用到这个函数。由于Linux支持许多不同的文件系统,内核必须有一个特殊的数据结构来描述不同文件系统的所具备的能力。file_system_type
就是这个作用:
struct file_system_type {
const char *name; /* filesystem’s name */
int fs_flags; /* filesystem type flags */
/* the following is used to read the superblock off the disk */
struct super_block *(*get_sb) (struct file_system_type *, int,
char *, void *);
/* the following is used to terminate access to the superblock */
void (*kill_sb) (struct super_block *);
struct module *owner; /* module owning the filesystem */
struct file_system_type *next; /* next file_system_type in list */
struct list_head fs_supers; /* list of superblock objects */
/* the remaining fields are used for runtime lock validation */
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key;
struct lock_class_key i_lock_key;
struct lock_class_key i_mutex_key;
struct lock_class_key i_mutex_dir_key;
struct lock_class_key i_alloc_sem_key;
};
get_sb()
函数从disk的超级块中读取数据,然后在文件系统被加载时将信息存入file_system_type
中。其余的函数描述了文件系统的特性。
每一个文件系统都只有一个file_system_type
,无论有多少个该文件系统的实例被挂载在系统中,或者该文件系统有没有被挂载。
当文件系统被挂载时,系统会创建一个vfsmount
数据结构。这个数据结构代表了一个特定的文件系统实例,换句话说,一个挂载点。
struct vfsmount {
struct list_head mnt_hash; /* hash table list */
struct vfsmount *mnt_parent; /* parent filesystem */
struct dentry *mnt_mountpoint; /* dentry of this mount point */
struct dentry *mnt_root; /* dentry of root of this fs */
struct super_block *mnt_sb; /* superblock of this filesystem */
struct list_head mnt_mounts; /* list of children */
struct list_head mnt_child; /* list of children */
int mnt_flags; /* mount flags */
char *mnt_devname; /* device file name */
struct list_head mnt_list; /* list of descriptors */
struct list_head mnt_expire; /* entry in expiry list */
struct list_head mnt_share; /* entry in shared mounts list */
struct list_head mnt_slave_list; /* list of slave mounts */
struct list_head mnt_slave; /* entry in slave list */
struct vfsmount *mnt_master; /* slave’s master */
struct mnt_namespace *mnt_namespace; /* associated namespace */
int mnt_id; /* mount identifier */
int mnt_group_id; /* peer group identifier */
atomic_t mnt_count; /* usage count */
int mnt_expiry_mark; /* is marked for expiration */
int mnt_pinned; /* pinned count */
int mnt_ghosts; /* ghosts count */
atomic_t __mnt_writers; /* writers count */
};
系统中的每一个进程都有自己的打开文件表,根文件系统,当前工作目录,挂载点等等。
files_struct
定义在
中,进程描述符中的files
域是一个指向files_struct
的指针。所有的关于进程打开文件的信息,以及文件描述符都在files_struct
其中。
struct files_struct {
atomic_t count; /* usage count */
struct fdtable *fdt; /* pointer to other fd table */
struct fdtable fdtab; /* base fd table */
spinlock_t file_lock; /* per-file lock */
int next_fd; /* cache of next available fd */
struct embedded_fd_set close_on_exec_init; /* list of close-on-exec fds */
struct embedded_fd_set open_fds_init /* list of open fds */
struct file *fd_array[NR_OPEN_DEFAULT]; /* base files array */
};
fd_array
数组中的元素为struct file*
指针。前面提到过,file
结构对应一个dentry
对象,open()
系统调用创造一个dentry
或者在dchche
中找到一个dentry
,并且创建一个指向dentry
的file
结构,将这个file
结构的地址加入到struct file *fd_array[]
中,然后返回在fd_array
中的索引作为open()
函数的返回值。
第二个与进程相关的数据结构是fs_struct
,包含了与该进程有关文件系统的信息,并且进程描述符中的fs
域指向这个数据结构。
struct fs_struct {
int users; /* user count */
rwlock_t lock; /* per-structure lock */
int umask; /* umask */
int in_exec; /* currently executing a file */
struct path root; /* root directory */
struct path pwd; /* current working directory */
};
这个结构中保存了当前工作目录(pwd)以及当前进程的根目录。
第三个重要的数据结构是namespace
结构,定义在
中,进程描述符中的mnt_namespace
域指向这个数据结构。从Linux 2.4内核开始,每个进程都有自己的namespace
,它使每个进程对系统上已经挂载的文件系统有一个自己独特的视角————不再是仅仅有一个根目录,而是一个完全的唯一的文件系统层级。
struct mnt_namespace {
atomic_t count; /* usage count */
struct vfsmount *root; /* root directory */
struct list_head list; /* list of mount points */
wait_queue_head_t poll; /* polling waitqueue */
int event; /* event count */
};
list
成员指向已经挂载了的文件系统的一个双向链表,这个双向链表组成了namespace
。对于大部分进程来说,它们的进程描述符会指向自己特有的files_struct
以及fs_struct
。对于通过具有克隆标志CLONE_FLAGS
或者CLONE_FS
创建的进程来说,它们则会共享filse_strucr、fs_struct
。这就导致不同的进程描述符会指向相同的
filse_strucr、fs_struct
。count
成员的作用就是记录共享文件进程的数量。
namespace
机制从相反的方向解决问题。默认情况下,所有的进程都具有相同的namespace
。(那就是说,they all see the same filesystem hierarchy from the same mount table)。只有当使用clone()
期间设置了CLONE_NEWNS
标志之后,每个进程才会具有一份namespace
结构的拷贝。否则大部分进程只是从其父母处继承namespace。