Linux文件系統之ext2
一
- 首先對於Linux系統來說,如圖所示,系統層級分為用戶層、內核層、以及物理設備層、
- 例如C語言中對於文件的寫操作,首先C語言本身會設置緩沖區,來提高讀寫效率。C寫函數會調用linux的系統函數接口write(),這是在用戶態下。緊接著write()會調用linux系統內核函數進入到內核層進行寫入,這個寫操作顯然是由文件系統來控制的,而內核中依舊還有一個高速緩沖設備來提高讀寫效率,之後通過設備驅動寫入磁盤中,簡單來說從調用C讀寫函數到將數據操作到磁盤上是這樣的流程。
二
- 當我們了解了Linux文件系統所處的位置之後,接下來說明一些基本概念
1)扇區:扇區並不是一個物理概念,而是一個單位,大小是521Byte
2)塊:是一個Linux文件系統的基本單元,大小是4096Byte
3)塊組:ext2文件系統是以塊組為基本單元
4)Inode:存儲文件基本信息(除文件名和文件類型),一個文件對應一個Inode;在ext2文件系統中,大小是128Byte
- 接下來開始解釋ext2文件系統,如圖所示,ext2采用一下的基本結構來管理數據存儲,每一個Block Group為一個基本單元,了解基本單元後就了解了文件系統
Boot Block:啟動塊,大小是1KB;存儲磁盤分區信息和啟動信息;PS:一個文件系統只有1份
Super Block:超級塊,大小是1塊;為了提高系統的健壯性,每一個塊組都有一個(ext4采用稀疏拷貝),並且每份內容一致;用來描述整個分區的文件系統信息;
例如塊大小、文件系統版本號、上次mount的時間等等
GDT:塊組描述符表,大小是多個塊,塊數不確定;由很多塊組描述符組成,整個分區分成多少個塊組就對應有多少個塊組描述符。
每個塊組描述符(Group Descriptor)存儲一個塊組的描述信息,例如在這個塊組中從哪裏開始是inode表,從哪裏開始是數據塊,
空閑的inode和數據塊還有多少個等等。和超級塊類似,塊組描述符表在每個塊組的開頭也都有一份拷貝,這些信息是非常重要的,
一旦超級塊意外損壞就會丟失整個分區的數據,一旦塊組描述符意外損壞就會丟失整個塊組的數據,因此它們都有多份拷貝。
通常內核只用到第0個塊組中的拷貝,當執行e2fsck檢查文件系統一致性時,第0個塊組中的超級塊和塊組描述符表就會拷貝到其它塊組
,這樣當第0個塊組的開頭意外損壞時就可以用其它拷貝來恢復,從而減少損失。
Block Bitmap:塊位圖,原理和Bitmap算法一致(用每位來表示數據);標誌每個塊的使用情況(0沒被使用,1被使用)一個塊組中的塊是這樣利用的:
數據塊存儲所有文件的數據,比如某個分區的塊大 小是1024字節,某個文件是2049字節,那麽就需要三個數據塊來存,
即使第三個塊只存了一個字節也需要 占用一個整塊;超級塊、塊組描述符表、塊位圖、inode位圖、inode表這幾部分存儲該塊組的描述信息。 那麽如何知道哪些塊已經用來存儲文件數據或其它描述信息,哪些塊仍然空閑可用呢?
塊位圖就是用來描述整個塊組中哪些塊已用哪些塊空閑的,它本身占一個塊,其中的每個bit代表本塊組中的一個塊,這個bit為 1表示該塊已用,這 個bit為0表示該塊空閑可用。
Inode Bitmap:和塊位圖類似,本身占用一個塊;其中每一位表示一個inode是否可用;
Inode Table:存儲Inode的表,inode表占多少個塊在格式化時就要決定並寫入塊組描述符中,mke2fs格式化工具的默認策略是
一個塊組有多少個8KB就分配多少個inode。
Data Blocks:存儲數據
三
- 接下來說明一下Inode結構
如圖所示,一個數據指針指針指向一個數據塊,後三個多級指針為了拓展數據塊
四 文件放入flow
1. 先找GDT,查看InodeTable所在位置
2. 查找Table裏未被使用的最小值分配給文件使用,
3. Inode Bitmap對應位置由0置1
4. Inode存放文件信息,更新Table
註:1)文件系統很復雜,以上只是放入文件大致flow,實際系統還有空閑檢測、動態分配等
2)文件刪除只是將Inode BItma由1置0,更新block bimap 更行GDT,所以文件並沒有真正被刪除
五 目錄結構
一個目錄占一個塊或多個塊,目錄塊內容如下:
註:1. 符號連接是新建一個記錄項,指向次文件記錄項
2. 硬鏈接是新建一個記錄項,指向此文件
附:遞歸列出目錄中的文件列表
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#define MAX_PATH 1024
/* dirwalk: apply fcn to all files in dir */
void dirwalk(char *dir, void (*fcn)(char *)) {
char name[MAX_PATH];
struct dirent *dp; DIR *dfd;
if ((dfd = opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can‘t open %s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL) {
if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
continue; /* skip self and parent */
if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name))
fprintf(stderr, "dirwalk: name %s %s too long\n", dir, dp->d_name);
else {
sprintf(name, "%s/%s", dir, dp->d_name);
(*fcn)(name);
}
}
closedir(dfd);
}
/* fsize: print the size and name of file "name" */
void fsize(char *name) {
struct stat stbuf;
if (stat(name, &stbuf) == -1) {
fprintf(stderr, "fsize: can‘t access %s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk(name, fsize);
printf("%8ld %s\n", stbuf.st_size, name);
}
int main(int argc, char **argv) {
if (argc == 1) /* default: current directory */
fsize(".");
else
while (--argc > 0)
fsize(*++argv);
return 0;
}
Linux文件系統之ext2