数据结构:
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(".");
}
vfs学习
最新推荐文章于 2021-04-21 17:13:56 发布