2021-2022-1-diocs-EXT2檔案系統第六週學習筆記
20191205 2021-2022-1-diocs-EXT2檔案系統(第六週學習筆記)
一、任務詳情
自學教材第11章,提交學習筆記(10分)
知識點歸納以及自己最有收穫的內容 (3分)
問題與解決思路(2分)
實踐內容與截圖,程式碼連結(3分)
...(知識的結構化,知識的完整性等,提交markdown文件,使用openeuler系統等)(2分)
二、教材內容歸納整理
本章討論了 EXT2檔案系統。本章首先描述了EXT2檔案系統在Linux 中的歷史地位以及 EXT3/EXT4檔案系統的當前狀況;用程式設計示例展示了各種EXT2資料結構以及如何遍歷 EXT2檔案系統樹;介紹瞭如何實現支援Linux 核心中所有檔案操作的
思維導圖
一、知識點總結
1.通過mkfs建立虛擬磁碟
在 Linux下,命令
mke2fs [-b blksize -N ninodes] device nblocks
在裝置上建立一個帶有
dd if=/dev/zero of=vdisk bs=1024 count=1440
mke2fs vdisk 1440
可在一個名為vdisk的虛擬磁碟檔案上建立一個EXT2檔案系統,有1440個大小為1KB 的塊。
2.超級塊
Block#1
struct ext2_guper_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_blook; /* First Data Block */ u32 s_log_block_size; /* Block size */ u32 s_log_cluster_size; /* Allocation 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*/ u32 s_mtime;u32 s_wtime; /* Mount time */ u16 s_mnt_count; /* Write time */ s16 s_max_mnt_count; /*_Mount_count*/L u16 8_magic; /* Magic signature */ // more non-essential fields u16 s_inode_size; /* size of inode structure*/ }
3.塊組描述符
Block#2∶塊組描述符塊(硬碟上的s first data block+1)EXT2將磁碟塊分成幾個組。每個組有8192個塊(硬碟上的大小為32K)。每組用一個塊組描述符結構體來描述。
struct ext2_group_desc { u32 bg_block_bitmap; // Bmap block 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; ul6 bg_used_dirs_count; u16 bg_pad; // ignore these u32 bg_reserved[3]; };
4.塊和索引節點點陣圖
Block#8∶塊點陣圖(Bmap)(bg block bitmap)點陣圖是用來表示某種項的位序列,例如磁碟塊或索引節點。點陣圖用於分配和回收項。在點陣圖中,0位表示對應項處於FREE狀態,1位表示對應項處於IN USE狀態。一個軟盤有1440個塊,但是 Block#0未被檔案系統使用。所以,點陣圖只有1439個有效位。無效位被視作INUSE,設定為1。
Block#9∶索引節點點陣圖(Imap)(bg inode bitmap)一個索引節點就是用來代表一個檔案的資料結構。EXT2檔案系統是使用有限數量的索引節點建立的。各索引節點的狀態用B9的Imap 中的一個位表示。在EXT2 FS中,前10個索引節點是預留的。所以,空 EXT2 FS的Imap 以10個1開頭,然後是0。無效位再次設定為1。
5.索引節點
Block#10∶索引(開始)節點(bg inode table)每個檔案都用一個128位元組(EXT4中是256位元組)的唯一索引節點結構體表示。
struct ext2_inode { u16 i_mode; // 16 bits=|tttt |ugs|rwx|rwx|rwxl ul6 i_uid; // owner uid u32 i_size; // file size in bytes u32 i_atime; // time fields in seconds u32 1_ctime; // since 00:00:00,1-1-1970 u32 i_mtime; u32 i_dtime; i_gid; // group ID u16 u16 i_links_count; // hard-link count u32 i_blocks;u32 i_flags; // number of 512-byte sectors u32 i_reservedl; // IGNORE // IGNORE u32 i_block[15]; // See details below u32 i_pad[7]; // for inode size = 128 bytes }
直接塊:block[0]至iblock[11],指向直接磁碟塊。
間接塊:iblock[12]指向一個包含256個塊編號(對於1KB BLKSIZE)的磁碟塊,每個塊編號指向一個磁碟塊。
雙重間接塊:i block[13]指向一個指向 256個塊的塊,每個塊指向256個磁碟塊。
三重間接塊:iblock[14]是三重間接塊。對於"小型"EXT2檔案系統,可以忽略它。
6.目錄條目
目錄包含dir_entry 結構,即
struct ext2_dir_entry_2{ u32 inode; // inode number; count from 1,NOT 0 u16 rec_len; // this entry's length in bytes u8 name_len; // name length in bytes u8 file_type; // not used char name[EXT2_NAME_LEN]; // name:1-255 chars,no ending NULL ; };
dir_entry 是一種可擴充結構。名稱欄位包含1到255個字元,不含終止NULL。所以dir entry 的 rec len也各不相同。
7.郵差演算法應用
(1)C語言中的Test-Set-Clear位
注意,一些 C語言編譯器允許在結構體中指定位,如:
struct bits{ unsigned int bit0 : 1; //bit0 field is a single bit unsigned int bit123 : 3; // bit123 field is a range of 3 bits unsigned int otherbits :27; // other bits field has 27 bits unsigned int bit31 :1; // bit31 is the highest bit }var;
該結構體將 var.定義為一個32位無符號整數,具有單獨的位或位範圍。那麼,var.bit0=0;將1賦值給第0位,則有var.bit123=5;將101賦值給第1位到第3位等。但是,生成的程式碼仍然依賴於郵差演算法和位遮蔽來訪問各個位。我們可以用郵差演算法直接操作點陣圖中的位,無須定義複雜的C語言結構體。
(2)將索引節點號轉換為磁碟上的索引節點
在 EXT2檔案系統中,每個檔案都有一個唯一的索引節點結構。在檔案系統磁碟上,索引節點從inode table塊開始。每個磁碟塊包含
INODES_PER_BLOCK=BLoCK_SIZE/sizeof(INODE)
個索引節點。每個索引節點都有一個唯一的索引節點號,ino=1,2,…,從1開始線性計數。已知一個ino,如1234,那麼哪個磁碟塊包含該索引節點,以及哪個索引節點在該塊中呢?我們需要知道磁碟塊號,因為需要通過塊來讀/寫一個真正的磁碟。
8.遍歷EXT2檔案系統樹
1.遍歷演算法
(1)讀取超級塊。檢查幻數s magic(0xEF53),驗證它確實是 EXT2 FS。
(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 NAME] [ino rec_len name_len NAME] . . . . .
(6)使用索引節點號ino 來定位相應的索引節點。回想前面的內容,ino 從1開始計數。使用郵差演算法計算包含索引節點的磁碟塊及其在該塊中的偏移量。
9.檔案系統的結構
(1)是當前執行程序的PROC結構體;
(2)是檔案系統的根指標;
(3)是一個openTable條目;
(4)是記憶體索引節點;
(5)是已掛載的檔案系統表。
10.基本檔案系統
- 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 ] // PROC structures PROC *running; // current executing
11.檔案系統專案的擴充套件
(1)多個組:組描述符的大小為32位元組。對於1KB大小的塊,一個塊可能包含1024/32=32組描述符。32個組的檔案系統大小可以擴充套件為32*8=256MB。
(2)4KB大小的塊:對於4KB大小的塊和一個組,檔案系統大小應為4*8=32MB。對於一個組描述符塊,檔案系統可能有128個組,可將檔案系統大小擴充套件到128*32=4GB。對於2個組描述符塊,檔案系統大小為8GB等。大多數擴充套件都很簡單,適合用於程式設計專案。
(3)管道檔案:管道可實現為普通檔案,這些檔案遵循管道的讀/寫協議。此方案的優點是;它統一了管道和檔案索引節點,並允許可被不相關程序使用的命名管道。為支援快速讀/寫操作,管道內容應在記憶體中,比如在 RAMdisk中。必要時,讀者可將命名管道實現為FIFO檔案。
(4)I/O緩衝:在程式設計專案中,每個磁碟塊都是直接讀寫的。這會產生過多的物理磁碟I/O操作。為提高效率,實際檔案系統通常使用一系列I/O緩衝區作為磁碟塊的快取記憶體。檔案系統的I/O緩衝將會在第12章中討論,但是可以把它合併到檔案系統專案中。
二、最有收穫的內容
程式設計專案:1級檔案系統的實現&&2級檔案系統的實現
1.1級函式包括
mkdir演算法、creat演算法、mkdir-creat演算法、rmdir演算法、link演算法、unlink演算法、symlink演算法、readlink演算法,其他1級函式(訪問、chmod、chown、更改檔案的時間欄位等)
所有這些函式的操作方式均相同。
操作示例:
(1)chmod oct檔名:將檔名的許可權位更改為八進位制值;
(2)utime檔名:將檔案的訪問時間更改為當前時間。
2.2級函式包括
Open演算法、lseek、close演算法、讀/寫普通檔案、opendir-readdir
操作示例:
3.3級函式包括
掛載演算法、解除安裝演算法、交叉掛載點、檔案保護、檔案鎖定、實際uid和有效uid
操作示例:
三、問題與解決思路
通過學習本章知識,深刻地感知到了Linux的EXT2檔案系統的操作複雜,所以一直在思考一個問題:Linux系統是如何讀取一個檔案的?
解決思路:下面分別針對目錄和檔案來說明:
1.目錄
ext2檔案系統建立一個目錄時,會給該目錄分配一個indoe和至少一個塊。inode記錄該目錄的相關屬性,並指向分配到的那個塊。這個塊記錄了這個目錄下的相關檔案或目錄的關聯性。
2.檔案
建立普通檔案時,會給該檔案分配一個inode與相對於該檔案大小的塊數量。如一個塊大小為4KB,建立一個100KB的檔案,linux將分配一個inode和25個塊來儲存該檔案。
其中inode本省不記錄檔名,而是記錄檔案的相關屬性,檔名是記錄在目錄所屬的塊區域中的。
因此要讀取一個檔案的內容時,Linux會從/開始,一直獲取到該檔案的上層目錄所在的inode,再由該目錄的塊區域中的檔名對應的inode號來找到對應的檔案,最後根據inode中的指標找到最終的檔案內容。
如上圖,讀取/etc/crontab的流程為:
1.根據/根目錄的塊中找到/etc對應的inode號;
2.根據/etc 目錄的塊中的inode資料,查詢到crontab的inode號;
3.根據查到的inode號來獲取該檔案的屬性,並且前往該inode所指的塊,順利獲取crontab 的檔案內容。
3.具體操作:編譯程式碼並執行
程式碼連結:
https://gitee.com/two_thousand_and_thirteen/codes/1i07cq8ag6po4zswjdmth16
執行截圖:
四、實踐內容(截圖、程式碼連結)本次實踐是基於OpenEuler系統下實現的
通過編寫C語言實現mkdir功能
一、背景
linux 中的mkdir命令是用來建立一個目錄的,相應的就需要使用到linux中的系統呼叫函式mkdir來實現目錄建立的功能。單單只是建立目錄的話一個系統呼叫足以,本文是使用mkdir函式來實現mkdir -p這個選項的功能,對其不存在的父目錄實現建立。
二、思路
對於一個a/b/c這樣的一個多級目錄,要想實現父目錄的建立方法和思路有很多,可以進行字串處理分出一級一級目錄來,但是這樣實現很是繁瑣,以至於我想到了遞迴實現。
思路如下:
1.先判斷a/b/c是否存在,不存在獲取其父目錄判斷。若存在直接退出
2.判斷a/b是否存在,不存在就獲取其父目錄,若存在退出
3.判斷a/是否存在,不存在就獲取其父目錄,若存在退出
4.如果其父目錄為.或/時退出
為了實現以上過程,需要一個可以獲得一個目錄的父目錄的函式。通過man可以清楚這個函式要求。
最終定位到dirname函式非常符合我的要求。函式宣告如下:
#include <libgen.h> char *dirname(char *path);
三、程式碼實現
程式碼連結:
https://gitee.com/two_thousand_and_thirteen/codes/a9qhy5pizk6grsdfcvot856
功能實現截圖:
四、總結
在寫mkdir -p這個功能的時候,思路很明確,程式碼也基本上早就寫好了,但是除錯花了很長時間。究其原因是在於dirname這個函式,看其宣告很明顯就是給一個目錄的path字串指標,返回一個指向其目錄的字串指標,但是其實不然。dirname不僅返回一個指向其父目錄的字串指標還可能修改傳入的引數path的值為父目錄字串。man文件中說明如下:
The dirname() function may modify the string pointed to by path, and may return a pointer to static storage that may then be overwritten by subsequent calls to dirname().
最終還是通過printf列印除錯的,並沒有藉助gdb,主要還是認為gdb用起來不太方便。