vfs学习

来源:互联网 发布:java购物车生成订单 编辑:程序博客网 时间:2024/05/29 15:38
数据结构:struct path {//路径查找过程中用    struct vfsmount *mnt;    struct dentry *dentry;};struct qstr {//quick string,查找时用的          union {                  struct {                          HASH_LEN_DECLARE;                  };                  u64 hash_len;          };          const unsigned char *name;  };struct nameidata {//路径查找,中间结果//path.mnt表示当前文件系统对象地址//path.dentry表示当前目录对象地址          struct path     path;          struct qstr     last;          struct path     root;          struct inode    *inode; /* path.dentry.d_inode */          unsigned int    flags;          unsigned        seq;          int             last_type;          unsigned        depth;          char *saved_names[MAX_NESTED_LINKS + 1];  };struct file_system_type {//需要注册的文件系统类型    const char *name;    int fs_flags;    struct dentry *(*mount) (struct file_system_type *, int,                   const char *, void *);    void (*kill_sb) (struct super_block *);    struct module *owner;    struct file_system_type * next;    struct hlist_head fs_supers;struct lock_class_key s_lock_key;    struct lock_class_key s_umount_key;    struct lock_class_key s_vfs_rename_key;    struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];    struct lock_class_key i_lock_key;    struct lock_class_key i_mutex_key;    struct lock_class_key i_mutex_dir_key;};struct vfsmount {          struct dentry *mnt_root;        /* root of the mounted tree */          struct super_block *mnt_sb;     /* pointer to superblock */          int mnt_flags;  }struct mount {       struct list_head mnt_hash;//本描述符链入散列值链表       struct mount *mnt_parent;//父文件系统描述符       struct dentry *mnt_mountpoint;//父文件系统安装点目录       struct vfsmount mnt;...}  //////////////////////////////////////////////////////    /*   *有一个全局变量,将所有注册的文件系统链起来   *http://lxr.linux.no/linux+v3.6.2/fs/filesystems.c#L69   */     static struct file_system_type *file_systems;    /*   *rootfs的定义是rootfs_fs_type   *http://lxr.linux.no/linux+v3.6.2/fs/ramfs/inode.c#L272   */static struct file_system_type rootfs_fs_type = {    .name    = "rootfs",//名称    .mount   = rootfs_mount,//mount方法,返回根dentry    .kill_sb = kill_litter_super,//删除super_block方法 };  static struct dentry *rootfs_mount(struct file_system_type *fs_type,         int flags, const char *dev_name, void *data) {//这里是有一个回调ramfs_fill_super,用来填充super_block         return mount_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super); } static void ramfs_kill_sb(struct super_block *sb) {         kfree(sb->s_fs_info);         kill_litter_super(sb); }   //位置:http://lxr.linux.no/linux+v3.6.2/fs/super.c#L1073 struct dentry *mount_nodev(struct file_system_type *fs_type,        int flags, void *data,        int (*fill_super)(struct super_block *, void *, int)){        int error;//在本fs_type中查找或创建一个新的super_block        struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);        if (IS_ERR(s))                return ERR_CAST(s);//回调分配inode,并根据inode生成根目录("/")的dentry        error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);        if (error) {                deactivate_locked_super(s);                return ERR_PTR(error);        }        s->s_flags |= MS_ACTIVE;        return dget(s->s_root);}   void kill_litter_super(struct super_block *sb) {         if (sb->s_root)                 d_genocide(sb->s_root);         kill_anon_super(sb); }  /////////////////////////注册并安装rootfs//////////////////////////// /*  * 每种文件系统只有一个超级块。  * rootfs是启动后系统装载的第一个文件系统,它是特殊的,因为它提供  * 了一个文件系统的根,其它的文件系统在这个文件系统上加载。成为它的  * 某一个目录。  * 文件系统的mount有覆盖性,比如在/dev下加载一个ext2文件系统,那么当  * 访问/dev时,就会实际访问到ext2文件系统内容,父文件系统内容被覆盖。  * 同时,也可多次在一点上加载文件系统,比如再在/dev上加载一个ntfs文件  * 系统,那么在访问/dev时,访问的是ntfs文件系统内容。因为vfs总是访问最  * 后加载的那个文件系统,父级文件系统被覆盖掉了。  *   * rootfs默认只生成一个根目录"/",如果添加子目录的话,将生成一对dentry  * 和inode,比如生成/dev目录。而/dev目录是属于rootfs文件系统的。  *  * 每当在某个目录加载一个文件系统时,系统会为此文件系统生成几个结构:  * file_system_type,vfs_mount,super_block,根dentry,inode;而根文件  * 系统的这几个结构是在初始化阶段放进去的。如果在根文件系统某目录  * 如/dev上挂载新文件系统,如ext2,那么vfs会为ext2文件系统也生成一套  * 结构。  * 两个文件系统是怎么串连起来而形成一棵树的呢?怎么覆盖父文件系统目录  * 的呢?  *   * 对rootfs而言,它的/dev有inode_dev,dentry_dev,  * vfsmount_dev_rootfs(文件系统描述符)  * 而ext2 挂载到rootfs的/dev时,也会生成inode_ext2_dev,dentry_ext2_dev  * vsfmount_dev_ext2同时会设置dentry_dev.d_mounted,来表示rootfs的/dev  * 目录节点被加载过新文件系统。同时,系统会把本vsfmount_dev_ext2文件系统  * 描述符挂载到一个hash表中去,以父文件系统对应结点的dentry和vsmount为key  *   * 在遍历时,当查找到rootfs的/dev时,发现dentry_dev.d_mounted不为0,则在  * 那个vfsmount的hash表中找vsfmount_dev_ext2的描述符。而描述符中的mnt_root  * 字段指向子文件系统的根目录dentry,进而继续遍历。  * 同时要注意,/dev下可能多次挂载文件系统,那么dentry_dev.d_mounted>1了,  * hash表中以rootfs信息为key的hash项就是一个链表,每次会找到最后加入(最新)  * 的文件系统,这样就实现了对老文件系统的覆盖。  *   * 可见,同一目录下可能加载多个文件系统。同一目录节点,几个文件系统,对  * 应几个dentry,inode,vsfmount,super_block等结构。  *  *在http://lxr.linux.no/linux+v3.6.2/init/main.c#L466的  * start_kernel中有两个函数与vfs相关    * vfs_caches_init_early();//申请dentry和inode的cache  *vfs_caches_init(totalram_pages);  *  * vfs_caches_init中有个函数叫mnt_init(),此函数完成加载rootfs工作。  * 它体内调用了两个函数 init_rootfs();和init_mount_tree();     *  * 路径:start_kernel->vfs_caches_init->mnt_init->init_rootfs  * 在http://lxr.linux.no/linux+v3.6.2/fs/ramfs/inode.c#L276中  */int __init init_rootfs(void){    int err;     err = bdi_init(&ramfs_backing_dev_info);    if (err)       return err; //这句就是把rootfs_fs_type变量链入file_systems全局链表中    err = register_filesystem(&rootfs_fs_type);    if (err)       bdi_destroy(&ramfs_backing_dev_info);     return err; }  /*  * 路径:start_kernel->vfs_caches_init->mnt_init->init_mount_tree  * 在http://lxr.linux.no/linux+v3.6.2/fs/namespace.c#L2581中  * 用rootfs的vfsmount,dentry设置当前(0)进程的fs_struct结构。  * 也设置了Init进程的namespace,使其后代有rootfs相关属性,所有进程  * 从他生出,它的后代进程都默认继承这个属性。  */ static void __init init_mount_tree(void){struct vfsmount *mnt;struct mnt_namespace *ns;struct path root;//这句是重点,只要生成了mntmnt = 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 = mnt;root.dentry = mnt->mnt_root;//设置进程0的当前目录和根目录set_fs_pwd(current->fs, &root);set_fs_root(current->fs, &root);}  static struct vfsmount *do_kern_mount(const char *fstype, int flags, const char *name, void *data){//在file_system链表中找到这种类型的文件系统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;}  struct vfsmount * vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) {     struct mount *mnt;     struct dentry *root;      if (!type)         return ERR_PTR(-ENODEV);//申请一个mount结构     mnt = alloc_vfsmnt(name);     if (!mnt)         return ERR_PTR(-ENOMEM);      if (flags & MS_KERNMOUNT)         mnt->mnt.mnt_flags = MNT_INTERNAL;/* * type:rootfs的文件类型 * name:rootfs * data:NULL * 调用type里的mount函数rootfs_mount()生成根dentry,inode */     root = mount_fs(type, flags, name, data);     if (IS_ERR(root)) {         free_vfsmnt(mnt);         return ERR_CAST(root);     }      mnt->mnt.mnt_root = root;     mnt->mnt.mnt_sb = root->d_sb;//root带回来的sb     mnt->mnt_mountpoint = mnt->mnt.mnt_root;     mnt->mnt_parent = mnt;     br_write_lock(&vfsmount_lock);     list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);     br_write_unlock(&vfsmount_lock);     return &mnt->mnt; }   ////////////////////////////////////////////////////// /*  * rootfs 加载完成后,就会加载别的实际文件系统,如initrd(the RamDisk)  * 实际上initrd的使用是根据配置来的。(以下内容来自网上)  * 0.安装好rootfs  * 1.有RamDiska,b两种情况对应两个文件/init/initramfs.c和/init/noinitramfs.c两个文件在init/makefile中是互斥存在的.二者都注册了Initb阶段函数且同名:rootfs_initcall(populate_rootfs);/init/initramfs.crootfs_initcall(default_rootfs);/init/noinitramfs.c所以a,b两种情况区别在于生成根文件系统目录方法不同a.ramdisk为initramfsstart_kernel->rest_init->kernel_init->do_basic_setup->do_initcalls调用rootfs_initcall注册过的函数rootfs_initcall(populate_rootfs)[http://lxr.linux.no/linux+v3.6.2/init/initramfs.c中有一句rootfs_initcall(populate_rootfs);]populate_rootfs解压initramfs到rootfs,initramfs须包含init文件,否则还要挂载其它文件系统。kernel会检查init文件是否存在,如果存在就不再挂载其它文件系统,prepare_namespace也不再被调用,否则还需要挂载其它文件系统b.ramdisk为noninitramfskernel_init->prepare_namespace->initrd_load->rd_load_image--加载->identify_ramdisk_image若ROOT_DEV!=ROOT_RAM0则handle_initrd通过linuxrc启动用户态进程若ROOT_DEV==ROOT_RAM0则rd_load_image加载后,也通过mount_root加载ROOT_RAM0根文件系统。2.无RamDiskkernel_init->prepare_namespace->mount_root,mount_root会加载新文件系统,并将新文件系统根移动到rootfs根上  */  static int __init kernel_init(void * unused){...do_basic_setup();...if (!ramdisk_execute_command)        ramdisk_execute_command = "/init";    if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {/* * 前面的do_basic_setup会加载initramfs,如果没加载或/init找不到就会 * 调用prepare_namespace来重加载文件系统。核心问题是/init是否存在 */        ramdisk_execute_command = NULL;        prepare_namespace();    }...} static void __init do_basic_setup(void) {         cpuset_init_smp();         usermodehelper_init();         shmem_init();         driver_init();         init_irq_proc();         do_ctors();         usermodehelper_enable();         do_initcalls(); }  /*  * 进这个函数的前提:  * 1. do_basick_setup中函数没加载initrd  * 2. 1中加载了,但没有/init  * 3. 系统没有提供ramdisk,则需要mount_root重建文件系统和目录  * 实质上是需要启动的ramdisk不存在的话,就得进来让系统正常启动  *   */ void __init prepare_namespace(void) {     int is_floppy;      if (root_delay) {         printk(KERN_INFO "Waiting %dsec before mounting root device...\n",            root_delay);         ssleep(root_delay);     }     wait_for_device_probe();      md_run_setup();      if (saved_root_name[0]) {         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;     }      if (initrd_load())//创建/dev/ram设备,加载/initrd.image         goto out;      /* wait for any asynchronous scanning to complete */     if ((ROOT_DEV == 0) && root_wait) {         printk(KERN_INFO "Waiting for root device %s...\n",             saved_root_name);         while (driver_probe_done() != 0 ||             (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)             msleep();         async_synchronize_full();     }      is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;      if (is_floppy && rd_doload && rd_load_disk(0))         ROOT_DEV = Root_RAM0;      mount_root(); out:     devtmpfs_mount("dev");     sys_mount(".", "/", NULL, MS_MOVE, NULL);     sys_chroot("."); }                                      

原创粉丝点击