博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
话说文件系统——aufs源码分析(三)【转】
阅读量:6842 次
发布时间:2019-06-26

本文共 11030 字,大约阅读时间需要 36 分钟。

本文转载自:

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

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

你可能感兴趣的文章
Java异常架构
查看>>
Git 分支合并冲突及合并分类
查看>>
解决UnicodeEncodeError: 'ascii' codec can't encode characters in position
查看>>
Android开机广播android.intent.action.BOOT_COMPLETED
查看>>
Linux服务器信息收集
查看>>
怎样在 CentOS 7.0 上安装和配置 VNC 服务器
查看>>
学习 SQL 语句 - Select(2): 指定表中的字段
查看>>
iptraf
查看>>
Tomcat JDBC pool源码部析
查看>>
a 伪类在IE6下优先级大于class
查看>>
iOS 导出 ipa 包时 四个选项的意义
查看>>
我的友情链接
查看>>
android 简单解决询问权限问题和apk打包过大问题
查看>>
Android Accessibility学习笔记
查看>>
QEMU用户模式学习笔记
查看>>
两种方法解决mysql主从不同步
查看>>
Lvs+Keepalived+MySQL Cluster架设高可用负载均衡Mysql集群
查看>>
Spring高级应用之注入嵌套Bean
查看>>
mini6410 uboot1.1.6 MMC fat command support
查看>>
系统日志的实践应用
查看>>