安装根文件系统分为两个阶段:
1、内核安装特殊rootfs文件系统,该文件系统仅提供一个作为初始安装点的空目录。
2、内核在空目录上安装实际根文件系统。比如pc上的ext4,比如嵌入式中用到的各种flash的根文件系统。
这一次分析sysfs,发现sysfs的安装和挂载更特殊:
在mnt_init中:
err = sysfs_init();
if (err)
printk(KERN_WARNING "%s: sysfs_init error: %d\n",
__func__, err);
fs_kobj = kobject_create_and_add("fs", NULL);
if (!fs_kobj)
printk(KERN_WARNING "%s: kobj create error\n", __func__);
init_rootfs();
init_mount_tree();
sysfs_init还在init_rootfs和init_mount_tree,这就很奇怪,rootfs还没初始化,整个vfs目录tree还没建起来,为什么就先sysfs_init了?我的理解是这样的:sys_init注册sysfs 并且建立sysfs的文件树,这样各种设备就可以在sysfs中表现出来。因为这个还是系统初始化的时候,我们需要很快先建立sysfs(fs bus driver这些目录当时仅仅建立在sysfs 的文件树中),而系统启动完以后,我们用mount -t sysfs sysfs /sys,这样sysfs才可以在整个vfs 目录树中得以展现,我们在用户空间才可以访问/sys目录。
sys_init-->register_filesystem(&sysfs_fs_type); & kern_mount(&sysfs_fs_type);-->kern_mount_data-->vfs_kern_mount
vfs_kern_mount我们已经分析过了,主要是根据文件系统的类型来创建vfsmount已安装文件系统描述符,并且sysfs自己的文件树建立起来了,也就是有了自己的“/”,这样 在初始后的过程就有了这个文件树的fs bus 等目录。
sysfs在初始化之初就安装好了,后面才用mount -t sysfs sysfs /sys将其挂载在vfs目录树之下。
在fs/namespace.c中有mount的系统调用的定义,主要是拷贝参数值到内核,然后调用do_mount
do_mount主要步骤:
1、调用kern_path(dir_name, LOOKUP_FOLLOW, &path);来获取安装点的路径名;
2、检查安装的标志,决定做什么
3、调用do_new_mount,基本上工作都在这里面。
do_new_mount分为两步:
1、do_kern_mount,这个已经分析过了,就是建立要mount的文件系统自己的目录树
2、do_add_mount将步骤1中建立的目录树加入到vfs的目录树,或者说加到rootfs的目录树上,因为任何fs都必须直接或间接 挂在rootfs上。
Do_add_mount 两个关键点:
1、
/* Something was mounted here while we slept */
while (d_mountpoint(path->dentry) &&
follow_down(path))
注释说的很清楚:这句代码不起眼,确实mount的关键,主要作用是做vfsmount的切换,为什么这么说我们要看第二个关键点后就会明了。
2、graft_tree
Graft:嫁接,接枝的意思
从这个单词可以看出来,是将将要mount的目录树与当前目录的文件系统的目录树连接起来,很像嫁接技术,而原来文件系统 的目录树没损伤。
Graft_tree先做一些检查类的操作,然后执行关键函数:attach_recursive_mnt(mnt, path, NULL);
2.1:propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list);
将新mount的目录的mnt_list与source_mnt的mnt_list连接起来
2.2:
mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
commit_tree(source_mnt);
void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,
struct vfsmount *child_mnt)
{
child_mnt->mnt_parent = mntget(mnt);
child_mnt->mnt_mountpoint = dget(dentry);
dentry->d_mounted++;
}
这样两个文件系统的目录嫁接就完成了,并且通过d_mounted标明被mount了。
static void commit_tree(struct vfsmount *mnt)
{
struct vfsmount *parent = mnt->mnt_parent;
struct vfsmount *m;
LIST_HEAD(head);
struct mnt_namespace *n = parent->mnt_ns;
BUG_ON(parent == mnt);
list_add_tail(&head, &mnt->mnt_list);
list_for_each_entry(m, &head, mnt_list)
m->mnt_ns = n;
list_splice(&head, n->list.prev);
list_add_tail(&mnt->mnt_hash, mount_hashtable +
hash(parent, mnt->mnt_mountpoint));
list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
touch_mnt_namespace(n);
}
Commit_tree是将新的文件系统vfsmount加入到mount_hashtable这个hash表中。
分析完这里,我们再回头看第一个关键点:
while (d_mountpoint(path->dentry) &&
follow_down(path))
;
static inline int d_mountpoint(struct dentry *dentry)
{
return dentry->d_mounted;
}
int follow_down(struct path *path)
{
struct vfsmount *mounted;
mounted = lookup_mnt(path);
if (mounted) {
dput(path->dentry);
mntput(path->mnt);
path->mnt = mounted;
path->dentry = dget(mounted->mnt_root);
return 1;
}
return 0;
}
如果当前的dentry的d_mounted大于0,表明这个上面已经有mount过文件系统,就要用follow_down做文件系统的切换动作。
lookup_mnt(path);就是根据graft_tree中的mount_hashtable这个hash表来查找的。
查找到后:
path->mnt = mounted;
path->dentry = dget(mounted->mnt_root);
就完成了文件系统的切换。
经过这两个关键点,我们的目录树就嫁接成功了。
其实在路径查找中,也会有这种文件系统切换,因为要保证每个文件操作的操作方法与正确的文件系统相对应。
到这里为止,sysfs也有了。下一篇分析路径查找,这个好难看,太长了。。。