磁盘文件系统会包含有一个超级块,该块用于保存磁盘文件系统的一些属性和信息,内核设计了超级块对象用于描述超级块信息,超级块对象是存在于内存中的,超级块存在于磁盘上,因此需要定期同步。
磁盘文件系统是按照文件节点inode来管理文件的,内核中使用文件节点对象来管理文件节点inode。文件节点对象存在于内存中,而其中的一些数据需要和磁盘中保持同步。
磁盘文件系统会按照自己的方式管理和保存目录项,它存在的目的是管理文件系统中的文件名以及目录结构,一个目录项对应一个文件节点,而一个文件节点则可能对应多个目录项,比如硬链接就是这样。内核中使用目录项对象来管理目录项,目录项对象中的内容需要和磁盘目录项保持同步。
文件对象是内核用于描述进程如何与打开的文件进行交互操作的,它的结构体是struct file,它只存在于内存中,而在磁盘文件系统中不存在,使用文件对象可以找到对应的目录项对象,从而找到文件节点对象,并最终和物理磁盘联系在一起。每个进程都有自己创建的的文件对象集合用于记录当前进程打开的文件,每个文件对象都对应着一个目录项对象,而一个目录项对象可能对应着多个文件对象,比如当一个文件同时被两个进程打开时,每个进程都有一个该文件对应的文件对象,里面保存着当前的文件指针偏移量,而这两个文件对象实际上对应着同一个目录项。
每个进程都对应各自的文件管理结构struct files_struct,这个结构体管理着所有被进程打开和使用的文件对象指针,文件对象指针存放在一个数组结构中,而数组的下标就是文件描述符。多个描述符可能对应同一个文件对象。
多个文件描述符可能对应着同一个文件对象(dup操作),多个文件对象可能对应着一个文件目录项(多进程同时打开一个文件),多个文件目录项可能对应着一个文件索引节点(硬链接,硬链接是共享同一个inode,软链接不会)。
Linux支持多种不同类型的文件系统,为了保证统一的对上接口和一致性,内核实现了虚拟文件系统来达到这个目的,内核把各种类型的文件系统抽象为统一的模型,把它叫做虚拟文件系统。
进程相关的files_struct结构体:
/*
* Open file table structure
*/
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[1];
unsigned long open_fds_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
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;
};
fdtable中的fd初始化时指向的就是当前进程的fd array数组,NR_OPEN_DEFAULT等于32。在初始化时该成员是直接指向进程的fd_array数组的,后面如果进程打开文件数量超过此值,那么会进行expand扩展操作。
struct files_struct init_files = {
.count = ATOMIC_INIT(1),
.fdt = &init_files.fdtab,
.fdtab = {
.max_fds = NR_OPEN_DEFAULT,
.fd = &init_files.fd_array[0],
.close_on_exec = init_files.close_on_exec_init,
.open_fds = init_files.open_fds_init,
},
.file_lock = __SPIN_LOCK_UNLOCKED(init_files.file_lock),
};
默认的NR_OPEN_DEFAULT是32个,所以默认对应的open_fds和close_on_exec位图都是32bit的。
进程相关的fs_struct结构体:
struct fs_struct {
int users;
spinlock_t lock;
seqcount_t seq;
int umask;
int in_exec;
struct path root, pwd;
};
管理着当前进程的pwd当前路径,以及所在文件系统的root路径。路径包含的内容如下:
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
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 */
unsigned long dirtied_time_when;
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;
#ifdef CONFIG_IMA
atomic_t i_readcount; /* struct files open RO */
#endif
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct file_lock_context *i_flctx;
struct address_space i_data;
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
void *i_private; /* fs or device private pointer */
};
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 */
struct list_head d_lru; /* LRU list */
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 rcu_head d_rcu;
} d_u;
};
dentry通过d_inode成员和inode联系在一起的。
file结构体如下所示:
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} 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;
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;
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
file结构体通过path成员f_path与对应的dentry联系在一起。
filename结构体用来记录一个路径名称,其中包含audit_names进行相关的审计。
struct filename {
const char *name; /* pointer to actual string */
const __user char *uptr; /* original userland pointer */
struct audit_names *aname;
int refcnt;
bool separate; /* should "name" be freed? */
};
nameidata是用来在中间操作的结构体,包含dentry和inode相关的信息。
struct nameidata {
struct path path;
struct qstr last;
struct path root;
struct inode *inode; /* path.dentry.d_inode */
unsigned int flags;
unsigned seq, m_seq;
int last_type;
unsigned depth;
struct file *base;
char *saved_names[MAX_NESTED_LINKS + 1];
};