zoukankan      html  css  js  c++  java
  • ch11-EXT2文件系统

    知识点归纳

    EXT2即第二代扩展文件系统(英语:second extended filesystem,缩写为 EXT2)

    Linux一直使用EXT2作为默认文件系统,EXT3是EXT2的扩展,增加的主要内容是日志文件

    Block#0 是引导块,文件系统不会使用它,它用于容纳从磁盘引导操作系统的引导程序

    Block#1 是超级块,用于容纳关于整个文件系统的信息

    Block#2 是块组描述符,EXT2将磁盘块分成几个组,每组有8192个块,每组用一个块组描述符结构体描述

    Block#8 是块位图,用于表示某种项的位序列,例如磁盘块或索引节点,位图用于分配和回收项。

    Block#9 是索引节点位图,索引节点用于代表一个文件的数据结构

    Block#10 是索引(开始)节点块,索引节点大小用于平均分割块大小,所以每个索引节点块都包含整数个索引节点。

    数据块 是紧跟在索引节点块后面的文件存储块。

    inode结构体

    struct ext2_inode{
    	u16 i_mode;	        //16 bits = |tttt | ugs |rwx | rwx | rwx |
    	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-1ink count
    	u32 i_b1ocks;		//number of 512-byte sectors
    	u32 i_f1ags;		//IGNORE
    	u32 i_reserved1;	//IGNORE
    	u32 i_block [15];	//See details below
    	u32 i_pad[7];		//for inode size = 128 bytes
    }
    

    i_mode

    i_block[15]数组存了15个指针
    i_block[0-11]指向12个直接磁盘块
    i_block[12]是间接块,指向一个磁盘块,该磁盘块包含256个块编号,即能映射256个磁盘块
    i_block[13]是双重间接块,即双重映射,1 --> 256 * 256
    i_block[14]是三重间接块,即三重映射,1 --> 256 * 256 * 256

    dentry结构体

    struct ext2_dir_entry_2{
    	u32  inode;		 	//inode number; count from 1,NOT o
    	u16  rec_len;		        //this entry's length in bytes
    	u8	 name_len;	  	//name length in bytes
    	u8	 fi1e_type;		//not used
    	char name[EXT2_NAME_LEN] ;      // name:1-255 chars,no ending NULL
    };
    
    

    邮差算法:类似二维数组的存储和访问

    遍历算法:

    (1)读取超级块。检查幻数s_magic ( OxEF53),验证它确实是EXT2FS。

    (2)读取块组描述符块(1+s_first_data_block),以访问组0描述符。从块组描述符的bg_inode_table条目中找到索引节点的起始块编号,并将其称为InodesBeginBlock 。

    (3)读取 InodeBeginBlock,获取/的索引节点,即INODE #2。

    (4)将路径名标记为组件字符串,假设组件数量为n。
    例如,如果路径名=/a/b/c,则组件字符串是“a”"b”“c”,其中n =3。用name[0],name[1],…,name[n-1]来表示组件。

    (5)

    从(3)中的根索引节点开始,在其数据块中搜索name[0]。
    为简单起见,我们可以假设某个目录中的条目数量很少,因此一个目录索引节点只有12个直接数据块。
    有了这个假设,就可以在12个(非零)直接块中搜索name[0]。目录索引节点的每个数据块都包含以下形式的dir_entry结构体:
    [ino rec_len name_len NANE] [ino rec_len name_len NAME]
    其中NAME是一系列nlen字符,不含终止NULL。
    对于每个数据块,将该块读入内存并使用dir_entry *dp指向加载的数据块。
    然后使用name_len将NAME提取为字符串,并与name[0]进行比较。如果它们不匹配,则通过以下代码转到下一个dir_entry:
    dp = (dir_entry *) ((char *)dp + dp->rec_len);
    继续搜索。如果存在name[0],则可以找到它的dir_entry,从而找到它的索引节点号。

    (6)

    使用索引节点号ino来定位相应的索引节点。回想前面的内容,ino从1开始计数。使用邮差算法计算包含索引节点的磁盘块及其在该块中的偏移量。
    blk=(ino - 1) / INODES_PER_BLOCK+ InodesBeginBlock;
    offset = (ino - 1)% INODES_PER_BLOCK;
    然后在索引节点中读取/a,从中确定它是否是一个目录(DIR)。
    如果/a不是目录,则不能有/a/b,因此搜索失败。
    如果它是目录,并且有更多需要搜索的组件,那么继续搜索下一个组件name[1]。
    现在的问题是:在索引节点中搜索/a的name[1],与第(5)步完全相同。

    (7)
    由于(5)~(6)步将会重复n次,所以最好编写一个搜索函数:
    u32 search ( INODE*inodePtr, char *name)
    然后只需调用n次search函数
    如果搜索循环成功结束,ip必须指向路径名的索引节点。

    文件系统的结构

    文件系统的级别

    文件系统的实现分为三个级别。每个级别处理文件系统的不同部分。这使得实现过程模块化,更容易理解。
    第1级别实现了基本文件系统树。用户命令程序有:mkdir,creat,mknod,rmdir,link,unlink,symlink,rm,ls,cd和pwd
    第2级别实现了文件内容读写函数
    第3级别实现了文件系统的挂载、卸载和文件保护

    基本文件系统

    • type.h文件
      这类文件包含EXT2文件系统的数据结构类型,比如超块、组描述符、索引节点和目录条目结构。
      此外,它还包含打开文件表、挂载表、PROC结构体和文件系统常数。
    • global.c文件
      这类文件包含文件系统的全局变量。全局变量的例子有:
      MINODE minode [NMINODE]; //in memory INODEs
      MTABLE mtable[NMTABLE]; //mount tables
      OFT oft[NOFT]; //Opened file instance
      PROC proc [NPROC] //PRoC structures
      PROC *running; //current executing PROC
    • util.c文件
      该文件包含文件系统常用的实用程序函数。最重要的实用程序函数是读/写磁盘块函数iget()、iput()和 getino()。
    • mount-root.c文件
      该文件包含mount_root()函数,在系统初始化期间调用该函数来挂载根文件系统。它读取根设备的超级块,以验证该设备是否为有效的EXT2文件系统。
      然后,它将根设备的根 INODE ( ino = 2)加载到minode中,并将根指针设置为根minode。它还将所有进程的当前工作目录设置为根minode。
      分配一个挂载表条目来记录挂载的根文件系统。根设备的一些关键信息,如 inode和块的数量、位图的起始块和inode,表,也记录在挂载表中,以便快速访问。

    问题与解决思路

    时间戳转换为任意可读的日期格式

    unix时间戳的数据类型,是32位(4字节)的无符号数,可以表示的大致时间范围是1970-2038

    为了避免"时间回归"bug,有的地方已经开始用64位(8字节)的无符号数,来表示系统时间

    <time.h>

    time_t

    通过sizeof,在本人电脑上是8字节,long是4字节,long long 是8字节。

    获取unix时间戳
    time_t time(time_t *seconds)

    time_t curtime;
    curtime = time(NULL);
    or
    time(curtime)
    printf("%ld
    ",curtime);
    

    char *ctime(const time_t *timer)
    该函数返回一个字符串,包含了可读格式的日期和时间信息。
    注意传入参数是指针。
    格式固定,英文输出,星期,月份,日期,时:分:秒,年份
    字符串结尾自带 ' ' ,不够灵活


    struct tm *localtime(const time_t *timer)
    传入指针,返回结构体指针

    struct tm {
       int tm_sec;         /* 秒,范围从 0 到 59                */
       int tm_min;         /* 分,范围从 0 到 59                */
       int tm_hour;        /* 小时,范围从 0 到 23                */
       int tm_mday;        /* 一月中的第几天,范围从 1 到 31                    */
       int tm_mon;         /* 月份,范围从 0 到 11                */
       int tm_year;        /* 自 1900 起的年数                */
       int tm_wday;        /* 一周中的第几天,范围从 0 到 6                */
       int tm_yday;        /* 一年中的第几天,范围从 0 到 365                    */
       int tm_isdst;       /* 夏令时                        */    
    };
    

    虽然可以自行输出结构体里的成员,达到灵活输出日期的效果,但是time.h还有个格式化输出神器。


    size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
    format如下:它们是区分大小写的
    %a 星期几的简写
    %A 星期几的全称
    %b 月分的简写
    %B 月份的全称
    %c 标准的日期的时间串
    %C 年份的后两位数字
    %d 十进制表示的每月的第几天
    %D 月/天/年
    %e 在两字符域中,十进制表示的每月的第几天
    %F 年-月-日
    %g 年份的后两位数字,使用基于周的年
    %G 年分,使用基于周的年
    %h 简写的月份名
    %H 24小时制的小时
    %I 12小时制的小时
    %j 十进制表示的每年的第几天
    %m 十进制表示的月份
    %M 十时制表示的分钟数
    %n 新行符
    %p 本地的AM或PM的等价显示
    %r 12小时的时间
    %R 显示小时和分钟:hh:mm
    %S 十进制的秒数
    %t 水平制表符
    %T 显示时分秒:hh:mm:ss
    %u 每周的第几天,星期一为第一天 (值从0到6,星期一为0)
    %U 第年的第几周,把星期日做为第一天(值从0到53)
    %V 每年的第几周,使用基于周的年
    %w 十进制表示的星期几(值从0到6,星期天为0)
    %W 每年的第几周,把星期一做为第一天(值从0到53)
    %x 标准的日期串
    %X 标准的时间串
    %y 不带世纪的十进制年份(值从0到99)
    %Y 带世纪部分的十进制年份
    %z,%Z 时区名称,如果不能得到时区名称则返回空字符。
    %% 百分号

    void format_time(time_t arg){
        char buf[64];
        struct tm *ptm = localtime(&arg);
        strftime(buf,64,"%F %T",ptm);
        printf("%s
    ",buf);
      }
    想要灵活输出,修改format即可
    "%F %R" 2021-10-17 17:15
    "%F %T" 2021-10-17 17:15:10
    "%Y年%m月%e日 %H:%M:%S" 2021年10月17日 17:15:10
    

    实践内容

    显示超级块

    首先创建一个包含简单EXT2文件系统的虚拟磁盘

    dd if=/dev/zero of=mydisk bs=1024 count=1440
    mke2fs -b 1024 mydisk 1440
    
    

    superblock.c

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <fcntl.h>
      4 #include <sys/types.h>
      5 #include <unistd.h>
      6 #include <ext2fs/ext2_fs.h>
      7 #include <time.h>
      8 //typedef u8,u16,u32 SUPER for convenience
      9 typedef unsigned char  u8;
     10 typedef unsigned short u16;
     11 typedef unsigned int   u32 ;
     12 typedef struct ext2_super_block SUPER;
     13 
     14 SUPER *sp;
     15 char buf[1024];
     16 int fd,blksize,inodesize;
     17 int print (char *s,u32 x){
     18         printf ( "%-30s = %8d
    " ,s,x);
     19 }
     20 
     21 int super (char *device)
     22 {
     23         fd = open(device, O_RDONLY);
     24         if(fd < 0 ){
     25                 printf ( " open %sfailed
    " , device);
     26                 exit(1);
     27         }
     28         lseek(fd,(long)1024*1,0);               //block 1 on PD,offset 1024 on HD
     29         read (fd,buf,1024);
     30         sp = (SUPER*)buf;                               //as a super b1ock structure
     31         
     32         //check EXT2 Fs magic number :
     33         printf ("%-30s = %8x ","s_magic",sp->s_magic);
     34         if(sp->s_magic != 0xEF53){
     35                 printf ( "NOT an EXT2 FS
    " );
     36                 exit(2);
     37         }
     38         printf( "EXT2 FS OK
    " ) ;
     39         print( "s_inodes__count" ,sp->s_inodes_count);
     40         print( "s_blocks_count" ,sp->s_blocks_count) ;
     41         print ("s_r_blocks_count" ,sp->s_r_blocks_count);
     42         print("s_free_inodes_count" , sp->s_free_inodes_count);
     43         print ("s_free_blocks_count" , sp->s_free_blocks_count);
     44         print ( "s_first_data_block" ,sp->s_first_data_block);
     45         print ( "s_log_block_size" ,sp->s_log_block_size);
     46         print ( "s_blocks_per_group", sp->s_blocks_per_group);
     47         print ( "s_inodes _per_group",sp->s_inodes_per_group);
     48         print ( "s__mnt_count" ,sp->s_mnt_count) ;
     49         print ( "s_max_mnt_count" ,sp->s_max_mnt_count);
     50         printf ( "%-30s = %8x
    " , "s_magic" , sp->s_magic);
     51         printf ( "s__mtime = %s" , ctime ( &sp->s_mtime) ) ;
     52         printf( "s_wtime =%s" , ctime ( &sp->s_wtime));
     53         blksize = 1024 * ( 1 <<sp->s_log_block_size);
     54         printf( "block size = %d
    ", blksize) ;
     55         printf ( "inode size = %d
    " ,sp->s_inode_size);
     56 }
     57 
     58 char *device = "mydisk" ;               //default device name
     59 int main(int argc, char *argv[])
     60 {
     61         if(argc>1){
     62                 device = argv[1];
     63         }
     64         super(device);
     65 }
     66 
    
    

    显示索引节点位图(imap)

    imap.c

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <fcntl.h>
      4 #include <ext2fs/ext2_fs.h>
      5 #include <unistd.h>
      6 #include <sys/types.h>
      7 typedef unsigned char  u8;
      8 typedef unsigned short u16;
      9 typedef unsigned int   u32 ;
     10 typedef struct ext2_super_block SUPER;
     11 typedef struct ext2_group_desc GD;
     12 #define BLKSIZE 1024
     13 
     14 SUPER *sp;
     15 GD *gp;
     16 char buf[BLKSIZE];
     17 int fd;
     18 
     19 //get_block ( ) reads a disk block into a buf[ ]
     20 
     21 int get_block(int fd, int blk,char *buf){
     22     lseek (fd,(long)blk*BLKSIZE,SEEK_SET);
     23     return read(fd,buf,BLKSIZE);
     24 }
     25 
     26 int imap(char *device){
     27     int i, ninodes, blksize,imapblk;
     28     fd = open (device,O_RDONLY) ;
     29     if (fd < 0 ){
     30         printf ( " open %s failed
    " , device);
     31         exit (1);
     32     }   
     33     get_block (fd,1,buf) ;              //get superblock
     34     sp = (SUPER*) buf;                  
     35     //check magic number to ensure it's an EXT2 FS
     36     ninodes = sp->s_inodes_count;       //get inodes_count
     37     printf ( "number of inodes = %d
    " , ninodes) ;
     38     get_block(fd,2,buf);                //get group descriptor
     39     gp = (GD*)buf;
     40     imapblk = gp->bg_inode_bitmap;      //get imap block number
     41     printf ( "imap blk number = %d
    " , imapblk) ;
     42     get_block(fd,imapblk,buf);          //get imap block into buf[ ]
     43     for (i=0; i<=ninodes/8; i++){       //print each byte in HEX
     44         printf ("%02x ", (u8)buf[i]);
     45     }
     46     printf ("
    ");
     47 }
     48 
     49 int main(int argc,char *argv[]){
     50     char *dev= "mydisk" ;               //default device
     51     if(argc>1){
     52         dev = argv[1];
     53     }
     54     imap(dev);
     55 }
    
    
    

  • 相关阅读:
    |,&,<<,>>运算符
    Unity 异步加载场景
    string字母排序,
    冒泡算法
    Direct3D 12 编程---(1)
    点云密度粗估计
    git工具使用
    opencv---灰度图像与彩色图像遍历
    求平面两直线的交点,两直线段的交点
    结构体重载运算符
  • 原文地址:https://www.cnblogs.com/cfqlovem-521/p/15414979.html
Copyright © 2011-2022 走看看