EXT4檔案系統學習(15)VFS之VFS 檔案/目錄物件

王二車發表於2019-03-05

無論下層具體檔案系統差異如何,VFS通過file結構向上層提供一個統一的檔案目錄物件。

VFS的檔案物件

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 */

比較重要的是f_op指標,指向一個file_operations結構體,定義如下:

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 (*read_iter) (struct kiocb *, struct iov_iter *);
	ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
	int (*iterate) (struct file *, struct dir_context *);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	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 (*mremap)(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 *, loff_t, loff_t, 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 **, void **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
	void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
	unsigned (*mmap_capabilities)(struct file *);
#endif
};

read和write是同步阻塞讀寫函式,read_iter和write_iter是非同步讀寫,由於需要相容下層大多數的具體檔案系統,所有定義了所有函式指標,但是在ext4_file_operations中只對某些函式指標賦值,file物件的f_op指標型別與inode的i_fop是一致的,i_fop是在ext4_fill_super函式中inode初始化時賦值的,f_op應該是也是在file物件初始化賦值的。

VFS的目錄物件

在VFS中每個目錄項都對應一個dentry物件,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 目錄對應inode
					 * 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;
};

檔案路徑是通過目錄樹來組織的,如/usr/bin,首先根目錄/對應一個dentry物件,然後bin目錄對應一個dentry物件,每個目錄項的名字儲存在d_name成員中,這些目錄通過d_child連結串列組織成樹的結構,例如根目錄下所有的dentry物件都連結在根目錄中的d_child連結串列中,同時各個dentry物件的d_parent指向父目錄的dentry物件。

不只是目錄才有目錄項,檔案也有dentry物件。

根據路徑定位一個檔案過程就是一層一層找對應的dentry物件,根目錄的dentry物件是在mount時建立的,從d_child找對應的dentry物件,如果記憶體中沒有就從磁碟中讀出。

各個具體檔案系統對目錄操作d_op也不一樣,定義如下:

struct dentry_operations {
	int (*d_revalidate)(struct dentry *, unsigned int);
	int (*d_weak_revalidate)(struct dentry *, unsigned int);
	int (*d_hash)(const struct dentry *, struct qstr *);
	int (*d_compare)(const struct dentry *, const struct dentry *,
			unsigned int, const char *, const struct qstr *);
	int (*d_delete)(const struct dentry *);
	void (*d_release)(struct dentry *);
	void (*d_prune)(struct dentry *);
	void (*d_iput)(struct dentry *, struct inode *);
	char *(*d_dname)(struct dentry *, char *, int);
	struct vfsmount *(*d_automount)(struct path *);
	int (*d_manage)(struct dentry *, bool);
	struct inode *(*d_select_inode)(struct dentry *, unsigned);
} ____cacheline_aligned;

d_op為NULL時表示使用預設dentry操作方式,我們的ext4是這樣使用預設的嗎?

VFS在程式中的檔案結構

各種管理結構的整體關係:

程式中fs中的root指向程式根目錄的dentry結構,而pwd指向程式當前目錄的dentry結構,遮蔽掉下層具體檔案系統的差異,程式看到的都是VFS的檔案物件。

 

相關文章