天天看點

Linux的虛拟檔案系統VFS

沒想到Virtual File System VFS還是比較受歡迎的,是以今天詳細寫一下這一部分。

VFS是一個抽象層,對不同檔案系統的實作屏蔽,對上提供統一的接口。

Linux的虛拟檔案系統VFS
Linux的虛拟檔案系統VFS

這張圖是Linux核心中對于VFS相關資料結構的描述。

每一個程序在核心中,都對應一個task_struct結構

include/linux/sched.h中有

struct task_struct {
    /* Open file information: */
    struct files_struct     *files;           

從注釋就可以看出是對所有打開的檔案的一個結構。

struct files_struct {
       struct file __rcu * fd_array[NR_OPEN_DEFAULT];           

這裡面有一個資料,儲存了打開的所有檔案,每個檔案有一個檔案描述符File Desicriptor FD,其中預設開啟的有stdin, stdout, stderr,分别為0,1,2。

是以在指令行執行一個背景指令的時候常這樣用:

nohup run_command.sh > run.log 2>&1 &           

也即我們把stderr和并到stdout裡面,全部輸出到檔案run.log裡面。

整個系統所有打開的檔案儲存在同一個連結清單中的,當一個檔案被打開多次的時候,f_count記錄被打開的次數,一般的檔案系統不保護檔案被多個程序寫入,需要程序之間通過同步機制做這件事情。一旦一個檔案被多個程序打開,如果在指令行删除檔案之後,檔案可能不可見了,但是不會被删除,已經打開的程序仍然可以讀寫檔案,直到引用為0。

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

其中path為

struct path {
    struct vfsmount *mnt;
    struct dentry *dentry;
};           

這裡dentry稱為directory cache,顧名思義是一個緩存,為了查詢快的,從系統啟動開始,所有被引用過的檔案,都會在這裡緩存一下,在dentry結構裡面有hashlist,可以友善通過檔案或者路徑名進行查找,有lru list,可以不斷的淘汰。

這裡vfsmount,稱為mount list,每個被mount的linux檔案系統,都會對應一項。對于被Mount的檔案系統的跟路徑和mount point的路徑,各對應一個dentry。

Linux的虛拟檔案系統VFS

如圖是dentry和vfsmount的對應關系,對于左上角的圖。

對于作業系統的根路徑/對應一個dentry和一個vfsmount,還有一個file結構指向dentry和vfsmount。

home路徑是一個mount point,因而對應兩個dentry,一個表示上面這個檔案系統的mount point,一個表示下面這個檔案系統的root directory。有一個vfsmount對應于home路徑,parent指向/對應的vfsmount。有一個file結構指向root directory的dentry和vfsmount。

project路徑也是一個mount point,也對應兩個dentry,有一個vfsmount,并且parent指向home對應的vfsmount。有一個file結構指向root directory的dentry和vfsmount。

對于普通的檔案或者路徑data和guide,各有一個dentry對應,各有一個file指向相應的dentry,vfsmount都指向project的vfsmount。

在file這個結構中,最本質的是struct inode *f_inode,了解檔案系統結構的同學知道,每個檔案都有一個inode儲存資訊。

Linux的虛拟檔案系統VFS

如圖所示,檔案系統會有SuperBlock,還有Inode BitMap,通過Inode的一個ID号,可以在Inode Table裡面找到對應的inode。

Linux的虛拟檔案系統VFS

Inode裡面儲存的是這個檔案的資料儲存在了哪些block中。

核心記憶體中的inode是硬碟上inode的一個緩存。

struct inode {
    umode_t         i_mode;
    unsigned short      i_opflags;
    kuid_t          i_uid;
    kgid_t          i_gid;
    unsigned int        i_flags;
    const struct inode_operations   *i_op;
    struct super_block  *i_sb;
    struct address_space    *i_mapping;
    /* Stat data, not accessed from path walking */
    unsigned long       i_ino;
    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;
    const struct file_operations    *i_fop;           

其中inode_operation是對inode可以執行的操作,file_operation是對檔案可以執行的操作,對于不同的檔案系統,這兩個結構是不同的。

對于NFS來講,有下面的檔案系統類型

struct file_system_type nfs_fs_type = {
    .owner      = THIS_MODULE,
    .name       = "nfs",
    .mount      = nfs_fs_mount,
    .kill_sb    = nfs_kill_super,
    .fs_flags   = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
};           

需要注冊檔案系統給VFS

ret = register_filesystem(&nfs_fs_type);

ret = register_nfs4_fs();           

當應用層調用系統調用Mount的時候,會在核心裡面調用

long do_mount(const char *dev_name, const char __user *dir_name,
        const char *type_page, unsigned long flags, void *data_page)           

最終會調用

struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
    struct mount *mnt;
    struct dentry *root;
    mnt = alloc_vfsmnt(name);
    root = mount_fs(type, flags, name, data);
    mnt->mnt.mnt_root = root;
    mnt->mnt.mnt_sb = root->d_sb;
    mnt->mnt_mountpoint = mnt->mnt.mnt_root;
    mnt->mnt_parent = mnt;
    lock_mount_hash();
    list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
    unlock_mount_hash();
    return &mnt->mnt;
}           

其中

struct dentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{
    struct dentry *root;
    struct super_block *sb;
    root = type->mount(type, flags, name, data);           

到這裡會調用NFS這個具體的檔案系統的函數

struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
    int flags, const char *dev_name, void *raw_data)           

裡面重要的兩步如下:

nfs_mod = get_nfs_version(mount_info.parsed->version);
mntroot = nfs_mod->rpc_ops->try_mount(flags, dev_name, &mount_info, nfs_mod);           

已經開始調用rpc層了。

struct dentry *nfs_try_mount(int flags, const char *dev_name,
                 struct nfs_mount_info *mount_info,
                 struct nfs_subversion *nfs_mod)           

會調用

struct nfs_server *nfs3_create_server(struct nfs_mount_info *mount_info,
                      struct nfs_subversion *nfs_mod)
{
    struct nfs_server *server = nfs_create_server(mount_info, nfs_mod);
    /* Create a client RPC handle for the NFS v3 ACL management interface */
    if (!IS_ERR(server))
        nfs_init_server_aclclient(server);
    return server;
}           

最終會建立RPC的Client,進行互相通信。

error = nfs_init_server_rpcclient(server, &timeparms,

data->selected_flavor);

Linux的虛拟檔案系統VFS

是以是符合上述過程的。

RPC層就比較複雜了。