vfs学习

数据结构:
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;
	//这句是重点,只要生成了mnt
	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 = 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.有RamDisk
		a,b两种情况对应两个文件/init/initramfs.c和/init/noinitramfs.c两个文件在
		init/makefile中是互斥存在的.二者都注册了Initb阶段函数且同名:
		rootfs_initcall(populate_rootfs);/init/initramfs.c
		rootfs_initcall(default_rootfs);/init/noinitramfs.c
		所以a,b两种情况区别在于生成根文件系统目录方法不同
		a.ramdisk为initramfs
			start_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为noninitramfs
			kernel_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.无RamDisk
		kernel_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(".");
 }
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值