【Linux 內核】文件系統(四)

本篇將在前面的基礎上,再次通過剖析虛擬文件系統以及更下層文件系統結構,主要是基於結構篇深度剖析。

Linux內核的應用層就是以文件系統爲核心而展開的。文件系統在整個內核架構中具有基礎架構性質,字符設備、塊設備這些設備驅動的概念都要依靠文件系統來實現。

進階篇說到了存儲設備分區,引導區、超級塊區、inode table區以及數據塊區。

引導區主要是爲計算機開機服務的。我們這裏略過,先看

1、超級塊 super block

超級塊(super block)代表了整個文件系統本身,代表一個具體的已安裝文件系統,通常。超級塊是對應文件系統自身的控制塊結構,超級塊保存了文件系統設定的文件塊大小,超級塊的操作函數,文件系統內所有的inode也都要鏈接到超級塊的鏈表頭,當然還包括其餘的信息,通過超級塊對象,我們可以找到這些必要的信息。

超級塊是文件系統的心臟,是與文件系統相關的,在超級塊中保存了全局文件信息,如硬盤已用空間、數據塊可用空間、inode節點信息等,一個文件系統中有哪些資源都記錄在其中。

struct super_block {
	struct list_head	s_list;		/* Keep this first */
	dev_t			s_dev;		/* search index; _not_ kdev_t */
	unsigned long		s_blocksize;//指定了文件系統的塊大小
	unsigned char		s_blocksize_bits;
	unsigned char		s_dirt;
	unsigned long long	s_maxbytes;	/* Max file size *///最大文件的尺寸
	struct file_system_type	*s_type;//指向邋file_system_type結構的指針
	struct super_operations	*s_op;//超級塊操作函數集
	struct dquot_operations	*dq_op;
 	struct quotactl_ops	*s_qcop;
	struct export_operations *s_export_op;
	unsigned long		s_flags;
	unsigned long		s_magic;//魔術數字
	struct dentry		*s_root;//文件系統根目錄項
	struct rw_semaphore	s_umount;
	struct mutex		s_lock;//互斥鎖  SMP safe
	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,通過它可以遍歷所有inode對象
	struct list_head	s_dirty;	/* dirty inodes *///所有dirty的inode對象
	struct list_head	s_io;		/* parked for writeback */
	struct hlist_head	s_anon;		/* anonymous dentries for (nfs) exporting */
	struct list_head	s_files;

	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 mutex s_vfs_rename_mutex;	/* Kludge */

	/* Granularity of c/m/atime in ns.
	   Cannot be worse than a second */
	u32		   s_time_gran;
};

超級塊的內容需要讀取具體文件系統在硬盤上的超級塊結構獲得,超級塊是具體文件系統超級塊的內存抽象。

創建、管理和撤銷超級塊對象的代碼位於文件fs/super.c 中。超級塊對象通過 alloc_super() 函數創建並初始化。在文件系統安裝時,文件系統會調用該函數以便從磁盤讀取文件系統超級塊,並且將其信息填充到內存的超級塊對象中。

從其結構可以看出,超級塊包含了與文件系統相關的參數信息:包括inode表容量、文件系統中邏輯塊的大小、以邏輯塊計。文件系統的大小等。

2、目錄項 dentry

首先有必要區分一下目錄跟目錄項。在Unix語義中,一切皆文件,所以目錄也會被當成一個文件來處理,也就不存在目錄對象,那麼目錄項又是什麼?目錄項代表的是路徑中的一個組成部分,他可能包括一個普通文件,換言之,目錄可以包含子目錄(子文件)、目錄是可以層層嵌套的,所以形成了文件路徑,而文件路徑中的每一部分,就是所謂的目錄項。

對文件系統而言,通常,文件和目錄是按樹狀結構來保存的,目錄項就是反應文件系統的這種樹狀關係,在VFS裏,目錄本身也是一個文件,只是有點特殊。每個文件至少都有一個dentry,這個dentry鏈接到上級目錄的dentry,根目錄有一個dentry結構,而根目錄裏的文件和目錄都鏈接到這個dentry,然後二級目錄裏的文件和目錄,同樣通過dentry鏈接到二級目錄,這樣層層鏈接,就形成了一棵dentry樹,從樹頂可以遍歷整個文件系統的所有目錄和文件。也就是說,因爲這些關係,在Linux環境下你可以從根目錄"/"出發,找到整個文件系統中的任何目錄和文件,從文件你也可以cd .. 返回上層目錄。

上面的數據結構其實很簡單,簡單的說來就是,一個節點裏面含有指向父目錄集和子目錄集的指針。

爲了加快對dentry的查找(如果VFS層遍歷路徑名中所有的元素並將它們逐個解析成目錄項對象,還要到達最深層目錄,將是一個非常費力的工作),內核使用了hash表來緩存dentry,成爲dentry cache。前面的進階篇裏有說到。file結構中的f_dentry指針就是指向dentry cache中的dentry的。

struct dentry {
	atomic_t d_count;
	unsigned int d_flags;		/* protected by d_lock */
	spinlock_t d_lock;		/* per dentry lock */
	struct inode *d_inode;		/* Where the name belongs to - NULL is
					 * negative *///指向一個inode結構,這個inode和dentry共同描述了一個普通文件或者目錄文件
	/*
	 * The next three fields are touched by __d_lookup.  Place them here
	 * so they all fit in a cache line.
	 */
	struct hlist_node d_hash;	/* lookup hash list *///dentry cache的hash鏈表
	struct dentry *d_parent;	/* parent directory *///指向父dentry
	struct qstr d_name;//名字

	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 *///dentry自身的鏈表頭
	 	struct rcu_head d_rcu;
	} d_u;
	struct list_head d_subdirs;	/* our children *///子項(可能是目錄,可能是文件)的鏈表頭
	struct list_head d_alias;	/* inode alias list */
	unsigned long d_time;		/* used by d_revalidate */
	struct dentry_operations *d_op;//操作函數集
	struct super_block *d_sb;	/* The root of the dentry tree */
	void *d_fsdata;			/* fs-specific data */
#ifdef CONFIG_PROFILING
	struct dcookie_struct *d_cookie; /* cookie, if any */
#endif
	int d_mounted;//指示dentry是否是一個掛載點,如果是掛載點,該成員不爲零
	unsigned char d_iname[DNAME_INLINE_LEN_MIN];	/* small names */
};
構建dentry樹,重要的結構自然就是d_subdirs、d_child以及d_parent成員了。

d_subdirs 是子項的鏈表頭,所有的子項都要鏈接到這個鏈表,d_child是dentry自身的鏈表頭,需要鏈接到父dentry的d_subdirs成員。d_parent則是指向父dentry結構的指針。當移動文件的時候,需要把一個dentry結構從舊的父dentry的鏈表上脫離,然後鏈接到新的父dentry的d_subdirs成員,這樣dentry結構之間就構成了一棵目錄樹。

3、索引節點inode

inode代表一個文件,inode保存了文件的大小,創建時間,文件的塊大小等參數,以及對文件的讀寫函數、文件的讀寫緩存等信息。

索引節點對象包含了內核在操作文件或目錄時需要的全部信息。僅當文件被訪問時,纔在內存中創建。

一個真實的文件可以有多個dentry,因爲指向文件的路徑可以有多個(考慮到文件的鏈接),但是inode只有一個。

struct inode {
	struct hlist_node	i_hash;
	struct list_head	i_list;//用於描述inode當前狀態的鏈表
	struct list_head	i_sb_list;//用於鏈接到超級塊中的inode鏈表
	struct list_head	i_dentry;//用於鏈接到dentry鏈表
	unsigned long		i_ino;//inode號
	atomic_t		i_count;//引用計數,遞增遞減均爲原子操作
	umode_t			i_mode;//文件類型
	unsigned int		i_nlink;
	uid_t			i_uid;//用戶。組等等相關的
	gid_t			i_gid;
	dev_t			i_rdev;
	loff_t			i_size;
	struct timespec		i_atime;//三個時間戳
	struct timespec		i_mtime;
	struct timespec		i_ctime;
	unsigned int		i_blkbits;//文件塊的位數
	unsigned long		i_blksize;
	unsigned long		i_version;
	blkcnt_t		i_blocks;
	unsigned short          i_bytes;
	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	struct mutex		i_mutex;
	struct rw_semaphore	i_alloc_sem;
	struct inode_operations	*i_op;//inode操作函數集
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	struct super_block	*i_sb;//鏈接到超級塊
	struct file_lock	*i_flock;
	struct address_space	*i_mapping;//緩存文件的內容,加快文件的讀操作
	struct address_space	i_data;//指向文件數據塊的指針
        ......
};
4、文件對象 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 dentry		*f_dentry;//文件對應的dentry結構
	struct vfsmount         *f_vfsmnt;//文件所屬於的文件系統的vfsmount對象
	const struct file_operations	*f_op;//操作函數集
	atomic_t		f_count;
	unsigned int 		f_flags;
	mode_t			f_mode;
	loff_t			f_pos;//進程對文件操作的位置
	struct fown_struct	f_owner;
	unsigned int		f_uid, f_gid;
	struct file_ra_state	f_ra;//文件預讀位置

	unsigned long		f_version;
	void			*f_security;

	/* 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;
	spinlock_t		f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
	struct address_space	*f_mapping;//文件的讀寫緩存
};

VFS是具體文件系統的抽象,VFS是依靠超級塊、dentry、inode以及文件這些結構來發揮作用。








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章