话说文件系统——aufs源码分析(三)【转】
本文转载自:http://www.cnblogs.com/xie0812/p/8848185.html
1. linux中设备驱动的入口都是:module_init(xxx_init);里面注册的函数,对于文件系统来说也是一样的,对于aufs来说,就是aufs_init,具体如下
1
2
3
4
5
|
//用于描述aufs文件系统的特性和功能static struct file_system_type aufs_type = { .name = "aufs" , .mount = aufs_get_sb, .kill_sb = kill_litter_super, }; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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中。每个注册到内核中的文件系统都有一个这样的结构体来表示,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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把系统中的所有文件系统链接到一起的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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函数返回的值,也是把要注册的文件系统添加到链表中了
1
2
3
4
5
6
7
8
9
|
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函数穿了一件外套,那就看看我们的真大神吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//(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,同时对其初始化,且把它们相互
联系起来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
//(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进行处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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。
1
2
3
4
5
|
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都是从这个函数中得到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
(kernel)/fs/super.cstruct 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链表中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
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函数
1
2
3
4
5
6
7
|
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结构体嘛
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
//(kernel)/fs/libfs.cint 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源码分析(三)【转】的更多相关文章
- 话说文件系统——aufs源码分析(三)
1. linux中设备驱动的入口都是:module_init(xxx_init);里面注册的函数,对于文件系统来说也是一样的,对于aufs来说,就是aufs_init,具体如下 //用于描述aufs文 ...
- tomcat源码分析(三)一次http请求的旅行-从Socket说起
p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析
代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian AnnotationCon ...
- ABP源码分析三:ABP Module
Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...
- ABP源码分析三十一:ABP.AutoMapper
这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttri ...
- ABP源码分析三十三:ABP.Web
ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在A ...
- ABP源码分析三十四:ABP.Web.Mvc
ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...
- ABP源码分析三十五:ABP中动态WebAPI原理解析
动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...
随机推荐
- C++课程资源下载问题
[来信] 贺老师,您好,我是江西某高校软件学院的一名在校学生.看了您在csdn上公布的博文和视频,我获益良多.不得不承认,之前的大学时光我是荒废了,立即就要大三了,我主攻的是C++方面.因此我悔过自新 ...
- 版本号控制软件:TortoiseSVN高速上手
百度百科对于SVN的一点解释: TortoiseSVN是Subversion版本号控制系统的一个免费开源client,能够超越时间的管理文件和文件夹.文件保存在中央版本号库,除了能记住文件和文件夹的每 ...
- 《Linux设备驱动开发具体解释(第3版)》(即《Linux设备驱动开发具体解释:基于最新的Linux 4.0内核》)网购链接
<Linux设备驱动开发具体解释:基于最新的Linux 4.0内核> china-pub spm=a1z10.3-b.w4011-10017777404.30.kvceXB&i ...
- 【C语言学习】封装和模块化思想
刚学习完C后,做的关于C的课程设计是在一个源文件里放了几百行代码,并且各个功能之间都是相互依赖的,这样就会非常麻烦. 由于当我要改动某个地方的时候,就会牵连着要改动喝多的地方.而在实际的程序设计中.这 ...
- Mongo JavaTest
import com.mongodb.MongoClient; import com.mongodb.DB; import com.mongodb.DBCollection; import com.m ...
- robotframe使用之滚动条
方法一:Excute JavaScript window.scrollTo(0,document.body.scrollHeight); 方法二:Execute javascript document ...
- 栈(C++)
简介: 限定仅在表尾进行插入或删除操作的线性表 表尾端称为栈顶(top),表头端称为栈底(bottom) 特点: 在栈中,后入栈的元素先出栈 C语言版本 用于测试的文件,以及测试结果可以去作者GitH ...
- A和B是好友,他们经常在空闲时间聊天,A的空闲时间为[a1 ,b1 ],[a2 ,b2 ]..[ap ,bp ]。B的空闲时间是[c1 +t,d1 +t]..[cq +t,dq +t],这里t为B的起床时间。这些时间包括了边界点。B的起床时间为[l,r]的一个时刻。若一个起床时间能使两人在任意时刻聊天,那么这个时间就是合适的,问有多少个合适的起床时间?
// ConsoleApplication5.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<vector> ...
- 转:MSN君最后的十个瞬间
五年前我用过MSN五年.在一家ERP公司当程序猿的时候我甚至在业余时间做过一款MSN订餐机器人. 转完这篇文章.就是真正跟MSN的bye bye了. 转自:www.gogo.cn 今天是一个普通的周五 ...
- erlang中通过ip和子网掩码,计算地址范围 【二进制和十进制的转换】
在程序中,难免用的二进制和十进制之间的转换.遇到一个场景,通过ip和子网掩码,计算地址范围. 而地址范围为:网络地址+1—— 广播地址 -1 . 网络地址即ip和子网掩码的与的位运算.广播地址为:网 ...