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的块。
虚拟磁盘布局
一个磁盘可以分成多个分区,每个分区必须先用格式化工具格式化成某种格式的文件系统,才能存储文件,在格式化的过程中会在磁盘上写一些管理存储布局的信息。
- 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
}
查看分区挂载
查看超级块
邮差算法
在计算机系统中,经常出现下面这个问题。一个城市有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;
}
rmdir的系统调用实现
#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;
}
问题与解决方案
- 系统调用的实现,往往找不到具体可用的函数。
解决方案:通过老师上课讲的man -k的使用,首先找到有哪些可以用的系统调用函数,然后根据具体功能实现,编写伪代码,写清具体实现方案,然后可以实现系统调用编写功能函数。
- 本章主要讲的是EXT2文件系统,比较难实现,很难有具体实践性理解。
解决过程:通过大量的从网上查资料,在超级块等,我找到了实现实践的方法,利用df等等,具体的实践来深入学习。
思考总结
答:主要还是需要多实践,尤其是系统调用实现系统中功能时,只有真正把代码思路想出来,再把代码仔细敲一遍,才对系统调用有非常深入的理解;同时,我明白有非常好的氛围、讨论能够解决非常多的问题,促进学习,下一步还是要多努力,多实践。