1. 程式人生 > >Linux最簡單的檔案系統aufs剖析詳解

Linux最簡單的檔案系統aufs剖析詳解

對於一個普通的程式設計小白來說,檔案系統無非就是幾個功能,建立檔案,建立目錄,開啟檔案和檔案讀寫。對於通常的硬碟檔案系統來說,這要涉及硬碟的讀寫和硬碟空間管理,而讀寫從檔案系統一直到通用塊裝置再到硬碟驅動。我們這些就剖析最簡單的檔案系統,深入核心。
aufs檔案系統原始碼

#include <linux/module.h> 
#include <linux/fs.h> 
#include <linux/pagemap.h> 
#include <linux/mount.h> 
#include <linux/init.h> 
#include <linux/namei.h> 
#define AUFS_MAGIC 0x64668735 static struct vfsmount *aufs_mount; static int aufs_mount_count; static struct inode *aufs_get_inode(struct super_block *sb, int mode, dev_t dev) { struct inode *inode = new_inode(sb); if (inode) { inode->i_mode = mode; inode->i_uid = current->fsuid; inode->i_gid = current->fsgid; inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0
; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; switch (mode & S_IFMT) { default: init_special_inode(inode, mode, dev); break; case S_IFREG: printk("creat a file \n"); break; case
S_IFDIR: inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; printk("creat a dir file \n"); inode->i_nlink++; break; } } return inode; } /* SMP-safe 建立iNode的函式 */ static int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode *inode; int error = -EPERM; /* 判斷inode是否存在 如果存在就返回退出函式 */ if (dentry->d_inode) return -EEXIST; /* 如果inode不存在就呼叫aufs_get_inode函式建立inode */ inode = aufs_get_inode(dir->i_sb, mode, dev); if (inode) { /* */ d_instantiate(dentry, inode); dget(dentry); error = 0; } return error; } static int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int res; /* 引數S_IFDIR表示建立一個目錄檔案的inode */ res = aufs_mknod(dir, dentry, mode |S_IFDIR, 0); if (!res) dir->i_nlink++; return res; } static int aufs_create(struct inode *dir, struct dentry *dentry, int mode) { return aufs_mknod(dir, dentry, mode | S_IFREG, 0); } static int aufs_fill_super(struct super_block *sb, void *data, int silent) { static struct tree_descr debug_files[] = {{""}}; return simple_fill_super(sb, AUFS_MAGIC, debug_files); } static struct super_block *aufs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_single(fs_type, flags, data, aufs_fill_super); } static struct file_system_type au_fs_type = { .owner = THIS_MODULE, .name = "aufs", .get_sb = aufs_get_sb, .kill_sb = kill_litter_super, }; /* 建立檔案的inode和dentry結構 */ static int aufs_create_by_name(const char *name, mode_t mode, struct dentry *parent, struct dentry **dentry) { int error = 0; /* If the parent is not specified, we create it in the root. * We need the root dentry to do this, which is in the super * block. A pointer to that is in the struct vfsmount that we * have around. */ /* 判斷是否有父目錄 沒有就賦予檔案系統的根dentry */ if (!parent ) { if (aufs_mount && aufs_mount->mnt_sb) { parent = aufs_mount->mnt_sb->s_root; } } if (!parent) { printk("Ah! can not find a parent!\n"); return -EFAULT; } *dentry = NULL; /* 原子鎖 */ mutex_lock(&parent->d_inode->i_mutex); /* 呼叫lookup_one_len:首先在父目錄下根據名字查詢dentry結構 如果存在就返回指標 不存在就建立一個dentry */ *dentry = lookup_one_len(name, parent, strlen(name)); if (!IS_ERR(dentry)) { if ((mode & S_IFMT) == S_IFDIR) /* 這裡表示建立一個目錄檔案的inode */ error = aufs_mkdir(parent->d_inode, *dentry, mode); else /* 建立一個檔案的inode */ error = aufs_create(parent->d_inode, *dentry, mode); } else error = PTR_ERR(dentry); mutex_unlock(&parent->d_inode->i_mutex); return error; } /* 建立一個檔案 檔案是用dentry和inode代表的 這裡是建立dentry和inode */ struct dentry *aufs_create_file(const char *name, mode_t mode, struct dentry *parent, void *data, struct file_operations *fops) { struct dentry *dentry = NULL; int error; printk("aufs: creating file '%s'\n",name); error = aufs_create_by_name(name, mode, parent, &dentry); if (error) { dentry = NULL; goto exit; } if (dentry->d_inode) { if (data) dentry->d_inode->u.generic_ip = data; if (fops) dentry->d_inode->i_fop = fops; } exit: return dentry; } /* 目錄建立 linux中目錄也是檔案 所以呼叫aufs_create_file建立檔案 傳入引數S_IFDIR指明建立的是一個目錄 */ struct dentry *aufs_create_dir(const char *name, struct dentry *parent) { return aufs_create_file(name, S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, parent, NULL, NULL); } static int __init aufs_init(void) { int retval; struct dentry *pslot; /* 將檔案系統登記到系統 */ retval = register_filesystem(&au_fs_type); if (!retval) { /* 建立super_block 根dentry 根inode */ aufs_mount = kern_mount(&au_fs_type); /* kern_mount錯誤就解除安裝檔案系統 */ if (IS_ERR(aufs_mount)) { printk(KERN_ERR "aufs: could not mount!\n"); unregister_filesystem(&au_fs_type); return retval; } } /* 建立目錄和目錄下的幾個檔案 */ pslot = aufs_create_dir("woman star",NULL); aufs_create_file("lbb", S_IFREG | S_IRUGO, pslot, NULL, NULL); aufs_create_file("fbb", S_IFREG | S_IRUGO, pslot, NULL, NULL); aufs_create_file("ljl", S_IFREG | S_IRUGO, pslot, NULL, NULL); pslot = aufs_create_dir("man star",NULL); aufs_create_file("ldh", S_IFREG | S_IRUGO, pslot, NULL, NULL); aufs_create_file("lcw", S_IFREG | S_IRUGO, pslot, NULL, NULL); aufs_create_file("jw", S_IFREG | S_IRUGO, pslot, NULL, NULL); return retval; } static void __exit aufs_exit(void) { /* 退出函式中解除安裝super_block 根dentry 根inode */ simple_release_fs(&aufs_mount, &aufs_mount_count); /* 解除安裝檔案系統 */ unregister_filesystem(&au_fs_type); } /* 模組入口出口函式宣告 和模組宣告 */ module_init(aufs_init); module_exit(aufs_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("This is a simple module"); MODULE_VERSION("Ver 0.1");

對於沒了解過linux模組概念的話 這裡可以稍微講解 這裡可以用百度的官方解釋
Linux核心模組:儘管Linux是”單塊核心“(monolithic)的作業系統–這是說整個系統核心都運行於一個單獨的保護域中,但是linux核心是模組化組成的,它允許核心在執行時動態地向其中插入或從中刪除程式碼。這些程式碼(包括相關的子執行緒、資料、函式入口和函數出口)被一併組合在一個單獨的二進位制映象中,即所謂的可裝載核心模組中,或簡稱為模組。支援模組的好處是基本核心映象儘可能的小,因為可選的功能和驅動程式可以利用模組形式再提供。模組允許我們方便地刪除和重新載入核心程式碼,也方便了除錯工作。而且當熱插拔新裝置時,可通過命令載入新的驅動程式。
上文解釋的比較全面,簡單而言就是我們可以通過insmod命令向核心新增模組 和使用rmmod刪除模組

所以aufs也是用這種方法向核心insmod這個檔案系統
Linux模組中有兩個關鍵的函式 就是入口和出口函式
對於分析一個驅動程式碼或者其他模組 我們不是一上來就是愣頭青的從頭分析程式碼 而是從模組的入口函式看起

/* 入口函式 */
static int __init aufs_init(void)  
{  
  int retval;  
       struct dentry *pslot;  

 /* 將檔案系統登記到系統 */
  retval = register_filesystem(&au_fs_type);  

  if (!retval) {  
/* 建立super_block 根dentry 根inode */
       aufs_mount = kern_mount(&au_fs_type);
/* kern_mount錯誤就解除安裝檔案系統 */
       if (IS_ERR(aufs_mount)) {  
           printk(KERN_ERR "aufs: could not mount!\n");  
           unregister_filesystem(&au_fs_type);  
           return retval;  
       }  
  }  
/* 建立目錄和目錄下的幾個檔案 */
  pslot = aufs_create_dir("woman star",NULL);  
  aufs_create_file("lbb", S_IFREG | S_IRUGO, pslot, NULL, NULL);  
  aufs_create_file("fbb", S_IFREG | S_IRUGO, pslot, NULL, NULL);  
  aufs_create_file("ljl", S_IFREG | S_IRUGO, pslot, NULL, NULL);  

  pslot = aufs_create_dir("man star",NULL);  
  aufs_create_file("ldh", S_IFREG | S_IRUGO, pslot, NULL, NULL);  
  aufs_create_file("lcw", S_IFREG | S_IRUGO, pslot, NULL, NULL);  
  aufs_create_file("jw", S_IFREG | S_IRUGO, pslot, NULL, NULL);  

  return retval;  
}  
static void __exit aufs_exit(void)  
{  
/* 退出函式中解除安裝super_block 根dentry 根inode */
  simple_release_fs(&aufs_mount, &aufs_mount_count);  
  /* 解除安裝檔案系統 */
  unregister_filesystem(&au_fs_type);  
}  
 /* 模組入口出口函式宣告 和模組Licence 描述 版本說明 */
module_init(aufs_init);  
module_exit(aufs_exit);  
MODULE_LICENSE("GPL");
/* 以上三個宣告為必須 下面兩個宣告可有可無 */  
MODULE_DESCRIPTION("This is a simple module");  
MODULE_VERSION("Ver 0.1"); 

分析aufs的入口函式
從程式碼可知 入口函式就主要就呼叫了register_filesystemkern_mount函式進行檔案系統的建立 然後就是用函式aufs_create_diraufs_create_file建立目錄和檔案 入口函式就幹了這幾件事 那我們來分析這兩個函式 看看核心是怎麼建立一個檔案系統的
register_filesystem函式實現程式碼

int register_filesystem(struct file_system_type * fs)
{
    int res = 0;
    struct file_system_type ** p;

    BUG_ON(strchr(fs->name, '.'));
    /* 檢查檔案系統的連結串列節點是否存在 */
    if (fs->next)
        return -EBUSY;
    INIT_LIST_HEAD(&fs->fs_supers);
    write_lock(&file_systems_lock);
    /* 查詢同名的檔案系統 */
    p = find_filesystem(fs->name, strlen(fs->name));
    if (*p)
    /* 存在則返回忙 */
        res = -EBUSY;
    else
    /* 不存在則加入檔案系統連結串列 */
        *p = fs;
    write_unlock(&file_systems_lock);
    return res;
}

這個檔案系統程式碼中先定義了file_system_type結構體 這個結構體是用來描述檔案系統的 結構體程式碼如下
在這個檔案系統中 我們只用到了其中的四個變數 所以我們只對著四個變數分析

struct file_system_type {
    /* 檔案系統名字 */
    const char *name;
    int fs_flags;
    /* 獲得超級塊superblock的函式 */
    int (*get_sb) (struct file_system_type *, int,
               const char *, void *, struct vfsmount *);
    /* 銷燬超級塊superblock的函式 */
    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;
    }

register_filesystem的函式引數為struct file_system_type * fs 意即傳入一個file_system_type結構體指標 而這個結構體包含有這個檔案系統的一些資訊 比如獲取超級塊的函式,名字之類。
register_filesystem先檢查檔案系統的連結串列節點是否存在 然後尋找相同名字的檔案系統 如果不存在相
同名字的檔案系統,就把檔案系統加入到系統的檔案系統連結串列。如果已存在則返回忙 核心定義
了一個全域性變數file_systems,用來儲存所有登記的檔案系統,find_filesystem就是利用了這個
全域性變數file_systems執行了具體的查詢過程
從register_filesystem的程式碼得知 register_filesystem函式並未建立超級塊物件和vfsmount物件 而只是將檔案系統登記入系統 所以主要的功能還是要在kern_mount裡實現
kern_mount 函式實現程式碼

struct vfsmount *kern_mount(struct file_system_type *type)
{
    return vfs_kern_mount(type, 0, type->name, NULL);
}

可以看出kern_mount只是vfs_kern_mount的封裝
vfs_kern_mount 函式實現程式碼

struct vfsmount *vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
    struct vfsmount *mnt;
    char *secdata = NULL;
    int error;
    /* type不存在 就返回錯誤 */
    if (!type)
        return ERR_PTR(-ENODEV);

    error = -ENOMEM;

    /* 根據檔案系統的名字為檔案系統建立一個vfsmount結構 */
    mnt = alloc_vfsmnt(name);
    /* mount有資料 則繼續實行 否則goto退出 */
    if (!mnt)
        goto out;
    /* 傳進來的data為NULL 不執行 */
    if (data) {
        secdata = alloc_secdata();
        if (!secdata)
            goto out_mnt;
    /* 安全相關程式碼 */
        error = security_sb_copy_data(type, data, secdata);
        if (error)
            goto out_free_secdata;
    }

    /* 呼叫file_system_type結構體裡的get_sb建立一個超級塊物件super_block 
     * 並且建立一個dentey結構作為檔案系統的根dentry和一個inode作為檔案系統的根inode
     */
    error = type->get_sb(type, flags, name, data, mnt);
    if (error < 0)
        goto out_free_secdata;

    /* 安全相關程式碼 */
    error = security_sb_kern_mount(mnt->mnt_sb, secdata);
    if (error)
        goto out_sb;

    /* mnt資料在申請的時候被賦予空值 */
    /* mnt_mountpoint為根dentry */
    mnt->mnt_mountpoint = mnt->mnt_root;

    /* vfsmount結構的父指標為本身 */
    /* 如果把檔案系統mount到其他系統 就得把這兩個引數設定為原始檔系統 */
    mnt->mnt_parent = mnt;
    up_write(&mnt->mnt_sb->s_umount);
    free_secdata(secdata);
    return mnt;
out_sb:
    dput(mnt->mnt_root);
    up_write(&mnt->mnt_sb->s_umount);
    deactivate_super(mnt->mnt_sb);
out_free_secdata:
    free_secdata(secdata);
out_mnt:
    free_vfsmnt(mnt);
out:
    return ERR_PTR(error);
}

根據以上程式碼可知vfs_kern_mount呼叫了alloc_vfsmnt 函式和呼叫file_system_type結構體裡的get_sb建立一個超級塊物件super_block 並且建立一個dentey結構作為檔案系統的根dentry和一個inode作為檔案系統的根inode
這裡使用的是函式aufs_get_sb來獲取超級塊物件
aufs_get_sb函式實現程式碼如下

static struct super_block *aufs_get_sb(struct file_system_type *fs_type,  
                int flags, const char *dev_name,  
                void *data)  
{  
  return get_sb_single(fs_type, flags, data, aufs_fill_super);  
}  

aufs_get_sb是get_sb_single的封裝函式

int get_sb_single(struct file_system_type *fs_type,
    int flags, void *data,
    int (*fill_super)(struct super_block *, void *, int),
    struct vfsmount *mnt)
{
    struct super_block *s;
    int error;

    /* 獲取一個超級塊物件 */
    s = sget(fs_type, compare_single, set_anon_super, NULL);

    /* 如果檔案系統的超級塊物件已經存在 返回物件指標 */
    if (IS_ERR(s))
        return PTR_ERR(s);

    /* 如果超級塊物件的根dentry不存在 則呼叫穿進來的函式指標fill_super為超級塊物件賦值給根dentry和根inode */   
    if (!s->s_root) {
        s->s_flags = flags;
        error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
        if (error) {
            up_write(&s->s_umount);
            deactivate_super(s);
            return error;
        }
        s->s_flags |= MS_ACTIVE;
    }
    do_remount_sb(s, flags, data, 0);
    /* 把建立的超級塊物件賦值給vfsmount結構所指的超級塊同時vfsmount所指的mnt_root點賦值給超級塊所指的根dentry
     *於是從vfsmount結構就可以獲得檔案系統的超級塊物件和根dentry 
     */
    return simple_set_mnt(mnt, s);
}

從程式碼可知get_sb_single函式使用sget函式來獲得一個超級塊物件 並且呼叫fill_super為超級塊物件填充根dentry和根inode。
在這aufs檔案系統裡 fill_super就是aufs_fill_super函式
aufs_fill_super函式實現程式碼如下

static int aufs_fill_super(struct super_block *sb, void *data, int silent)  
{  
/* 定義空的tree_descr結構體 作用是描述一些檔案 如果不為空,填充超級塊的同時需要在根目錄下建立一些檔案,當前為空,說明不需要建立任何檔案 */
  static struct tree_descr debug_files[] = {{""}};  
 /* 呼叫simple_fill_super函式填充根dentry和根inode */
  return simple_fill_super(sb, AUFS_MAGIC, debug_files);  
}  

simple_fill_super函式實現程式碼如下

int simple_fill_super(struct super_block *s, int magic, struct tree_descr *files)
{
    static const struct super_operations simple_super_operations ={.statfs  = simple_statfs,};
    struct inode *inode;
    struct dentry *root;
    struct dentry *dentry;
    int i;
    /* 對超級塊物件物件賦值 */
    /* 檔案系統塊大小 */
    s->s_blocksize = PAGE_CACHE_SIZE;
    /* 塊大小的位 */
    s->s_blocksize_bits = PAGE_CACHE_SHIFT;
    s->s_magic = magic;
    /* 為超級物件賦予操作函式simple_statfs  */
    s->s_op = &simple_super_operations;
    s->s_time_gran = 1;

    /* 建立一個根inode結構 */
    inode = new_inode(s);
    if (!inode)
        return -ENOMEM;
    /*
     * because the root inode is 1, the files array must not contain an
     * entry at index 1
     */
     /* inode結構體賦值 */
    inode->i_ino = 1;
    /* 設定檔案的型別和訪問許可權 */
    inode->i_mode = S_IFDIR | 0755;
    /* 設定組id和使用者id */
    inode->i_uid = inode->i_gid = 0;
    /* 設定檔案所佔塊數 */
    inode->i_blocks = 0;
    /* 設定檔案的修改訪問之間之類 */
    inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
    /* 設定操作函式 */
    inode->i_op = &simple_dir_inode_operations;
    inode->i_fop = &simple_dir_operations;
    inode->i_nlink = 2;

    /* 建立一個根dentry 根dentry的父結構是自身,它指向的超級塊物件就是檔案系統的超級塊物件  */
    root = d_alloc_root(inode);
    if (!root) {
        iput(inode);
        return -ENOMEM;
    }

    /* 建立一系列的檔案 並且設定inode */
    for (i = 0; !files->name || files->name[0]; i++, files++) {
        if (!files->name)
            continue;

        /* warn if it tries to conflict with the root inode */
        if (unlikely(i == 1))
            printk(KERN_WARNING "%s: %s passed in a files array"
                "with an index of 1!\n", __func__,
                s->s_type->name);

        dentry = d_alloc_name(root, files->name);
        if (!dentry)
            goto out;
        inode = new_inode(s);
        if (!inode)
            goto out;
        inode->i_mode = S_IFREG | files->mode;
        inode->i_uid = inode->i_gid = 0;
        inode->i_blocks = 0;
        inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
        inode->i_fop = files->ops;
        inode->i_ino = i;
        d_add(dentry, inode);
    }
    s->s_root = root;
    return 0;
out:
    d_genocide(root);
    dput(root);
    return -ENOMEM;
}

從上程式碼可知 建立了根dentry和根inode

所以register_filesystem和kern_mount所做的事就是將檔案系統登記到系統並且建立super_block 根dentry 根inode。那麼我們後面建立的檔案和目錄都應該連結到這個根dentry

然後就是aufs建立目錄和檔案的過程
aufs_create_dir函式實現程式碼如下

 /* 目錄建立 linux中目錄也是檔案 所以呼叫aufs_create_file建立檔案 傳入引數S_IFDIR指明建立的是一個目錄 */
struct dentry *aufs_create_dir(const char *name, struct dentry *parent)  
{  
  return aufs_create_file(name,  
                  S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,  
                  parent, NULL, NULL);  
}  

aufs_create_file函式實現程式碼如下

/* 建立一個檔案 檔案是用dentry和inode代表的 這裡是建立dentry和inode */
struct dentry *aufs_create_file(const char *name, mode_t mode,  
                  struct dentry *parent, void *data,  
                  struct file_operations *fops)  
{  
  struct dentry *dentry = NULL;  
  int error;  

  printk("aufs: creating file '%s'\n",name);  

  error = aufs_create_by_name(name, mode, parent, &dentry);  
  if (error) {  
       dentry = NULL;  
       goto exit;  
  }  
  if (dentry->d_inode) {  
       if (data)  
            dentry->d_inode->u.generic_ip = data;  
       if (fops)  
            dentry->d_inode->i_fop = fops;  
  }  
exit:  
  return dentry;  
}  

上述程式碼主要就是呼叫了aufs_create_by_name建立檔案的dentry和inode結構
aufs_create_by_name函式實現程式碼如下

 /* 建立檔案的inode和dentry結構 */
static int aufs_create_by_name(const char *name, mode_t mode,  
                 struct dentry *parent,  
                 struct dentry **dentry)  
{  
  int error = 0;  

  /* If the parent is not specified, we create it in the root.  
   * We need the root dentry to do this, which is in the super  
   * block. A pointer to that is in the struct vfsmount that we  
   * have around.  
   */  
   /* 判斷是否有父目錄 沒有就賦予檔案系統的根dentry */
  if (!parent ) {  
       if (aufs_mount && aufs_mount->mnt_sb) {  
           parent = aufs_mount->mnt_sb->s_root;  
       }  
  }  
  if (!parent) {  
       printk("Ah! can not find a parent!\n");  
       return -EFAULT;  
  }  

  *dentry = NULL;  
  /* 原子鎖  */
  mutex_lock(&parent->d_inode->i_mutex);  

  /* 呼叫lookup_one_len:首先在父目錄下根據名字查詢dentry結構 如果存在就返回指標 不存在就建立一個dentry */
  *dentry = lookup_one_len(name, parent, strlen(name));  
  if (!IS_ERR(dentry)) {  
       if ((mode & S_IFMT) == S_IFDIR)  
        /* 這裡表示建立一個目錄檔案的inode */
            error = aufs_mkdir(parent->d_inode, *dentry, mode);  
       else  
       /* 建立一個檔案的inode */
            error = aufs_create(parent->d_inode, *dentry, mode);  
  } else  
       error = PTR_ERR(dentry);  
  mutex_unlock(&parent->d_inode->i_mutex);  

  return error;  
}  

aufs_create_by_name函式的主要功能就是呼叫lookup_one_len函式根據名字查詢dentry結構 存在就返回 不存在就建立一個 反正最後都是要得到一個dentry結構,和呼叫aufs_mkdir或者aufs_create建立inode結構

lookup_one_len函式實現程式碼如下

struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
{
    int err;
    struct qstr this;

    err = __lookup_one_len(name, &this, base, len);
    if (err)
        return ERR_PTR(err);
    /* 通過hash值查詢同名字的dentry */
    return __lookup_hash(&this, base, NULL);
}

__lookup_one_len函式實現程式碼如下

static inline int __lookup_one_len(const char *name, struct qstr *this, struct dentry *base, int len)
{
    unsigned long hash;
    unsigned int c;

    this->name = name;
    this->len = len;
    if (!len)
        return -EACCES;

    /* 根據名字計算hash值,看看是怎麼計算的  */
    hash = init_name_hash();
    while (len--) {
        c = *(const unsigned char *)name++;
        if (c == '/' || c == '\0')
            return -EACCES;
        hash = partial_name_hash(c, hash);
    }
    this->hash = end_name_hash(hash);
    return 0;
}

瀏覽上面兩個函式的程式碼可知 查詢同名的dentry就是計算dentry名字的hash值 然後呼叫__lookup_hash來根據上述算出的hash值來找到同名的dentry結構

__lookup_hash函式實現程式碼如下

static inline struct dentry * __lookup_hash(struct qstr *name, struct dentry *base, struct nameidata *nd)
{
    struct dentry *dentry;
    struct inode *inode;
    int err;

    inode = base->d_inode;
    /* 檢查inode的許可權 */
    err = permission(inode, MAY_EXEC, nd);
    dentry = ERR_PTR(err);
    if (err)
        goto out;

    dentry = __lookup_hash_kern(name, base, nd);
out:
    return dentry;
}

__lookup_hash是先檢查inode的許可權 然後呼叫__lookup_hash_kern函式來根據hash值獲得同名的dentry結構
__lookup_hash_kern函式實現程式碼如下

static inline struct dentry *__lookup_hash_kern(struct qstr *name, struct dentry *base, struct nameidata *nd)
{
    struct dentry *dentry;
    struct inode *inode;
    int err;

    inode = base->d_inode;

    /*
     * See if the low-level filesystem might want
     * to use its own hash..
     */
     /* 查詢檔案系統是否提供了特有的hash函式d_hash 如果有就呼叫重新計算hash值 */
    if (base->d_op && base->d_op->d_hash) {
        err = base->d_op->d_hash(base, name);
        dentry = ERR_PTR(err);
        if (err < 0)
            goto out;
    }
    /* 在dentry cache裡查詢同名的dentry結構 如果返回為空 說明不存在同名的dentry結構 就呼叫d_alloc建立dentry結構 */
    dentry = cached_lookup(base, name, nd);
    if (!dentry) {
        struct dentry *new = d_alloc(base, name);
        dentry = ERR_PTR(-ENOMEM);
        if (!new)
            goto out;
    /* 建立dentry結構完成後 得再次呼叫檔案系統的lookup查詢是否有同名的dentry存在,防止同名的dentry已經被其他使用者提前建立了 */
        dentry = inode->i_op->lookup(inode, new, nd);
        if (!dentry)
            dentry = new;
        else
            dput(new);
    }
out:
    return dentry;
}

cached_lookup函式實現程式碼如下

/* 使用了兩次lookup 防止併發的d_move操作 */
static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
{
    struct dentry * dentry = __d_lookup(parent, name);

    /* lockess __d_lookup may fail due to concurrent d_move() 
     * in some unrelated directory, so try with d_lookup
     */
    if (!dentry)
        dentry = d_lookup(parent, name);

    if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
        dentry = do_revalidate(dentry, nd);

    return dentry;
}

__d_lookup 函式程式碼如下

struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
{
    unsigned int len = name->len;
    unsigned int hash = name->hash;
    const unsigned char *str = name->name;
    /* 根據parent和hash值計算最終的hash值 從陣列dentry_hashtable中獲得hash連結串列的連結串列頭 
     * 核心中的dentry結構都根據hash值連結到眾多的hash連結串列中,這些hash連結串列的頭結構儲存在dentry_hashtable中
     */
    struct hlist_head *head = d_hash(parent,hash);
    struct dentry *found = NULL;
    struct hlist_node *node;
    struct dentry *dentry;

    rcu_read_lock();

    /* 獲得連結串列頭後 遍歷hash連結串列 尋找匹配的dentry結構 */
    hlist_for_each_entry_rcu(dentry, node, head, d_hash) {
        struct qstr *qstr;

        if (dentry->d_name.hash != hash)
            continue;
        if (dentry->d_parent != parent)
            continue;

        spin_lock(&dentry->d_lock);

        /*
         * Recheck the dentry after taking the lock - d_move may have
         * changed things.  Don't bother checking the hash because we're
         * about to compare the whole name anyway.
         */
        if (dentry->d_parent != parent)
            goto next;

        /*
         * It is safe to compare names since d_move() cannot
         * change the qstr (protected by d_lock).
         */
        qstr = &dentry->d_name;
        /* 如果檔案系統定義了d_compare就呼叫  */
        if (parent->d_op && parent->d_op->d_compare) {
            if (parent->d_op->d_compare(parent, qstr, name))
                goto next;
        } else {
            /* 判斷長度是否相同 */
            if (qstr->len != len)
                goto next;
            /* 比較目錄名字是否相同 */
            if (memcmp(qstr->name, str, len))
                goto next;
        }

        if (!d_unhashed(dentry)) {
            atomic_inc(&dentry->d_count);
            found = dentry;
        }
        spin_unlock(&dentry->d_lock);
        break;
next:
        spin_unlock(&dentry->d_lock);
    }
    rcu_read_unlock();

    return found;
}

兜了一整圈就是
aufs_create_dir->aufs_create_file->aufs_create_by_name:查詢或者建立dentry和inode結構->lookup_one_len:計算hash值->__lookup_one_len**dentry->**__lookup_one_len:根據hash值查詢->__lookup_hash->__lookup_hash_kern:根據hash值查詢 知道同名就返回 找不到就建立

上面分析了尋找dentry結構相關程式碼 下面就解析下建立inode的程式碼
目錄inode建立函式
aufs_mkdir函式實現程式碼如下

static int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)  
{  
  int res;  
 /* 引數S_IFDIR表示建立一個目錄檔案的inode */
  res = aufs_mknod(dir, dentry, mode |S_IFDIR, 0);  
  if (!res)  
       dir->i_nlink++;  
  return res;  
}  

aufs_mknod函式實現程式碼如下

/* SMP-safe  建立iNode的函式 */  
static int aufs_mknod(struct inode *dir, struct dentry *dentry,  
           int mode, dev_t dev)  
{  
  struct inode *inode;  
  int error = -EPERM;  
 /* 判斷inode是否存在 如果存在就返回退出函式 */
  if (dentry->d_inode)  
       return -EEXIST;  
 /* 如果inode不存在就呼叫aufs_get_inode函式建立inode */
  inode = aufs_get_inode(dir->i_sb, mode, dev);  
  if (inode) {  
  /* 把dentry加入到inode的dentry連結串列頭 */
       d_instantiate(dentry, inode);  
       dget(dentry);  
       error = 0;  
  }  
  return error;  
}  

aufs_get_inode函式實現程式碼如下

static struct inode *aufs_get_inode(struct super_block *sb, int mode, dev_t dev)  
{  
/* 申請一個inode結構 */
  struct inode *inode = new_inode(sb);  
 /* inode結構體成員的賦值 */
 /* current是核心定義的一個巨集,它的作用是獲得當前程序的結構指標 */
  if (inode) {  
       inode->i_mode = mode;  
       inode->i_uid = current->fsuid;  
       inode->i_gid = current->fsgid;  
       inode->i_blksize = PAGE_CACHE_SIZE;  
       inode->i_blocks = 0;  
       inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;  
       /* 根據inode的型別 設定不同的操作函式 */
       switch (mode & S_IFMT) {  
       default:  
           /* 塊裝置檔案或者字元裝置檔案 呼叫init_special_inode賦值 */
           init_special_inode(inode, mode, dev);  
           break;  
       case S_IFREG:  
           printk("creat a  file \n");  
           break;  
       case S_IFDIR:  
           /* 目錄檔案 設定i_op和i_fop */
           inode->i_op = &simple_dir_inode_operations;  
           inode->i_fop = &simple_dir_operations;  
           printk("creat a dir file \n");  

           inode->i_nlink++;  
           break;  
       }  
  }  
  return inode;  
}  

綜上分析 整個aufs檔案系統的程式碼已經為每個檔案和目錄建立了dentry結構,同時為每個檔案和目錄建立了inode結構。此時已經形成了一顆dentry樹,不過還不能訪問這顆樹,要真正用起來還需要掛載到根檔案系統。

相關推薦

Linux簡單檔案系統aufs剖析

對於一個普通的程式設計小白來說,檔案系統無非就是幾個功能,建立檔案,建立目錄,開啟檔案和檔案讀寫。對於通常的硬碟檔案系統來說,這要涉及硬碟的讀寫和硬碟空間管理,而讀寫從檔案系統一直到通用塊裝置再到硬碟驅動。我們這些就剖析最簡單的檔案系統,深入核心。 aufs檔

Linux核心與根檔案系統的關係

Linux核心與根檔案系統的關係 開篇題外話:對於Linux初學者來說,這是一個很糾結的問題,但這也是一個很關鍵的問題! 一語破天機: “儘管核心是 Linux 的核心,但檔案卻是使用者與作業系統互動所採用的主要工具。這對 Linux 來說尤其如此,這是因為在 UNIX 傳

用busybox製作Linux檔案系統製作過程

    Linux 檔案系統簡介 Linux支援多種檔案系統,包括ext2、ext3、vfat、ntfs、iso9660、jffs、romfs和nfs等,為了對各類檔案系統進行統一管理,Linux引入了虛擬檔案系統VFS(Virtual File System),為各類檔案

Linux檔案系統目錄結構

  在我們初學嵌入式Linux時,首先學習的就是Linux的最小根檔案系統;下面我將為初學者們詳細的闡述一下Linux的最小根檔案系統。     根目錄在Linux中即為“/”,要進入根目錄,命令“cd  /”即可進入根目錄,並利用&ld

檔案系統及Busybox之一

轉自 https://blog.csdn.net/chenlong12580/article/details/8761108   目錄 1.根檔案系統簡介...2 2.Busybox簡介...2 2.1Busybox簡介...2 2.2Busybox目錄結

linux基礎3-磁碟和檔案系統相關 LINUX支援哪些檔案系統 linux下磁碟分割槽 圖文(fdisk;mkfs)

一 dumpe2fs :    在Linux使用過程中,我們如果要了解檔案系統的配置情況,可以使用dumpe2fs檢視ext2/ext3/ext4格式的檔案系統資訊。 命令格式: dumpe2fs [選項] 裝置 常用選項: -h  僅列出超級塊中的資訊

嵌入式Linux簡單驅動模組編譯與剖析

# 最簡單的核心模組: #include<linux/module.h> #include<linux/kernel.h> static int my_driver_init(void) { printk("hello\n"); return

Linux之nfs檔案系統

NFS 概念 網路檔案系統 (NFS) 是 Unix 系統和網路附加儲存檔案管理器常用的網路檔案系統 , 允許多個客戶端通過網路共享檔案訪問。它可用於提供對共享二進位制目錄的訪問 , 也可用於允許使用者在同一工作組中從不同客戶端訪問其檔案。 一、nfs

Linux系統檔案賦權指令chmod和檔案所有權指令chown

檔案賦權指令chmod( chmod -R 777 /home/user) 檢視檔案許可權的語句:   在終端輸入: ls -l xxx.xxx (xxx.xxx是檔名)   那麼就會出現相類

Linux檔案系統--AUFS

Linux檔案系統–AUFS 檢查是否支援aufs 由於aufs的可維護性差,所以許多Linux的發行版本並沒有將aufs編譯進核心中,使用如下命令檢視是夠支援aufs。(Ubuntu 16.04) [email protected]:~$ gre

Linux 文件系統之LVM

linux lvm snapshotLVM是 Logical Volume Manager邏輯卷管理的簡寫,主要功能對卷能夠進行方便的動態擴展和縮減,大提高了磁盤管理的靈活性,工作原理大概如下幾點1.物理磁盤被格式化為PV(Physical Volume) 物理卷,空間被劃分為一個個的PE(Physical

安裝linux基本的系統配置

mod name rip iyu help wget edi work mount 1.安裝linux最小化2.修改分辨率:centos 7:vi /etc/sysconfig/grub"nomodeset vga=0x317"grub2-mkconfi

Linux安裝ssh,原來這麼簡單就可以了(

前提需要連線外網 1、首先要在烏班圖系統中,使用快捷鍵"Ctrl"+“Alt”+"T"開啟終端的快捷方式。 2、第一次設定root密碼命令為:“sudo passwd”,輸入自己想要設定的密碼。比如說(女友的生日) 3、設定完root密碼後,在確保連線外網的情況下輸入命令:“sudo

Linux檔案開啟數

轉載:https://www.cnblogs.com/pangguoping/p/5791432.html 介紹 在Linux下有時會遇到Socket/File : Can't open so many files的問題。其實Linux是有檔案控制代碼限制的,而且Linux預設一般都是102

3、簡述Linux支援哪些檔案系統及其特點

答:ext2和ext3. ext2的特性:1、當建立ext2檔案系統時,系統管理員可以根據預期的檔案平均長度來選擇最佳塊的大小(從1024到4069位元組)                     2:當建

Linux檢視分割槽檔案系統型別總結

在Linux 中如何檢視分割槽的檔案系統型別,下面總結幾種檢視分割槽檔案系統型別的方法。 1、df -T 命令檢視 這個是最簡單的命令,檔案系統型別在Type列輸出。只可以檢視已經掛載的分割槽和檔案系統型別。如下所示: 2、parted -l命令檢視 如下所示,parted -l 命令

linux裡tmpfs檔案系統

linux裡tmpfs檔案系統 是一個虛擬記憶體檔案系統,它不同於傳統的用塊裝置形式來實現的Ramdisk,也不同於針對實體記憶體的Ramfs。Tmpfs可以使用實體記憶體,也可以使用交換分割槽。 umount /data mount -t tmpfs -o size=32G,mode=0755

linux學習 lesson23CIFS檔案系統

一、基本概念: CIFS是實現檔案共享服務的一種檔案系統,主要用於實現windows系統中的檔案共享,linux系統中用的比較少linux系統中利用CIFS檔案系統實現檔案共享,需要安裝samba服務。它使程式可以訪問遠端Internet計算機上的檔案並要求此計算機提供服務。CIFS 使

Linux之初識檔案系統及初步管理

一、什麼是檔案系統 計算機的檔案系統是一種儲存和組織計算機資料的方法,它使得對其訪問和查詢變得容易,檔案系統使用檔案和樹形目錄的抽象邏輯概念代替了硬碟和光碟等物理裝置使用資料塊的概念,使用者使用檔案系統來儲存資料不必關心資料實際儲存在硬碟(或者光碟)的地址為多少的資料塊上,只需要記住這個檔案的所

JAVA 簡單獲取系統時間程式碼 LocalDateTime( 以yyyy-MM-dd HH:mm:ss.SSS格式顯示)

直接上程式碼,簡單粗暴:   import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** * @Author : JCccc * @CreateTime : 2018-11-27