天天看點

Linux虛拟檔案系統(VFS)學習

  虛拟檔案系統(Virtual Filesystem)也可稱之為虛拟檔案系統轉換(Virtual Filesystem Switch),是一個核心軟體層,用來處理與Unix标準檔案系統相關的全部系統調用。其健壯性表如今能為各種檔案系統提供一個通用的接口。

通用檔案系統模型

  VFS所隐含的主要思想在于引入一個通用的檔案系統模型(common file model),這個模型可以表示全部支援的檔案系統。在通用檔案模型中,每一個檔案夾被看做一個檔案,可以包括若幹檔案和其它的子檔案夾。

通用檔案模型由下列對象類型組成:

超級塊對象(superblock object)

存放已安裝檔案系統的有關資訊(A superblock object represents a mounted filesystem)。對基于磁盤的檔案系統,這類對象通常相應于存放在磁盤上的檔案系統控制塊

索引節點對象(inode object)

存放關于詳細檔案的一般資訊(An inode object represents an object within the filesystem)。對于基于磁盤的檔案系統,這類對象通常相應于存放在磁盤上的檔案控制塊。每一個索引節點對象都有一個索引節點号,這個節點号唯一地辨別檔案系統中的檔案。

檔案對象(file object)

存放打開檔案與程序之間進行互動的有關資訊(A file object represents a file opened by a process)。這類資訊僅當程序訪問檔案期間存在于核心記憶體中。

檔案夾項對象(dentry object)

存放檔案夾項(也就是檔案的特定名稱)與相應檔案進行連結的有關資訊。

VFS的資料結構

這裡僅僅列舉和程序相關的結構

索引節點對象

檔案系統處理檔案所須要的全部資訊都放在一個名為索引節點的資料結構中。檔案名稱能夠随時更改,可是索引節點對檔案是唯一的,而且随着檔案的存在而存在。記憶體中索引節點對象由一個struct inode資料結構構成。

struct inode {
	struct hlist_node	i_hash;     //用于散列連結清單
	struct list_head	i_list;		/* backing dev IO list */
	struct list_head	i_sb_list;  
	struct list_head	i_dentry;   //引用索引節點的檔案夾項對象連結清單頭
	unsigned long		i_ino;      //索引節點号
	atomic_t		i_count;        //引用計數器
	unsigned int		i_nlink;    //硬連結數目
	uid_t			i_uid;          //全部者辨別符
	gid_t			i_gid;          //組辨別符
	dev_t			i_rdev;         //實裝置辨別符
	u64			i_version;          //版本(每次使用後遞增)
	loff_t			i_size;         //檔案的位元組數
#ifdef __NEED_I_SIZE_ORDERED
	seqcount_t		i_size_seqcount;
#endif
	struct timespec		i_atime;    //上次訪問檔案的時間
	struct timespec		i_mtime;    //上次寫檔案的時間
	struct timespec		i_ctime;    //上次改動索引節點的時間
	blkcnt_t		i_blocks;       //檔案的塊數
	unsigned int		i_blkbits;  //塊的位數
	unsigned short          i_bytes;//塊的位元組數
	umode_t			i_mode;         //檔案的類型和訪問權限
	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	struct mutex		i_mutex;
	struct rw_semaphore	i_alloc_sem; //在直接I/O檔案操作中避免出現競争條件的讀寫信号量
	const struct inode_operations	*i_op; //索引節點的操作
	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; //指向address_space對象的指針
	struct address_space	i_data;     //檔案的address_space對象
#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_mark_entries; /* fsnotify mark entries */
#endif

#ifdef CONFIG_INOTIFY
	struct list_head	inotify_watches; /* watches on this inode */
	struct mutex		inotify_mutex;	/* protects the watches list */
#endif

	unsigned long		i_state;  //索引節點狀态标志
	unsigned long		dirtied_when;	/* jiffies of first dirtying */

	unsigned int		i_flags;  //檔案系統安裝标志

	atomic_t		i_writecount;
#ifdef CONFIG_SECURITY
	void			*i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
	struct posix_acl	*i_acl;
	struct posix_acl	*i_default_acl;
#endif
	void			*i_private; /* fs or device private pointer */
};
      

檔案對象

檔案對象描寫叙述程序如何與一個打開的檔案進行互動。檔案對象是在檔案被打開時建立的,由一個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 //與檔案相關的檔案夾項對象
#define f_vfsmnt	f_path.mnt    //含有該檔案的已安裝檔案系統
	const struct file_operations	*f_op; //檔案操作表指針
	spinlock_t		f_lock;  /* f_ep_links, f_flags, no IRQ */
	atomic_long_t		f_count; //檔案對象的引用計數器
	unsigned int 		f_flags; //當打開檔案時所指定的标志
	fmode_t			f_mode;      //程序訪問模式
	loff_t			f_pos;       //目前的檔案偏移量
	struct fown_struct	f_owner; //通過信号進行I/O事件通知的資料
	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;//檔案的事件輪詢等待着連結清單頭
#endif /* #ifdef CONFIG_EPOLL */
	struct address_space	*f_mapping;//指向檔案位址空間對象的指針
#ifdef CONFIG_DEBUG_WRITECOUNT
	unsigned long f_mnt_write_state;
#endif
};
      

檔案對象通過一個名為filp的slab快速緩存配置設定,filp描寫叙述符位址存放在filp_cachep變量中。因為配置設定的檔案對象數目是有限的,是以files_stat變量在其max_files字段中指定了可配置設定的檔案對象的最大數目,也就是系統可同一時候訪問的最大檔案數。

核心初始化期間,files_init()函數把max_files字段設定為可用RAM大小的1/10。隻是,系統管理者能夠通過寫/proc/sys/fs/file-max檔案來改動這個值。并且即使max_files個檔案對象已經被配置設定,超級使用者也總是能夠獲得一個檔案對象

void __init files_init(unsigned long mempages)
{ 
	int n; 

	filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
			SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

	/*
	 * One file with associated inode and dcache is very roughly 1K.
	 * Per default don't use more than 10% of our memory for files. 
	 */ 

	n = (mempages * (PAGE_SIZE / 1024)) / 10;
	files_stat.max_files = n; 
	if (files_stat.max_files < NR_FILE)
		files_stat.max_files = NR_FILE;
	files_defer_init();
	percpu_counter_init(&nr_files, 0);
} 
      

檔案夾項對象

VFS把每一個檔案夾看做若幹子檔案夾和檔案組成的一個普通檔案。一旦檔案夾項被讀入記憶體,VFS就把它轉換成基于dentry結構的一個檔案夾項對象。對于程序查找的路徑名中的每一個分量,核心都為其建立一個檔案夾項對象;檔案夾項對象将每一個分量與其相應的索引節點相聯系。比如在查找路徑名/tmp/test時,核心為根檔案夾“/”建立一個檔案夾項對象,為根檔案夾下的tmp項建立第二級檔案夾項對象,為/tmp檔案夾下的test建立一個第三級檔案夾項對象。

檔案夾項對象在磁盤上并沒有相應的映像,是以在dentry結構中不包括指出該對象已被改動的字段。檔案夾項對象存放在名為dentry_cache的快速緩存中。

struct dentry {
	atomic_t d_count;        //檔案夾項對象引用計數
	unsigned int d_flags;		/* 檔案夾項快速緩存标志protected by d_lock */
	spinlock_t d_lock;		/* per dentry lock */
	int d_mounted;          //對檔案夾而言,用于記錄安裝該檔案夾項的檔案系統計數器
	struct inode *d_inode;		/* 與檔案名稱關聯的索引節點Where the name belongs to - NULL is
					 * negative */
	/*
	 * 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 */
	struct dentry *d_parent;	/* 父檔案夾的檔案夾項對象parent directory */
	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 */
	 	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 */
	const struct dentry_operations *d_op;//檔案夾項方法
	struct super_block *d_sb;	/* 檔案的超級塊對象The root of the dentry tree */
	void *d_fsdata;			/* 依賴于檔案系統的資料fs-specific data */

	unsigned char d_iname[DNAME_INLINE_LEN_MIN];	/* small names */
};
      

與程序相關的檔案

每一個程序都有它自己目前的工作檔案夾和他自己的根檔案夾。這不過核心用來表示程序與檔案系統互相作用所必須維護的資料的兩個樣例。類型為fs_struct的整個資料結構就用于此目的

struct fs_struct {
	int users;
	rwlock_t lock;
	int umask;    //當打開檔案設定檔案權限是所用的位掩碼
	int in_exec;
	struct path root, pwd; /* 根檔案夾的檔案夾項,根檔案夾所安裝的檔案系統對象 
                              目前工作的檔案夾項,目前工作檔案夾所安裝的檔案系統對象*/
};      

程序目前打開的檔案與files_struct結構有關

struct fdtable {
	unsigned int max_fds;  //檔案對象的目前最大數目
	struct file ** fd;      /* current fd array */
	fd_set *close_on_exec;
	fd_set *open_fds;
	struct rcu_head rcu;
	struct fdtable *next;
};

/*
 * Open file table structure
 */
struct files_struct {
  /*
   * read mostly part
   */
	atomic_t count;     //共享程序的數目
	struct fdtable *fdt; //
	struct fdtable fdtab;
  /*
   * written part on a separate cache line in SMP
   */
	spinlock_t file_lock ____cacheline_aligned_in_smp;
	int next_fd;
	struct embedded_fd_set close_on_exec_init;
	struct embedded_fd_set open_fds_init;
	struct file * fd_array[NR_OPEN_DEFAULT];//檔案對象指針的初始化數組
};      

fd字段指向檔案對象的指針數組。該數組的長度存放在max_fds字段中。通常,fd字段指向files_struct結構中的fd_array字段,該字段包含32個檔案對象指針。假設程序打開的檔案資料多于32,核心就配置設定一個新的、更大的檔案指針數組,并将其位址存放在fd字段中,核心同一時候更新max_fds字段的值。

相應fd數組中有元素的每一個檔案來說,數組的索引就是檔案描寫叙述符。通常,數組的第一個元素(索引0)是程序的标準輸入檔案,數組的第二個元素(索引1)是程序的标準輸出檔案,數組的第三個元素(索引2)是程序的标準錯誤輸出檔案。

核心在程序描寫叙述符的signal->rlim[RLIM_NLIMITS]結構上強制動态限制檔案描寫叙述符的最大數;這個值通常為1024,但假設程序具有超級權限,就能夠增大這個值。

最經常使用的特殊檔案系統類型

名字           安裝點     說明

bdev           無        塊裝置

binfmt_misc    随意      其它可運作格式

devpts         /dev/pts  僞終端支援

eventpollfs    無        由有效事件輪詢機制使用

futexfs        無        由futex(高速使用者态加鎖)機制使用

pipefs         無        管道

proc           /proc     對核心資料結構的正常訪問點

rootfs         無        為啟動階段提供一個空的根檔案夾

shm            無        IPC共享線性區

mqueue         随意       實作POSIX消息隊列時使用

sockfs         無         套接字

sysfs          /sys       對系統參數的正常訪問

tmpfs          随意        暫時檔案(假設不被交換出去就保持在RAM中)

usbfs          /proc/bus/usb USB裝置

檔案系統注冊

每一個注冊的檔案系統都用一個類型為file_system_type的對象來表示

struct file_system_type {
	const char *name;  //檔案系統名
	int fs_flags;      //檔案系統标志
	int (*get_sb) (struct file_system_type *, int,
		       const char *, void *, struct vfsmount *); //讀取超級塊的方法
	void (*kill_sb) (struct super_block *); //删除超級塊的方法
	struct module *owner;    //指向實作檔案系統的子產品的指針
	struct file_system_type * next;//指向檔案系統連結清單中下一個元素的指針
	struct list_head fs_supers; //具有同樣檔案系統類型的超級塊對象連結清單頭

	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;
};
      

以sockfs檔案系統注冊為例

static struct file_system_type sock_fs_type = {
	.name =		"sockfs",
	.get_sb =	sockfs_get_sb,
	.kill_sb =	kill_anon_super,
};

static int __init sock_init(void)
{
	/*
	 *      Initialize sock SLAB cache.
	 */

	sk_init();

	/*
	 *      Initialize skbuff SLAB cache
	 */
	skb_init();

	/*
	 *      Initialize the protocols module.
	 */

	init_inodecache();
    /* 注冊sockfs檔案系統 */
	register_filesystem(&sock_fs_type);
	sock_mnt = kern_mount(&sock_fs_type);

	/* The real protocol initialization is performed in later initcalls.
	 */

#ifdef CONFIG_NETFILTER
	netfilter_init();
#endif

	return 0;
}