深入理解linux内核之文件系统分析一

前面的不写了,从内核源码do_mount开始分析
通过该函数传递过来的flags参数来设置mnt_flag的参数,然后清除flags的一些标志,
然后调用函数,看看安装点的路径名,如果错误直接退出

retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);  
    if (retval)
        return retval;

然后把路径名存放到nd的结构体里面去

int kern_path(const char *name, unsigned int flags, struct path *path)
{
    struct nameidata nd;
    int res = do_path_lookup(AT_FDCWD, name, flags, &nd);
    if (!res)
        *path = nd.path;
    return res;
}

然后根据各种flags标志进行对应的安装操作

    if (flags & MS_REMOUNT)    //重新安装
        retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
                    data_page);
    else if (flags & MS_BIND) //
        retval = do_loopback(&path, dev_name, flags & MS_REC);
    else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
        retval = do_change_type(&path, flags);
    else if (flags & MS_MOVE)
        retval = do_move_mount(&path, dev_name);
    else
        retval = do_new_mount(&path, type_page, flags, mnt_flags,
                      dev_name, data_page);

我们分析 do_new_mount()函数
这个函数的作用在于在命名空间里面创建一个挂载点,然后加载进命名空间的树节点里面去
进入do_new_mount里后,利用get_fs_type获取文件类型,分配一个文件描述符(alloc_vfsmnt),初始化一个新的超级块对象(get_sb),

然后进入do_add_mount 获取进程信号量,修改命名空间,插入新的安装文件到命名空间里面去,

    down_write(&namespace_sem); //当前进程的信号量
    /* Something was mounted here while we slept */
    while (d_mountpoint(path->dentry) &&
           follow_down(path))
        ;
    err = -EINVAL;   //返回一个错误码  
    if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(path->mnt))
        goto unlock;

    /* Refuse the same filesystem on the same mount point */
    err = -EBUSY;
    if (path->mnt->mnt_sb == newmnt->mnt_sb &&
        path->mnt->mnt_root == path->dentry)
        goto unlock;

    err = -EINVAL;
    if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
        goto unlock;

    newmnt->mnt_flags = mnt_flags;
    if ((err = graft_tree(newmnt, path)))//
        goto unlock;

    if (fslist) /* add to the specified expiration list */
        list_add_tail(&newmnt->mnt_expire, fslist);

    up_write(&namespace_sem);

安装根文件系统
第一步定义这个结构体

static struct file_system_type rootfs_fs_type = {
    .name       = "rootfs",
    .get_sb     = rootfs_get_sb,
    .kill_sb    = kill_litter_super,
};

然后在init_rootfs里面注册这个结构体

int __init init_rootfs(void)
{
    int err;

    err = bdi_init(&ramfs_backing_dev_info);
    if (err)
        return err;

    err = register_filesystem(&rootfs_fs_type);
    if (err)
        bdi_destroy(&ramfs_backing_dev_info);

    return err;
}

到init_mount_tree函数里
调用mnt = do_kern_mount(“rootfs”, 0, “rootfs”, NULL);
上面我们注册了rootfs的设备驱动,通过get_fs_type()函数找到注册的那个结构体,再用vfs_kern_mount(type, flags, name, data)分配一个安装文件的描述符,最终调用了上面注册的rootfs_get_sb函数。然后调用get_sb_nodev函数

do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
    struct file_system_type *type = get_fs_type(fstype);
    struct vfsmount *mnt;
    if (!type)
        return ERR_PTR(-ENODEV);
    mnt = vfs_kern_mount(type, flags, name, data);
    if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
        !mnt->mnt_sb->s_subtype)
        mnt = fs_set_subtype(mnt, fstype);
    put_filesystem(type);
    return mnt;
}
    static int rootfs_get_sb(struct file_system_type *fs_type,
    int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
    return get_sb_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super,
                mnt);
}

接下来分析get_sb_nodev函数

int get_sb_nodev(struct file_system_type *fs_type,
    int flags, void *data,
    int (*fill_super)(struct super_block *, void *, int),
    struct vfsmount *mnt)
{
    int error;
    struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);

    if (IS_ERR(s))
        return PTR_ERR(s);

    s->s_flags = flags;

    error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
    if (error) {
        deactivate_locked_super(s);
        return error;
    }
    s->s_flags |= MS_ACTIVE;
    simple_set_mnt(mnt, s);
    return 0;
}

调用sget函数分配新的超级块,这里传入一个 null进来,所以先分配超级块,然后设置,调用set_anon_super(),set_anon_super()函数用于初始化特殊文件系统的超级块,该函数本质上获取一个未使用的次设备号dev,然后用主设备号0和次设备号dev设置新超级块的s_dev字段,而kill_anon_super()函数移走特殊文件系统的超级块,

struct super_block *sget(struct file_system_type *type,
            int (*test)(struct super_block *,void *),
            int (*set)(struct super_block *,void *),
            void *data)
    spin_lock(&sb_lock);
    if (test) {
        list_for_each_entry(old, &type->fs_supers, s_instances) {
            if (!test(old, data))
                continue;
            if (!grab_super(old))
                goto retry;
            if (s) {
                up_write(&s->s_umount);
                destroy_super(s);
            }
            return old;
        }
    }
    if (!s) {
        spin_unlock(&sb_lock);
        s = alloc_super(type);
        if (!s)
            return ERR_PTR(-ENOMEM);
        goto retry;
    }

返回到最初的函数init_mount_tree()
将进程0的根目录和当前工作目录设置为根文件系统。

mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
    if (IS_ERR(mnt))
        panic("Can't create rootfs");
    ns = create_mnt_ns(mnt);
    if (IS_ERR(ns))
        panic("Can't allocate initial namespace");

    init_task.nsproxy->mnt_ns = ns;
    get_mnt_ns(ns);

    root.mnt = ns->root;
    root.dentry = ns->root->mnt_root;

    set_fs_pwd(current->fs, &root);
    set_fs_root(current->fs, &root);

安装实际的根文件系统
根文件系统安装操作是第二个阶段是由内核在系统初始化即将结束的时候进行的,根据内核被编译时所选择的选项,和内核装入程序所传递的启动选项,有几种安装方法,现在分析磁盘文件系统,它的的设备名文件通过”root”启动参数传递给内核。
下面分析prepare_namespace()函数

if (saved_root_name[0]) {
         /*saved_root_name是从启动参数"root"中获取的设备文件名"*/
        root_device_name = saved_root_name; 
        if (!strncmp(root_device_name, "mtd", 3) ||
            !strncmp(root_device_name, "ubi", 3)) {
            mount_block_root(root_device_name, root_mountflags);
            goto out;
        }
        ROOT_DEV = name_to_dev_t(root_device_name);
        if (strncmp(root_device_name, "/dev/", 5) == 0)
            root_device_name += 5;
    }

具体saved_root_name是怎么从启动参数”root”获取设备文件名的还不清楚,(待议),把ROOT_DEV 变量置为同一设备文件的主设备号和次设备号。
现在调用mount_root()函数
分析mount_root()函数

#ifdef CONFIG_ROOT_NFS
    if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
        if (mount_nfs_root())
            return;

        printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
        ROOT_DEV = Root_FD0;
    }

先配置nfs网络文件系统,nfs配置的设备号为0 root后的第一个设备,到挂载节点上去,在/dev/root下创建一个设备,至于后面还加载了一个root_mountflags的设备,不是很清楚

static int __init mount_nfs_root(void)
{
    void *data = nfs_root_data();

    create_dev("/dev/root", ROOT_DEV);
    if (data &&
        do_mount_root("/dev/root", "nfs", root_mountflags, data) == 0)
        return 1;
    return 0;
}

你可能感兴趣的:(深入理解linux内核之文件系统分析一)