文件系统实验

实验目的

  1. 掌握文件系统的工作机理,通过一个简单内核源码理解文件系统的主要数据结构。
  2. 学习较为复杂的 Linux 系统下的编程
  3. 了解 EXT2 文件系统的结构

步骤

以下操作都是在 root 用户下进行的,所以不用加 sudo

  1. 解压 filesystem2.tar.gz 文件,得到源文件如下图所示

  2. 直接运行 make,发现报错

  3. 开始解决问题,先在系统中寻找 3.10.0-327.el7.x86_64/build 的链接。

  4. 它指向 /usr/src/kernels/3.10.0-327.el7.x86_64,再去这个路径里面看看。

  5. 这里什么都没有。说明我的红帽系统缺少相应的内核开发包。

  6. 上网搜索相应的 kernel-devel 包,下载,并传送到虚拟机上。

  7. 使用 rpm2cpio 安装该包。

  8. 再尝试 make,这次成功。

  9. 使用 insmod aufs.ko 加载内核模块,发现无法安装。再 dmesg | tail 查看内核 debug 信息。

  10. 方知内核版本不支持,需要重新使用正确的内核版本编译才能通过。内核所需的 rhelversion 为 7.5,而我的系统为 7.2,显然不匹配。

  11. 我选择使用一台新的虚拟机来完成本实验。当然,系统安装结束后,要装载安装光盘镜像,从 Packages 目录中安装内核相关包,gccg++ 也要顺手安装。

  12. 再次 make。警告不影响编译通过。

  13. 再次尝试加载模块,这次就没有错误出现了。

    内核信息也可以看到加载内核的日志消息。super.cstatic int __init aufs_init(void) 得以执行

    解决 module verification failed 的方法是上网搜的,source:(https://blog.csdn.net/lyw13522476337/article/details/79486326);但是在加载 mkfs.aufs 后不需解决这个问题

  14. 直接使用 mount -o loop -t aufs ./image ./dir 挂载该模块,系统会重启。但是在重启后并没有找到刚才挂载上的目录。查阅资料后发现 mount 挂载是临时的,需要修改 /etc/fstab 文件,开机的时候,系统就是根据这个分区来挂载系统的。

    我没有选择修改 /etc/fstab 文件,因为这个操作容易使系统崩溃

  15. 格式化分区(关键的一步)

    1
    ./mkfs.aufs ./image

    然后再 mount,再次挂载系统就不会重启了。

    1
    2
    insmod aufs.ko
    mount -o loop -t aufs ./image ./dir

    此时 dmesg 的输出如下

    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
    [ 2082.575699] loop: module loaded
    [ 2093.191691] create inode cache success
    [ 2093.191698] register filesystem success
    [ 2093.191700] aufs module loaded
    [ 2094.725812] Buffer I/O error on dev loop0, logical block 0, async page read
    [ 2094.725832] Buffer I/O error on dev loop0, logical block 0, async page read
    [ 2094.725839] Buffer I/O error on dev loop0, logical block 0, async page read
    [ 2094.725846] Buffer I/O error on dev loop0, logical block 0, async page read
    [ 2094.725852] Buffer I/O error on dev loop0, logical block 0, async page read
    [ 2094.725859] Buffer I/O error on dev loop0, logical block 0, async page read
    [ 2094.725874] Buffer I/O error on dev loop0, logical block 3, async page read
    [ 2094.756612] aufs_super_block_fill 320017171
    [ 2094.756615] now magic number 320017171
    [ 2094.756617] aufs super block info:
    magic = 320017171
    inode blocks = 1
    block size = 4096
    root inode = 1
    inodes in block = 128
    [ 2094.756621] now device block size 4096
    [ 2094.756633] aufs reads inode 1 from 3 block with offset 32
    [ 2094.756642] aufs inode 1 info:
    size = 0
    block = 4
    blocks = 1
    uid = 0
    gid = 0
    mode = 40755
    [ 2094.756778] aufs_inode_get success
    [ 2094.756782] d_make_root success
    [ 2094.756783] aufs mounted

    从 debug 输出看,aufs 文件系统注册时(insmod 操作时),经历了加载 inode,注册文件系统两个过程。在 mount 的过程中获得了 inode 信息,建立了文件系统根目录,其中为 super block 超级块分配了一个魔法数,super block 大小为 4096,它含有 128 块 inode。第一块 inode 的大小为 0,有 4 块 block。Device 的块大小为 4096,偏移值为 32,从第一块 inode 的 block3 开始读的。Mode 的含义是,这是一个目录(0x40000),所有者拥有全部权限(0x700),用户组可读取,可写入(0x40|0x10),其他用户可读取,可写入(0x40|0x10)。

    可以用 lsmod 命令查看加载的模块列表,刚才的 aufs 在其中。

  16. 刚刚挂载上去的文件系统在桌面上是这样显示的

  17. 卸载模块:rmmod xxx.ko

    Super.cstatic void __exit aufs_fini(void) 得以执行

实验关键里程碑数据与结果

内核源代码结构分析

文件系统各种结构的定义,比如 block、inode、entry

aufs.h
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
struct aufs_disk_super_block {    //磁盘超级块结构定义
__be32 dsb_magic; //魔法数?
__be32 dsb_block_size; //每一块的大小
__be32 dsb_root_inode; //每一个inode的根节点
__be32 dsb_inode_blocks; //每一个超级块有多少块inode节点
};

struct aufs_disk_inode { //inode结构定义
__be32 di_first; //首节点
__be32 di_blocks; //节点块数
__be32 di_size; //节点大小
__be32 di_gid; //节点组号
__be32 di_uid; //节点唯一编号
__be32 di_mode; //节点模式和访问权限
__be64 di_ctime; //节点创建时间
};
struct aufs_disk_dir_entry { //磁盘目录对应表定义
char dde_name[AUFS_DDE_MAX_NAME_LEN]; //磁盘名字
__be32 dde_inode; //对应了哪个节点
};

struct aufs_super_block { //aufs超级块定义
unsigned long asb_magic; //魔法数?
unsigned long asb_inode_blocks; //已使用(?)inode数量
unsigned long asb_block_size; //块的大小
unsigned long asb_root_inode; //根inode
unsigned long asb_inodes_in_block; //总共多少块inode
};
......

还定义了 inode 的获取、分配和删除操作。

aufs 模块定义,内核版本定义等

aufs.mod.c
MODULE_INFO(srcversion, "7E6AB09FC99FF944E10E236");
MODULE_INFO(rhelversion, "7.5");

文件系统目录操作定义和实现,目录管理功能

定义设备操作 device_opendevice_releasedevice_readdevice_write 及其实现

文件结点操作

模块的装载与卸载

实验难点与收获

内核的安装比普通软件的安装要更加困难,它需要系统内核版本的匹配和更多的命令。这个实验给了一个 7.5 的红帽版本的 ko 内核,一开始并没有意识到版本问题,编译、挂载总是失败。后来解决了版本问题,但是挂载后系统会自动重启,而 Linux 重启后所有的挂载均会消失,很坑。

一步关键的动作是使用 mkfs.aufs 来格式化所给定的 image 文件。

观看内核源代码,发现它十分复杂,很难一次读通,对我来说是个极大的挑战。

实验思考

能不能照着这个给定的模块结构,自己写一个 Linux 内核模块呢?