话说文件系统——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的功能, ...
随机推荐
- Solidworks如何保存为网页可以浏览的3D格式
1 如图所示3D装配图,在Solidworks中可以旋转,缩放. 2 我想要另存为在浏览器中可以缩放,旋转的格式.如下所示(我的装配图初步.htm) 3 步骤是,先在Solidworks中出版 ...
- hibernate uuid
- 【Excle数据透透视表】如何删除数据透视表
选中区域A4:C17,在键盘上按DELETE键删除,结果提示: 那么如何删除呢? 解决方案 选中整个数透视表,再删除 具体操作: 选中整个数据透视表→DELETE 注意:删除之后,源数据不会受到影响
- 3、C++新的关键字
C++ 添加了一些全新的关键字. 1.new new 来进行动态内存的分配,而delect 则是进行内存的释放, 申请的方式: 变量申请: int *p = new int; // 申 ...
- Oraclet提交提示Record is locked by another user错误
http://blog.csdn.net/alifel/article/details/4324338下午修改oracle datebase中的字段时,提示"Record is locked ...
- Android微信分享功能实例+demo
Android微信分享功能实例 1 微信开放平台注册 2 获得appId,添加到程序中,并运行程序 3 使用应用签名apk生成签名,添加到微信开放平台应用签名,完成注册 4 测试分享功能. 有问题请留 ...
- Lua学习九----------Lua字符串
© 版权声明:本文为博主原创文章,转载请注明出处 1.Lua字符串 - ''单引号间的一串字符 - ""双引号之间的一串字符 - [[]]之间的一串字符 2.Lua转义字符 3.字 ...
- FTP 连接报错
Filezilla 站点管理器=>选中FTP站点=>加密(只使用普通FTP)
- 【BLE】CC2541之自己定义按键
本篇博文最后改动时间:2017年01月06日,11:06. 一.简单介绍 本文以SimpleBLEPeripheral为例.介绍怎样将普通IO口(P12)自己定义为按键. 注:本文加入按键方法不与协议 ...
- debian jessie install note
Debian支持非常多的硬件,包括arm/mips/ppc/x86,于是想安装个Debian看看,也不想总屈服在canonical的ubuntu下面. 光盘镜像太多了 纯社区版的安装总是没有想像得那么 ...