欢迎光临
我们一直在努力

【Linux系统】15 Ext系列⽂件系统(上)

1、机械磁盘物理结构

机械硬盘 (HDD) 是计算机里唯一的机械设备,属于块外设。特点:大容量、廉价、速度慢,有盘片、磁头、主轴电机。

1.1 磁盘核心部件

  • 盘片 Platter:圆形磁盘薄片,两面都可以存储数据,涂上磁性介质。一张盘片有上下2 个盘面。
  • 主轴马达 Spindle:带动所有盘片高速旋转(7200 转 / 10000 转)。
  • 磁头 Head:读写数据,一个盘面对应一个磁头,所有磁头固定在磁头臂,磁头是共进退的!(重点!!柱面概念的来源)
  • 磁头臂 / 传动臂:移动磁头,径向移动切换磁道。
  • 永磁铁:驱动磁头臂摆动。

📌重点记忆:所有磁头一起移动,不能单独移动某一个磁头,这是柱面概念的根本来源。

1.2 磁盘存储单元:磁道、柱面、扇区

  • 磁道 Track:盘面上一圈一圈同心圆,从外圈 0 号磁道向内圈递增。靠近主轴的磁道不存数据,是磁头停靠区。
  • 扇区 Sector:把磁道切分成扇形小块。扇区是磁盘硬件读写最小单位,固定 512 字节。磁盘和硬件交互,最小粒度就是扇区。
  • 柱面 Cylinder:所有盘片,半径相同的磁道,逻辑上叠在一起形成一个圆柱,叫做柱面。磁头移动一次就定位一个柱面。
  • 磁盘容量计算公式:

    \\(磁盘容量=磁头数 × 柱面数 × 每磁道扇区数 × 每扇区字节数\\)

    1.3 CHS 寻址(柱面‑磁头‑扇区)

    早期磁盘使用 CHS 三维坐标定位扇区:C(柱面号) H(磁头号) S(扇区号)

    • 柱面 C:从 0 开始编号
    • 磁头 H:从 0 开始编号
    • 扇区 S:从 1 开始编号(不是 0!坑点)

    CHS 寻址硬件限制: 8bit 存磁头、10bit 存柱面、6bit 存扇区。 最大支持容量:256 * 1024 *63 *512B ≈8GB,硬盘越来越大,CHS 不够用,于是诞生 LBA 线性寻址。

    1.4 LBA 逻辑块地址(线性地址)

    把磁盘所有扇区,全部展开成一维数组,数组下标就是 LBA 地址,LBA 从 0 开始计数。操作系统只和 LBA 打交道,不再关心物理的柱面磁头。

    CHS ↔ LBA 的转换由磁盘固件(磁盘硬件自己)完成,上层操作系统完全不用管物理 CHS。

    CHS 转 LBA 公式

    \\(LBA = C ×(磁头数 ×每磁道扇区数)+ H ×每磁道扇区数 + (S-1)\\)

    S 要减 1,因为扇区 S 从 1 开始,LBA 从 0 开始。

    LBA 转 CHS 公式

    单个柱面总扇区 = 磁头数 * 每磁道扇区数
    C = LBA // 单个柱面总扇区
    H = (LBA % 单个柱面总扇区) // 每磁道扇区数
    S = (LBA % 每磁道扇区数) + 1

    实操:查看磁盘扇区信息

    fdisk -l

    输出片段示例:

    Disk /dev/vda: 42.9 GB, 42949672960 bytes, 83886080 sectors
    Units = sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    /dev/vda1 2048 83875364 41936658+ 83 Linux

    • 83886080 sectors:磁盘总扇区数量
    • 每个扇区 512 字节;分区起始 LBA=2048。

    💡知识点:扇区是硬件最小读写单元,但操作系统不会按 512 字节读写,太慢!操作系统引入块 (block)。

    2、块 (block):操作系统文件存取最小单位

    扇区:硬件磁盘最小读写单位(512 字节) 块 block:操作系统文件存取最小单位!格式化的时候确定大小,不可修改。

    最常见:4KB 块 = 8 个 512 字节扇区。操作系统一次性读取 8 个连续扇区作为一个块,减少 IO 次数,提升性能。

    关系:

    • 块号 = LBA / 8
    • LBA = 块号 * 8 + 块内扇区偏移

    实操 stat 命令看块大小

    stat main.c

    输出示例:

    File: 'main.c'
    Size: 488 Blocks:8 IO Block:4096 regular file
    Device: fd01h/64769d Inode:1052007 Links:1
    Access: (0664/-rw-rw-r–) Uid:(1000/whb) Gid:(1000/whb)
    Access: 2024-10-25 22:15:52.909616443 +0800
    Modify: 2024-10-17 19:06:11.139423737 +0800
    Change: 2024-10-17 19:06:11.139423737 +0800

    • IO Block:4096:块大小 4KB
    • Blocks:8:该文件占用 8 个 512 字节扇区,合计 4KB。哪怕文件只有 488 字节,也要占一整个块。

    小文件浪费磁盘空间根源:块是分配最小单位,不足一个块也要占用完整块。

    3、分区是什么?

    磁盘可以切割成多个分区,Windows 的 C 盘 D 盘就是分区。

    • 早期分区以柱面为最小划分单元,给定起始柱面、结束柱面。
    • Linux 下分区对应设备文件:/dev/vda1、/dev/sda2。
    • 分区只是划分磁盘空间,分区之后不能直接存文件!必须格式化(写入文件系统)。

    划重点:分区只是空间切割;格式化是往分区里面写入管理数据(超级块、位图、inode 表等)。

    4、inode(索引节点)核心概念

    Linux:文件属性和文件内容分开存储!

    • 文件内容:存放在 Data Block 数据块
    • 文件元属性(权限、属主、大小、时间、数据块指针)存放在 inode 索引节点。

    4.1 inode 里面保存什么?

    ext2_inode 结构体关键字段:

    struct ext2_inode {
    __le16 i_mode; // 文件权限、文件类型
    __le16 i_uid; // 用户id
    __le32 i_size; // 文件字节大小
    __le32 i_atime; // access访问时间
    __le32 i_ctime; // change属性修改时间
    __le32 i_mtime; // modify内容修改时间
    __le32 i_dtime; // 删除时间
    __le16 i_gid; // 组id
    __le16 i_links_count; // 硬链接计数
    __le32 i_blocks; // 文件占用块数量
    __le32 i_block[15]; // 15个块指针!直接/间接块
    };

    📌非常重要细节:

  • 文件名不在 inode 里面!!inode 完全不存文件名!
  • inode 大小固定,一般 128 字节或者 256 字节,不管文件大小,每个 inode 占用空间固定。
  • inode 号是分区内唯一,跨分区 inode 号会重复!
  • 一个文件对应一个 inode;多个文件名可以指向同一个 inode(硬链接)。
  • 实操 ls -li 查看 inode 号

    ls -li

    输出:

    1052669 -rw-rw-r– 1 whb whb 225 Oct 17 19:09 test.txt
    1052007 -rw-rw-r– 1 whb whb 488 Oct 17 19:06 main.c

    第一列数字就是 inode 编号。

    inode 中 15 个块指针 i_block [15]

    #define EXT2_NDIR_BLOCKS 12 // 12个直接块
    #define EXT2_IND_BLOCK 12 // 一级间接块
    #define EXT2_DIND_BLOCK 13 // 二级间接块
    #define EXT2_TIND_BLOCK 14 // 三级间接块
    #define EXT2_N_BLOCKS 15

    • 12 个直接块:直接存数据块编号,小文件全部用直接块,访问速度最快。
    • 一级间接块:块里面不存文件数据,存一堆数据块编号指针。
    • 二级间接块:指针指向一级间接块列表。
    • 三级间接块:指针指向二级间接块列表。

    这样设计,让 ext2 既可以高效处理小文件,又支持超大文件。

    touch 创建新文件发生了什么?

  • 在 inode 位图中找一个空闲 inode,填写文件属性(权限、时间、大小 0)。
  • 在块位图找空闲数据块(如果写入内容才分配)。
  • 在 inode 的 i_block 数组记录数据块编号。
  • 把「文件名 + inode 号」写入当前目录的数据块!!
  • ⚠️目录本身也是文件!目录的内容就是一张表:文件名 <—-> inode编号。 文件名只保存在目录文件里面,inode 完全不知道自己叫什么名字!

    验证:读取目录内容,打印文件名和 inode

    保存为readdir.c

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <dirent.h>
    #include <sys/types.h>
    #include <unistd.h>

    int main(int argc, char *argv[]) {
    if (argc != 2) {
    fprintf(stderr, "Usage: %s <directory>\\n", argv[0]);
    exit(EXIT_FAILURE);
    }
    DIR *dir = opendir(argv[1]);
    if (!dir) {
    perror("opendir");
    exit(EXIT_FAILURE);
    }
    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
    if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
    continue;
    }
    printf("Filename: %-20s Inode: %lu\\n", entry->d_name, (unsigned long)entry->d_ino);
    }
    closedir(dir);
    return 0;
    }

    编译运行:

    gcc readdir.c -o readdir
    ./readdir ./

    输出每个文件名对应的 inode 号,和ls -li第一列完全对应。

    这就实锤:目录文件存储的就是文件名和 inode 映射关系!

    上篇小结

  • 机械磁盘:盘片、磁头、柱面、磁道、扇区。磁头共进退,诞生柱面概念。
  • CHS 三维寻址,早期磁盘使用;LBA 线性一维地址,现在操作系统使用,转换由磁盘固件完成。
  • 扇区 (硬件最小 512B) ≠ 块 (OS 最小 IO,4KB)。
  • 分区只是切割磁盘空间,必须格式化写入文件系统。
  • inode 保存文件全部属性,不存文件名;文件名保存在目录文件的数据块中。
  • inode 内部 15 个块指针(直接、一级、二级、三级间接块)实现文件数据寻址。
  • 访问文件流程:打开目录,根据文件名拿到 inode 号,通过 inode 拿到数据块编号,读取文件内容。
  • 赞(0)
    未经允许不得转载:171主机测评 » 【Linux系统】15 Ext系列⽂件系统(上)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址