最近完成了一个编程作业,大致功能是给定一个文件名,给出该文件所在目录和其本身所占用的簇号等信息。笔者选用了Linux的ext系列文件系统作为实验对象,通过实验对ext2文件系统的存储和索引有了一个较为细致的了解,在这里记录学习到的知识,以备查阅和参考。

   (这里记录的是笔者作业过程中的收获,故而主要细节集中在Linux ext2文件系统的存储布局和如何进行文件数据读取上,其他诸如访问控制等信息则略过)

   (本文的参考来源,更详细的ext2 文件系统的细节文本,包括结构的成员和功能的描述:The Second Extended File System,Internal Layout,这也是帮助笔者完成作业的主要文件,在本文中没有详细介绍的结构、字段等信息,在该文章中均有描述)

 

  目录

  1.概述

  2.基本概念

  3.文件系统概念

    3.1 superblock

    3.2 block

    3.3 block descriptor table

    3.4 inode/block bitmap

    3.5 inode table

    3.6 inode

    3.7 directory

   4. ext2文件系统布局

   5. 文件系统相关操作

    5.1 如何获得 inode/block 号对应的数据

    5.2 如何定位一个文件

    5.3 文件系统的根目录与根目录 /

    5.4 硬链接与符号链接

  6. 参考资料

 

概述

  文件系统是一种对计算机数据的组织和存储的方式,它通过特定的格式对系统上的信息和数据(狭义的理解,可以认为就是目录和文件)进行管理和控制,并方便操作系统在使用时对目标资源进行索引和查找。文件系统在格式化分区(硬盘)时被创建,为操作系统提供独立于磁盘物理结构的逻辑结构。目前用户日常接触的主流的文件系统格式包括 ext系列(Linux),NTFS(Windows) 和 FAT16/32(U盘等),这里介绍的是Linux环境下的ext2文件系统。

  Linux的ext系列文件系统使用superblock结构描述整个文件系统的配置数据,为操作系统使用文件系统提供基本信息。所有的数据均被视为文件进行管理,每个文件有且仅有一个inode结构进行描述,其中存放有与该文件相关的属性、数据存放的block号等控制信息,但文件名却不包含在其中。整个文件系统以block为管理的基本单位,文件的数据均存放在block中。目录被视为文件,拥有自己的inode结构,该文件的block块中存放着称为目录项的结构,每个目录项记录该目录下对应成员的文件名和对应的inode号等信息。为便于索引,将文件系统中的inode和block分成组(group)进行管理。

 

基本概念

  superblock:描述文件系统的基本结构,包含了文件系统的基本配置信息,其中包括该文件系统的inode和block数量、inode和block的固定大小、inode和block的组织方式等信息。

  block:文件系统中进行数据存放的基本单位,每个文件占用整数个block,即不同文件无法将数据存放在同一个block中。block的大小和数量在文件系统格式化时即被确定,常见的block大小有1KB、2KB、4KB和8KB等。

  inode: 文件系统中描述文件的基本结构,每个文件有且仅有一个inode与其对应,inode记录了一个文件除文件数据(存放在block中)和文件名(位于所在目录的数据块记录的目录项中)的所有信息,包括时间相关信息,访问控制信息,文件使用的block块信息等。

  

文件系统结构

superblock

  superblock包含了对一个文件系统的基本配置信息的描述,其总是自文件系统中偏移量为 1024 bytes 处的位置开始,并占据1024bytes的空间

  superblock中记录了与文件系统相关的配置信息,其中与文件访问相关的部分字段的信息如图所示:

 superblock中的偏移(offset)  字段长度(bytes) 字段名称 字段描述
 0  4   s_inodes_count 该文件系统中inode节点的总个数
 4 4  s_blocks_count 该文件系统中block节点的总个数
 24 4  s_log_block_size

若该字段记录值为n(非负),则可通过1024<< n 得到block的大小。

如n=1,则说明一个block大小为1024bytes

 32 4  s_blocks_per_group 记录每个group中block的个数
 40 4 s_inodes_per_group 记录每个group中inode的个数
 88 2 s_inode_size 记录文件系统中inode的固定大小

  完整的superblock结构的介绍可以参见这里。superblock的结构在ext2/3/4中基本是一致的。

  操作系统在读取superblock的数据后,即获得文件系统的具体参数信息,以便进行文件系统操作。Linux系统提供命令供用户查看superblock及其相关信息。

    dumpe2fs [options] 设备文件名
        //输出文件系统的superblock及其块分组信息
    -h    //仅输出文件系统 superblock 的信息

  用户可通过 df 命令获得当前挂载的文件系统的设备文件名,再通过 dumpe2fs -h 设备文件名(如/dev/sda1) 获得对应文件系统的superblock信息。

  用户也可以编写程序,读取原始的二进制磁盘数据,根据上述 superblock 结构字段的偏移和含义对原始数据进行分析,从而获得 superblock 记录的信息。笔者的一个简单实现如下:

Linux文件系统简介——ext2 文件系统
#include <unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<def.h>    //自定义的头文件,存放相关偏移的宏

super_block root_block;

int main(int argc , char *argv[])
{    
    int fd = open( FILE_SYSTEM ,O_RDONLY);

    if(!fd)
    {
        err_msg("failed to read the disk");
    }
    getSuperBlock(fd);

    close(fd);
    return 0;
}

void getSuperBlock( int fd )    //get superblock information
{
    unsigned char super_block_buffer[super_block_size];    //存放superblock的缓冲区

    if( lseek( fd , offset_super_block , SEEK_SET )== -1 )   //设置文件偏移至super_block所在起始位置 
        err_msg("failed to used lseek in superblock reading");

    if( read( fd , super_block_buffer , super_block_size )!= super_block_size ) //读取superblock数据至缓冲区
        err_msg("failed to read superblock");

    root_block.inode_size = parse_num( super_block_buffer + offset_s_inode_size , 2);    //inode size
    root_block.block_size =( (unsigned int)1024 )<< parse_num(super_block_buffer + offset_s_log_block_size , 4 );    //block size

    root_block.num_of_inode = parse_num(super_block_buffer + offset_s_inodes_count , 4 ); //inode number
    root_block.num_of_block = parse_num( super_block_buffer + offset_s_blocks_count , 4); //block number
    
    printf("num of inode:%u \t inode size:%u\n", root_block.num_of_inode  , root_block.inode_size );
    printf("num of blocks:%u \t block_size:%u\n",root_block.num_of_block  , root_block.block_size );
    
    root_block.inode_per_grp = parse_num( super_block_buffer + offset_s_inodes_per_group , 4 ); //inode_per_group
    root_block.block_per_grp = parse_num( super_block_buffer + offset_s_blocks_per_group , 4 ); //block_per_group
    printf("num of inodes per group:%u\n", root_block.inode_per_grp );
    printf("num of blocks per group:%u\n", root_block.block_per_grp );
    
    printf("\n\n");
}

void err_msg( char *msg)        // print error message and exit
{
        fprintf( stderr , "%s\n" , msg );
        exit(0);
}

unsigned int parse_num( unsigned char *temp , int length ) //解释小端法存放的数据
{
        int i;
        unsigned int count = 0;

        for( i = 0 ; i < length ; i++ )
        {
        unsigned int k = temp[i];
        k = k << ( i * 8 );
                count = count + k ;
        }

        return count;
}
main.c

相关文章: