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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章