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

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

话说文件系统——aufs源码分析(三)的更多相关文章

  1. 话说文件系统——aufs源码分析(三)【转】

    本文转载自:http://www.cnblogs.com/xie0812/p/8848185.html 1. linux中设备驱动的入口都是:module_init(xxx_init);里面注册的函数 ...

  2. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  3. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  4. Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析

    代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian AnnotationCon ...

  5. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

  6. ABP源码分析三十一:ABP.AutoMapper

    这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttri ...

  7. ABP源码分析三十三:ABP.Web

    ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在A ...

  8. ABP源码分析三十四:ABP.Web.Mvc

    ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...

  9. ABP源码分析三十五:ABP中动态WebAPI原理解析

    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...

随机推荐

  1. 套接字和标准I/O缓冲区

    设置标准I/O函数缓冲区的主要目的是为了提高性能.但套接字中的缓冲主要是为了实现TCP协议而设立的.例如,TCP传输中丢失数据时将再次传递,而再次发送数据则意味着在某地保存了数据.存在什么地方呢?套接 ...

  2. 用AutoHotkey实现Excel从表B提取匹配数据到表A

    说明:为表述方便,待填的表为[表A],资料库的表称为[表B].该工具可以快捷地从[表B]中提取相关数据到[表A],顺序和列可自定义. 使用方法:1.打开[ExcelGetFromB.exe](如要打开 ...

  3. August 09th 2017 Week 32nd Wednesday

    Find hope from despair, life will become brilliant. 从绝望中寻找希望,人生终将辉煌. Have you ever seen the movie Ba ...

  4. php中的static

    静态成员是一种类变量,可以把它看成时属于整个类而不是属于类的某个实例.与一般的实例变量不同的是,静态成员只保留一个变量值,而这个变量值对所有的实例都是有效的,也就是说,所有的实例共享这个成员. $th ...

  5. PhoneGap获取设备信息

    一. 获取设备信息的方法列表(如果没有或者检测不出来就显示undefined) 1.device.name              设备名称(一些国产机检测不出来) 2.device.model   ...

  6. 【模板】Tarjan算法与有向图的强连通性

    概念 流图 给定一个有向图G= (V,E),若存在r∈V满足,满足从r出发能够到达V中所有的点,则称G是一个流图,记为(G,r),其中r是流图的源点. 流图的搜索树 在一个流图(G,r)上从r出发,进 ...

  7. ubuntu ibus 输入法总在左下角不跟随光标的处理

    sudo apt-get install ibus-gtk ibus-gtk3 ibus-qt4 参考文章

  8. 10、Web Service-IDEA-jaxrs 整合spring

    1.服务端的开发 1.web项目目录 2.pom <?xml version="1.0" encoding="UTF-8"?> <projec ...

  9. max函数

    无论是几维,都只返回一个最大的值 >>> a = [1,2,3] >>> np.max(a) 3 >>> a = [[2,1],[3,4]] &g ...

  10. linux下批量重命名文件

    # 使用通配符批量创建 多个文件:$ touch zqunor{1..7}.txt # 批量将多个后缀为 .txt 的文本文件重命名为以 .c 为后缀的文件:$ rename 's/\.txt/\.c ...