数据结构: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("."); }