话说文件系统——aufs源码分析(三)

1. linux中设备驱动的入口都是:module_init(xxx_init);里面注册的函数,对于文件系统来说也是一样的,对于aufs来说,就是aufs_init,具体如下

//用于描述aufs文件系统的特性和功能
static struct file_system_type aufs_type = { .name = "aufs", .mount = aufs_get_sb, .kill_sb = kill_litter_super, };
static int __init aufs_init(void)
{
	int ret;

	struct dentry *pslot;
	
	ret = register_filesystem(&aufs_type); //把注册具体的文件系统
	if (ret) {
		printk(KERN_ERR "aufs: cannot register file system\n");
		return ret;	
	}

	aufs_mount = kern_mount(&aufs_type); //创建aufs对于的super_block和根目录dentry
	if (IS_ERR(aufs_mount)) {
		printk(KERN_ERR "aufs: cannot mount file system\n");
		unregister_filesystem(&aufs_type);
		return ret;
	}

     ......

	return 0;
}

2. struct file_system_type结构体在(kernel)/include/linux/fs.h中。每个注册到内核中的文件系统都有一个这样的结构体来表示,如下:

struct file_system_type {
	const char *name; //文件系统的名字,比如我们的名字为“aufs”
	int fs_flags;
     .....
	//用于从磁盘文件系统中读取到super_block,或者对于一些特殊文件系统来说,直接在kernel内存
	//中构造对于的super_block结构体。我们的aufs不是磁盘文件系统,所以是直接在内存中构造的
        struct dentry *(*mount) (struct file_system_type *, int,
		       const char *, void *);
	void (*kill_sb) (struct super_block *);//用于结束访问对应文件系统的super_block
	struct module *owner;
	struct file_system_type * next;//注册文件系统时,会把file_system_type添加到全局的file_systems中
	struct hlist_head fs_supers;
	......
};

3. (kernel)/fs/filesystems.c register_filesystem函数主要的作用就是通过在在内核中搜索是否要注册的文件系统是否已经注册过了,如果没有

就注册到系统中,其实所谓注册,就是把对应的file_system_type结构体添加到全局的file_systems变量组成的链表中。每个file_system_type不是

有next成员吗,就是通过next把系统中的所有文件系统链接到一起的。

int register_filesystem(struct file_system_type * fs)
{
	int res = 0;
	struct file_system_type ** p;

	BUG_ON(strchr(fs->name, '.'));
	if (fs->next) //如果要注册的fs的next不是NULL,证明此fs已经注册过了
		return -EBUSY;
	write_lock(&file_systems_lock);
	p = find_filesystem(fs->name, strlen(fs->name));//通过要注册的fs的名字在kernel中搜索
	if (*p)//如果搜索到了,证明已经注册过了
		res = -EBUSY;
	else //如果没有找到,就把具体的fs注册到系统中
		*p = fs;
	write_unlock(&file_systems_lock);
	return res;
}

4. find_filesystem((kernel)/fs/filesystems.c)函数在系统中通过所注册文件系统的名字,通过简单的字符串比对的方式找是否在系统中已经注册了。

这也可以说,文件系统的名字必须得唯一,这里需要注意的是:这个函数的返回值是指针的指针,正是由于这样,在register_filesystem函数中

可以改变find_filesystem函数返回的值,也是把要注册的文件系统添加到链表中了

static struct file_system_type **find_filesystem(const char *name, unsigned len)
{
	struct file_system_type **p;
	for (p=&file_systems; *p; p=&(*p)->next)
		if (strlen((*p)->name) == len &&
		    strncmp((*p)->name, name, len) == 0)
			break;
	return p;
}

5. 把文件系统中注册到链表中后,register_filesystem函数的使命就结束了。注册就这么简单?没错,没有花眼,其实重头戏在kern_mount函数中,

这个函数位于:(kernel)/include/linux中,是一个宏定义(#define kern_mount(type) kern_mount_data(type, NULL)),好吧,看来kern_mount只是

kern_mount_data函数穿了一件外套,那就看看我们的真大神吧。

//(kernel)/fs/namespace.c
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
{
	struct vfsmount *mnt;//文件系统的挂载点,这个结构代表一种文件系统的一个特定的实例,也就是说一种文件系统可以
						 //在父文件系统的多个地方挂载,只有文件系统挂载到具体的某个目录下了,才能使用
	mnt = vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);//注意我们传入的data为NULL
	if (!IS_ERR(mnt)) {
		/*
		 * it is a longterm mount, don't release mnt until
		 * we unmount before file sys is unregistered
		*/
		real_mount(mnt)->mnt_ns = MNT_NS_INTERNAL;
	}
	return mnt;
}

6. vfs_kern_mount这个函数主要的目的就是分配struct mount、aufs文件系统的根目录dentry、aufs的super_block,同时对其初始化,且把它们相互

联系起来

//(kernel)/fs/namespace.c
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);

	mnt = alloc_vfsmnt(name);//根据name创建mount,对我们来说就是创建名为aufs的mount
	if (!mnt)
		return ERR_PTR(-ENOMEM);

	if (flags & MS_KERNMOUNT)
		mnt->mnt.mnt_flags = MNT_INTERNAL;

	root = mount_fs(type, flags, name, data);//创建aufs的根目录dentry,同时创建aufs对应的super_block
	if (IS_ERR(root)) {
		mnt_free_id(mnt);
		free_vfsmnt(mnt);
		return ERR_CAST(root);
	}

	mnt->mnt.mnt_root = root;//把aufs的根目录dentry赋值给vfsmount的mnt_root,这样就可以通过挂载点找到根目录dentry
	mnt->mnt.mnt_sb = root->d_sb;//把aufs的super_block赋值给vfsmount的mnt_sb
	mnt->mnt_mountpoint = mnt->mnt.mnt_root;
	mnt->mnt_parent = mnt;//把父指针设置为自己
	lock_mount_hash();
	list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);//把mount实例添加到super_block中的s_mounts链表中,
                               //跟代码发现在文件系统重新时会遍历这个链表
	unlock_mount_hash();
	return &mnt->mnt;//返回aufs根目录的dentry
}

7. mount_fs函数中根据条件创建和初始化了aufs文件系统的super_block和根目录的dentry,其实真真是通过执行aufs_get_sb这个函数

创建的,这个函数中更多的是把aufs_get_sb函数返回的dentry和super_block进行处理

struct dentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{
	struct dentry *root;
	struct super_block *sb;
	char *secdata = NULL;
	int error = -ENOMEM;
  //注意:因为我们传入的data为NULL,所以这里直接跳过去
	if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
    ......
	}
    //其实真正创建aufs文件系统的super_block和根目录dentry是在这个函数中,还记得吗,我们的aufs_type的mount指针指向的是
  //aufs_get_sb这个函数
	root = type->mount(type, flags, name, data);
	if (IS_ERR(root)) {
		error = PTR_ERR(root);
		goto out_free_secdata;
	}
	sb = root->d_sb;//从root的dentry的得到super_block
	BUG_ON(!sb);
	WARN_ON(!sb->s_bdi);
	sb->s_flags |= MS_BORN;

	.....
	return root;
        .....
}

8. 视线转移到aufs_get_sb吧,这个函数只是调用了mount_single函数,不过注意,aufs_fill_super函数作为参数传递给了mount_single。

static struct dentry *aufs_get_sb(struct file_system_type *fs_type,
		int flags, const char *dev_name, void *data)
{
	return mount_single(fs_type, flags, data, aufs_fill_super);
}

9. 在我们实现的aufs文件系统的super_block和根目录dentry都是从这个函数中得到

(kernel)/fs/super.c
struct dentry *mount_single(struct file_system_type *fs_type, int flags, void *data, int (*fill_super)(struct super_block *, void *, int)) { struct super_block *s; int error; //搜索或者创建aufs对应的super_block,其中compare_single函数返回一直是1; //set_anon_super这个函数把创建的super_block的dev设置为none,比如如果是 //一个磁盘文件系统的话,对应的设备就是一个磁盘,或者U盘等 s = sget(fs_type, compare_single, set_anon_super, flags, NULL); if (IS_ERR(s)) return ERR_CAST(s); if (!s->s_root) { //如果执行到这里,证明根目录的dentry还没有创建,那我们推断这个还是中就会创建 //根目录对应的dentry和inode,同时把开始时创建的super_block联系起来,这个函数通过参数 //传递进来的,其实对应为aufs_fill_super 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; } else { do_remount_sb(s, flags, data, 0); } return dget(s->s_root); }

10. 到这个地方已经是胜利在望了,我们继续跟踪一下sget这个函数,它是怎么创建aufs的super_block的。其实file_system_type中

维护着一个以fs_supers的super_block链表,对应我们的aufs_type,当然我们的aufs_type中此成员设置为NULL,那么也就会创建一个

super_block,同时把aufs_type的对应创建的super_block添加到aufs_type中fs_supers链表中,同时把分配的super_block添加到内核维

护的一个全局super_block链表中

struct super_block *sget(struct file_system_type *type,
			int (*test)(struct super_block *,void *),
			int (*set)(struct super_block *,void *),
			int flags,
			void *data)
{
	struct super_block *s = NULL;
	struct super_block *old;
	int err;

retry:
	spin_lock(&sb_lock);
	if (test) {//这里test为compare_single,这个函数什么都没做,就返回1
		//在对应的file_system_type(aufs_type)结构中维护的fs_supers的super_block链表查找
		hlist_for_each_entry(old, &type->fs_supers, s_instances) {
			if (!test(old, data))
				continue;
			if (!grab_super(old))
				goto retry;
			if (s) {
				up_write(&s->s_umount);
				destroy_super(s);
				s = NULL;
			}
			return old;
		}
	}
	//如果没有找到,就分配一个super_block结构体,这里需要注意,分配完之后又goto retry,
	//这是因为allo_super有可能导致进程休眠,所以当分配成功时,有可能在进程休眠的时候已经
	//分配了对应的super_block,所以要goto回去
	if (!s) {
		spin_unlock(&sb_lock);
		s = alloc_super(type, flags);
		if (!s)
			return ERR_PTR(-ENOMEM);
		goto retry;
	}
		
	err = set(s, data);//这里的set对应:set_anon_super
	if (err) {
		spin_unlock(&sb_lock);
		up_write(&s->s_umount);
		destroy_super(s);
		return ERR_PTR(err);
	}
	s->s_type = type;//把创建的super_block的s_type设置为我们传递进来的aufs_type
	strlcpy(s->s_id, type->name, sizeof(s->s_id));//把创建的super_block的名字设置为aufs
	list_add_tail(&s->s_list, &super_blocks);//内核维护着一个super_block的链表,所有注册到内核中的文件系统都会注册到
											//这个链表上,这个链表的头就是super_blocks
	hlist_add_head(&s->s_instances, &type->fs_supers);//把aufs的super_block添加到aufs_type的fs_supers中
	spin_unlock(&sb_lock);
	get_filesystem(type);
	register_shrinker(&s->s_shrink);
	return s;
}

11. sget函数执行完之后,我们就得到了aufs_type对应的super_block,但是aufs对应的根目录的dentry还没有着落了,我们回到第9步,还记得吗,

我们在第9步说fill_super这个函数,对应aufs的aufs_fill_super函数

static int aufs_fill_super(struct super_block *sb, void *data, int silent)
{
	//在根目录下创建对应的文件,这里我们什么都没有创建
	static struct tree_descr debug_files[] = {{""}};
	//每个文件系统都会对应一个MAGIC number,用于区别文件系统
	return simple_fill_super(sb, AUFS_MAGIC, debug_files);
}

12. simple_fill_super从名字就可以看出来,主要是填充对应文件系统的super_block的,也会在这个函数创建aufs的根目录的dentry和inode,

当然这创建的这两个结构体也是为填充对应的super_block结构体嘛

//(kernel)/fs/libfs.c
int simple_fill_super(struct super_block *s, unsigned long magic, struct tree_descr *files) { struct inode *inode; struct dentry *root; struct dentry *dentry; int i; //这里初始化对应的super_block的成员信息 s->s_blocksize = PAGE_CACHE_SIZE; s->s_blocksize_bits = PAGE_CACHE_SHIFT; s->s_magic = magic; s->s_op = &simple_super_operations; s->s_time_gran = 1; inode = new_inode(s); //分配根目录对应的inode if (!inode) return -ENOMEM; /* * because the root inode is 1, the files array must not contain an * entry at index 1 */ //还记得上一篇文章中说inode表示一个特定的文件,对于文件那应该就一些属性, //比如文件的访问权限,创建时间、修改时间等 inode->i_ino = 1; inode->i_mode = S_IFDIR | 0755; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; set_nlink(inode, 2); root = d_make_root(inode);//这个函数创建文件系统的根目录dentry,可以看到会把dentry的 //的名字设置成了“/”,同时把根目录的dentry和inode匹配起来 if (!root) return -ENOMEM; //虽然我们传入的files为空的,但是这不妨我们看看这段代码,可以看到在具体的文件来是怎么 //创建普通文件的 for (i = 0; !files->name || files->name[0]; i++, files++) { if (!files->name) continue; /* warn if it tries to conflict with the root inode */ if (unlikely(i == 1)) printk(KERN_WARNING "%s: %s passed in a files array" "with an index of 1!\n", __func__, s->s_type->name); dentry = d_alloc_name(root, files->name); if (!dentry) goto out; inode = new_inode(s); if (!inode) { dput(dentry); goto out; } inode->i_mode = S_IFREG | files->mode; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_fop = files->ops; inode->i_ino = i; d_add(dentry, inode); } s->s_root = root; //设置对应的super_block的s_root为根目录的dentry, //注意对应的inode已经跟关联来了 return 0; out: d_genocide(root); shrink_dcache_parent(root); dput(root); return -ENOMEM; }

13. 到这里我们的aufs文件系统的super_block,根目录dentry和inode已经创建完成,且已经做了各种初始化,同时也把它们相互关联

起来了。在VFS的框架中,一个文件必须与一个inode结构体对应起来,必须有一个或者更多的dentry与inode对应起来,当然,在linux

中文件夹也是一种特殊的文件。哇塞,这么神奇啊,我们的aufs文件的雏形已经出来了,其实没有多么的复杂,我们要学会把复杂的问题

剖解成简单的、容易的来做,这样往往能达到不错的效果,也不至于因为一点都弄不明白,就放弃了。好吧,下一篇我们继续分析aufs

文件系统中是怎么创建文件夹和文件的。

posted @ 2018-04-15 16:53  葡萄宝宝  阅读(482)  评论(0编辑  收藏  举报