20201317 LYX 第11章学习总结

第11章 EXT2文件系统

什么是EXT2文件系统?

Linux一直使用EXT2作为默认文件系统。

EXT3是EXT2的扩展。EXT3中增加的内容是一个日志文件,将文件系统中的变更记录在日志中,日志可以在系统崩溃时更快的从错误中恢复。

磁盘的基本概念:
扇区为最小的物理存储单位,每个扇区为512字节。
将扇区组成一个圆,那就是柱面,柱面是分区的最小单位。
第一个扇区很重要,里面有硬盘主引导记录及分区表,其中MBR占有446字节,分区表占有64字节。

通过mkfs创建虚拟磁盘

在Linux下,命令mk2es

在设备上创建一个带有nblock个块和ninodes个索引节点的EXT2文件系统。设备可以是真实设备,也可以是虚拟磁盘文件。如果未指定blksize,则默认块大小为1KB.

下面的命令为可在一个名为vdisk的虚拟磁盘文件上创建一个EXT2文件系统,有1440个大小为1KB的块。

image-20221002142054281

虚拟磁盘布局

这里写图片描述

一个磁盘可以分成多个分区,每个分区必须先用格式化工具格式化成某种格式的文件系统,才能存储文件,在格式化的过程中会在磁盘上写一些管理存储布局的信息。

  • Block#0:
    引导块,文件系统不会使用它。它用于容纳从磁盘引导操作系统的引导程序。
  • Block#1:
    超级块(在硬盘分区中字节偏移量为1024)。用于容纳关于整个文件系统的信息。
    超级块中一些重要字段
struct et2_super block {
  u32 s_inodes_count;        /* Inodes count */
  u32 s_blocks_count;        /* Blocks count */
  u32 s_r_blocks_count;      /* Reserved blocks count */
  u32 s_free blocks_count;   /* Free blocks count */
  u32 s_free_inodes_count;   /* Free inodes count */
  u32 s_first_data_block;    /* First Data Block */
  u32 s_log block_size;      /* Block size */
  u32 s_log_cluster_size;    /* Al1ocation cluster size */
  u32 s_blocks per_group;    /* # Blocks per group * /
  u32 s_clusters per_group;  /* # Fragments per group */
  u32 s_inodes_per_group;    /* # Inodes per group * /
  u32s_mtime;                /* Mount time * /
  u32s_wtime;                /* write time */
  u16s_mnt_count;            /* Mount coune* /
  s16 s_max_ntcount;         /* Maximal mount count */
  u16 B_magic;               /* Magic signature */
  //more non-essential fields
  u16 s_inode_size;          /* size of inode structure*/
}


s_first_data_block:0表示4KB块大小,1表示1KB块大小。它用于确定块组描述符的起始块,即s_first_data_block +1。
s_log_block_size确定文件块大小,为1KB*(2**s_log_block_size),例如0表示 1KB块大小,1表示2KB块大小,2表示4KB块大小,等等。最常用的块大小是用于小文件系统的1KB和用于大文件系统的4KB。
s_mnt_count:已挂载文件系统的次数。当挂载计数达到max_mount_count时,fsck会话将被迫检查文件系统的一致性。
s_magic是标识文件系统类型的幻数。EXT2/3/4文件系统的幻数是OxEF53。

Block#2
块组描述符(硬盘上的s_first_data_blocks-1)
EXT2将磁盘块分成几个组,每个组有8192个块(硬盘上的大小为32K)

struct ext2_group_dese {
  u32 bg_b1ock_bitmap; //Bmap bloak number
  u32 bg_inode_bitmap; //Imap block number
  u32 bg_inode_table;  //Inodes begin block number
  u16 bg_free_blocks_count; //THESE are OBVIOUS
  u16 bg_free_inodes_count;
  u16 bg_used_dirs_count;
  u16 bg_pad; // ignore these
  u32 bg_reserved[3];
};

由于一个软盘只有1440个块,B2只包含一个块组描述符。其余的都是0。在有大量块组的硬盘上,块组描述符可以跨越多个块。块组描述符中最重要的字段是bg_block_bitmap.bg_inode_bitmap和 bg_inode_table,它们分别指向块组的块位图、索引节点位图和索引节点起始块。对于Linux格式的EXT2文件系统,保留了块3到块7。所以,bmap=8,imap=9,inode_table= 10。

  • Block #8 块位图(Bmap)
    用来表示某种项的位序列。0表示对应项处于FREE状态,1表示处于IN_USE状态。1个软盘有1440个块,但Block#0未被文件系统使用,所以对应位图只有1439个有效位,无效位视作IN_USE处理,设置为1.
  • Block #9 索引节点位图(Imap)
    一个索引节点就是用来代表一个文件的数据结构。EXT2文件系统是使用有限数量的索引节点创建的。各索引节点的状态用B9 中 Imap中的一个位表示。在EXT2 FS 中,前10个索引节点是预留的。所以,空EXT2FS的Imap 以10个1开头,然后是0。无效位再次设置为1。
  • Block #10 索引(开始)节点块(bg_inode_table)
    每个文件都用一个128字节(EXT4中的是256字节)的独特索引节点结构体表示。
struct ext2_inode{
  
u16 i_mode;
// 16 bits =|tttt|ugs|rwx|rwx|rwxl 
  u16 i_uid;
// owner uid 
  u32 i_size;
// file size in bytes 
  
u32 i_atime;// time fields in seconds
  u32 i_ctime;
// since 00:00:00,1-1-1970
  u32 i_mtime;
  u32 i_dtime;
  u16 i_gid;
// group ID 
  u16 i_links_count;// hard-link count 

  
u32 i_blocks;// number of 512-byte sectors
  
u32 i_flags;// IGNORE

  u32 i_reserved1;
// IGNORE
  u32 i_block[15];// See details below 
  u32 i_pad[7];
// for inode size = 128 bytes

}

查看分区挂载

image-20221002143718777

查看超级块

image-20221002144110491

邮差算法

在计算机系统中,经常出现下面这个问题。一个城市有M个街区,编号从0到M-1。每个街区有N座房子,编号从0到N-1。每座房子有一个唯一的街区地址,用(街区,房子)表示,其中0≤街区<M,0≤房子<N。来自外太空的外星人可能不熟悉地球上的街区寻址方案,倾向于采用线性方法将这些房子地址编为0,1,…,N-1,N,N+1等。已知某个街区地址 BA =(街区,房子),怎么把它转换为线性地址 LA,反过来,已知线性地址,怎么把它转换为街区地址?如果都从0开始计数,转换就会非常简单。

Linear_address LA = N*block + house;
Block_address BA = (LA / N, LA % N);

只有都从0开始计数时,转换才有效。如果有些条目不是从0开始计数的,则不能直接在转换公式中使用。

邮差算法的几种应用。

1. C语言中的 Test-Set-Clear 位

2. 将索引节点号转换为磁盘上的索引节点

基本文件系统函数的实现

mkdir的系统调用实现

#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
//核心是分割字符串

int main(int argc,char *argv[])
{
    argv[1];
    //对argv[1]进行分割
    char * p =  strtok(argv[1],"/");
    int erro = 0;
    char * path = (char *)malloc(256);
    strcpy(path,p);
    printf("%s\n",path);
    if(access(path,F_OK) != 0)
    {
        erro = mkdir(path ,0777);
        if(erro == -1)
        {
            perror("mkdir");
            return -1;
        }
    }
    while(1)
    {
        p = strtok(NULL,"/");
        if(p == NULL)
        {
            break ;
        }
        sprintf(path,"%s/%s",path,p);
        printf("%s\n",path);
        if(access(path,F_OK) == 0)
        {
            //存在这个目录,不需要创建。
        }
        else
        {
            erro = mkdir(path ,0777);
            if(erro == -1)
            {
                perror("mkdir");
                return -1;
            }
        }
    }
    return 0;
}

image-20221002150616368

image-20221002150756006

rmdir的系统调用实现

image-20221002150915347

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
int main(int argc,char* argv[])
{
  if(argc != 2)
   printf("Usage: %s dirname",basename(argv[1])),exit(-1);
  if(rmdir(argv[1]) != 0)
   perror("rmdir"),exit(-1);
 
  printf("rmdir %s success!\n",argv[1]);
 
  return 0;
} 

image-20221002151524479

image-20221002151629494

问题与解决方案

  1. 系统调用的实现,往往找不到具体可用的函数。

解决方案:通过老师上课讲的man -k的使用,首先找到有哪些可以用的系统调用函数,然后根据具体功能实现,编写伪代码,写清具体实现方案,然后可以实现系统调用编写功能函数。

  1. 本章主要讲的是EXT2文件系统,比较难实现,很难有具体实践性理解。

解决过程:通过大量的从网上查资料,在超级块等,我找到了实现实践的方法,利用df等等,具体的实践来深入学习。

思考总结

答:主要还是需要多实践,尤其是系统调用实现系统中功能时,只有真正把代码思路想出来,再把代码仔细敲一遍,才对系统调用有非常深入的理解;同时,我明白有非常好的氛围、讨论能够解决非常多的问题,促进学习,下一步还是要多努力,多实践。

posted @ 2022-10-02 15:36  B1smarck  阅读(27)  评论(0编辑  收藏  举报