or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing
xxxxxxxxxx
simplefs
Structure Overview
simplefs 的架構定義在
simplefs.h
中的simplefs_sb_info
結構體中,每個 simplefs 檔案系統中包含:sb->nr_inodes
個 inode,剩餘數量由sb->nr_free_inodes
紀錄sb->nr_blocks
個 block,剩餘數量由sb->nr_free_blocks
紀錄而 simplefs 中的 inode 會統一存放在 superblock 後的數個 block 中,即上方示意圖中的 inode store 區段。
在 simplefs 中,所有資料都是以 Little Endian 的形式儲存,因此會在存取資料時使用 htole32、le32toh 處理 host 與 Little Endian 間的轉換。
Block and Inode
Block
檔案系統中最基本的儲存單位,在 simplefs 中 block 的大小由
SIMPLEFS_BLOCK_SIZE
巨集定義:不論是 inode, bitmaps 還是 superblock 都是儲存在儲存裝置中,而當需要修改或是讀取這些 block 時,都是透過
buffer_head
相關的 API 進行讀寫,例如在透過simplefs_create
建立檔案時,會透過sb_bread
讀取指定的 block 到 buffer 中,然後就可以透過這個 buffer 修改資料,等到要更新資料到儲存裝置上時,再透過mark_buffer_dirty
標記 buffer 中的資料需要更新到儲存裝置上。TODO:
buffer_head
原理、address_space_operations
Inode
用來表示 inode 的結構體
simplefs_inode
定義在simplefs.h
中:simplefs_inode
基本上就是 VFS 的inode
結構體的子集合,只包含權限、大小等必須紀錄在儲存裝置中的資訊,不用耗費額外空間儲存沒使用到的資訊。但由於 VFS 提供的界面的參數都是
inode
等 VFS 提供的類型,因此 simplefs 還另外使用了另一個結構體simplefs_inode_info
來輔助:這個結構體相當於 inode 在記憶體中的快取,會在建立新的 inode 時一併被建立:
透過設定中斷點,可以看到在呼叫
simplefs_new_inode
並取得一個新的 inode 後,simplefs_alloc_inode
也接著被呼叫,而這個函式會透過kmem_cache_alloc
在記憶體中建立一個simplefs_inode_info
的物件。但此時這個結構體中只有
vfs_inode
是有意義的,ei_block
與i_data
則要等到回到simplefs_iget
後才會正式從儲存裝置中讀取simplefs_inode
儲存的資料,然後依據檔案類型指派對應的值。值得注意的是這個
vfs_inode
的型別不是指向struct inode
的指標,而在simplefs_alloc_inode
則是回傳&ci->vfs_inode
給 VFS 使用,這代表 simplefs 的 inode 物件本體是儲存在這個結構體中。因此只要 VFS 中的 inode 存在的話,就代表這個結構體的物件也存在記憶體中,因此之後只要透過
SIMPLEFS_INODE
(本質上是個container_of
)巨集就可以直接透過 inode 取得這個結構體的物件。TODO: 這個"快取"會一直留在記憶體中嗎?
而 inode 的相關操作則定義在
inode.c
的simplefs_inode_ops
與symlink_inode_ops
兩個inode_operations
結構體的物件中:以最基本的
.create
為例,建立檔案時會呼叫simplefs_create
,要做的工作依序大致為:simplefs_inode_info
物件simplefs_new_inode
建立新的 inodei_op
、i_fop
等操作Inode Bitmap and Block Bitmap
TODO: 相關操作函式說明
File and Directory
File
定義在
simplefs.h
中的simplefs_file
結構體負責紀錄 inode 與檔名的對應:檔名的長度限制為
SIMPLEFS_FILENAME_LEN
個字元,定義在simplefs.h
中,實際長度定義為 255。這個結構體主要是在尋找檔案以及走訪目錄時被使用到,能夠透過這個結構體找到某個檔名對應的 inode 編號,接著再透過
simplefs_iget
找到實際的 inode 編號對應的inode
結構體的物件。而實際上紀錄在儲存裝置中的資料則是由
simplefs_inode
結構體管理,其中的i_blocks
會紀錄該檔案佔用的 block 數量,並透過simplefs_extent
結構體管理:每個 extent 都是由數個連續的 block 組成,實際對應到的 block 編號範圍為 \([ee\_start,ee\_start+ee\_len)\),在分配 extent 時會透過
get_free_blocks
一次取得 8 個連續的閒置 block。但由於 extent 中的 blocks 用完後可能沒辦法找到與原本 block 相連的閒置 blocks,使得檔案系統需要分配新的 extent 給該檔案,而檔案與其使用的 extents 則是透過
simplefs_file_ei_block
結構體來管理:TODO: 檔案大小上限
而檔案相關的操作則由
simplefs_file_ops
與simplefs_dir_ops
兩個file_operations
結構體的物件管理:這個結構體定義了非目錄的檔案的操作,四個操作皆為 VFS 提供的通用函式。
TODO: 四個操作的用途以及被呼叫的時機
如同名稱,負責目錄相關的操作,這邊有定義的操作只有
iterate_shared
,會被readdir
等需要走訪目錄的操作使用。TODO: 除了 readdir 以外的呼叫?shared?
Directory
作為一群檔案的集合,目錄則是以另一個結構體
simplefs_dir_block
表示,這個結構體唯一的成員就是simplefs_file
結構體的陣列:在透過前面提到的
simplefs_iterate
走訪目錄時,只要簡單的透過for
迴圈走訪simplefs_file
陣列、取得目錄下各個檔案的 inode 編號。與檔案相似,這個結構體只是用來方便進行操作,不會紀錄在儲存裝置中。而實際會紀錄在儲存裝置中的是
simplefs_inode
的內容,並同樣以simplefs_extent
以及simplefs_file_ei_block
管理目錄所需的 blocks:但與檔案的使用有點不同,作為目錄使用時
nr_files
用來代表這個目錄中的檔案數量,而目錄的inode->i_blocks
則固定為 1。TODO: 檔案數量上限
Superblock
定義在
simplefs.h
中:而 superblock 相關的操作則是定義在
super.c
的super_operations
結構體中:例如:在掛載檔案系統時會呼叫
simplefs_fill_super
將 superblock 讀入記憶體,然後讀取 root inode 並建立檔案系統根目錄的 dentry。Make simplefs Filesystem
從 Makefile 中製作 simplefs 映像檔的部份:
在透過
dd
建立指定大小的映像檔後,需要透過mkfs.simplefs
進行格式化,而格式化的流程定義在mkfs.c
中。首先會透過
fstat
這個系統呼叫取得目標映像檔的資訊,用來檢查大小以及檔案類型。TODO: if not bdev?
接著就會開始依序分配 superblock、inode store、bitmaps 以及 data blocks:
Superblock
Superblock 會透過
write_superblock
函式分配空間,但在分配空間前要先依序計算一些資訊:Block 總量:
由於所有資訊都是以 block 的形式儲存,所以直接將所有空間分配給 block 即可,總共會有 \(\lfloor \frac{映像檔大小}{Block\ 大小} \rfloor\) 個 block。
Inode 總量:
由於一個 inode 能夠對應到一或多個 block,所以 inode 數量至少需要與 block 數量相同。
不清楚用途:
用來儲存 Inode 的 Block 數量:
由於每個 block 能紀錄的 inode 數量有限,因此需要使用 \(\lceil \frac{Inode\ 數量}{每個\ Block\ 能儲存的\ Inode\ 數量} \rceil\) 個 block 來儲存所有 inode。
Bitmaps:
接著由於各個 block 的使用情形會透過 bitmap 紀錄,因此也需要分配一些空間給 bitmap,實際上需要分配的大小為 \(\lceil \frac{Block\ 總量}{8} \rceil\) 個位元組,換算成 block 數量則需要再除以 block 大小,因此為 \(\lceil \frac{Block\ 總量}{8 \times Block\ 大小} \rceil\) 個。
相似地,紀錄 inode 狀況的 bitmap 則共需要 \(\lceil \frac{Inode\ 總量}{8 \times Block\ 大小} \rceil\) 個 block。
Data Block 數量
由於前面的資訊都需要 block 儲存,所以實際上能用來儲存檔案內容的 block 數量會比 block 總數少:
儲存 Inode 所需的 Block 數 + Bitmaps 所需 Block 數
個 blocks。接著,如同前面 Structure Overview 提到的,這些資訊會填入
simplefs_sb_info
結構體中,而這個結構體則會作為第 1 個 block 被寫入至檔案系統中。Inode Store
將 superblock 的資訊寫入到第 1 個 block 後,接著會透過
write_inode_store
函式將各個 inode 寫入到 block 中。在一個全新的 simplefs 中,需要分配的 inode 只有這個檔案系統的根目錄,也就是前面 superblock 部份提到的 root inode,若是沒有這個 root inode 的話,在新增 inode 時連要新增到哪個目錄的 block 中也不知道,這個檔案系統也就沒辦法使用。
而這個 inode 的設定跟一般的目錄的 inode 相似,預設權限為
drwxrwxr-x
,而實際用來儲存資料的 block 則是 data block 區段中的首個 block,也就是:而 inode store 區段中的其他 inode 則不需特別處理,全部初始化為 0 就可以了。
也因為這個根目錄佔用了一個 inode 以及 block,所以在前面寫入 superblock 的閒置 inode 及 block 數量時,其實就有先扣除了:
Bitmaps
初始化 ifree bitmap 與 bfree bitmap 分別是由
write_ifree_blocks
和write_bfree_blocks
兩個函式負責處理。write_ifree_blocks
會根據 superblock 中設定的 ifree bitmap 所需的 block 數量,將所有未使用的 inode 對應的位元都填入 1 表示可被分配。而由於目前只有分配 root inode,因此除了 inode bitmap 佔用的第一個 block 的第一個位元為 0 以外,其餘 block 皆用 1 填滿即可。write_bfree_blocks
要做的事也很相似,但是已經使用過的 block 不像 inode 只有一個:sb->info.nr_istore_blocks
sb->info.nr_ifree_blocks
sb->info.nr_bfree_blocks
所以需要比較特別的方法將 0 填入對應的位元:
每次以 64 位元的變數
line
為單位,透過for
迴圈從 LSB 開始填入 0,若這個line
已經填完的話,就寫入bfree
這個與 block 大小相同的 64 位元整數陣列暫存,直到指定數量的位元都填入 0 後才會將整個bfree
寫入對應的 block 中。TODO: 在什麼情況下會超過一個 block? 應該檢查 i 是否 out of range?
Data Blocks
TODO: 尚未實作,應全部填入 0?
Registering and Mounting
Registering and Unregistering
核心模組定義在
fs.c
中,掛載模組時,首先會在simplefs_init
中透過simplefs_init_inode_cache
建立 Inode Cache,然後才會呼叫register_filesystem
將 simplefs 加入到file_systems
串列中。而卸載模組則相反,先將 simplefs 從
file_systems
串列中移除後才會將釋放 Inode Cache。Mounting and Unmounting
Mounting
首先在 Linux 中 mount 是根據不同掛載裝置而有不同的掛載函式,如同 VFS 內所提到三種函式,而在 simplefs 中是選擇使用
mount_bdev()
作為掛載函式,並且利用simplefs_fill_super
作為初始化 superblock 的函式Unmounting
至於卸載檔案系統則是將 superblock 的資訊給消除掉,主要會利用到 linux/fs/super.c 的
kill_block_super
File page cache and read/write blocks on disk
simplefs 同時提供將 page cache 讀寫和從硬碟上將 block 從檔案系統中寫入至硬碟,而 block 中的資料可以同時包括 superblock, inode 和 bitmaps,而在 simplefs 中如同本章之前所提大約為 4 KB。
struct extent
在了解方法之前,先了解結構體
extent
,結構體extent
是在較新的檔案系統中才有的結構體,目的是為了解決處理大型檔案的問題,比如說在檔案系統如 bfs 就是直接對 block 進行操作,如以下程式碼上述情況會有什麼問題,那就是在處理大型檔案比如說超過 10 MiB 的檔案,以一個單位只有 4 KiB 的 block 來處理,那勢必會耗費大量時間,但是透過結構體
extent
,一次會分配 8 個 block 來處理檔案不管是要寫入或是讀取,以減少每次都要對每個 block 處理的時間。Get blocks from file system
如何將 file system 中的資料映射到要儲存在硬碟上的 block,要知道在 simplefs 中運用了 superblock,inode 等結構體來記錄一個檔案系統的資訊,所以一開始使用下面兩個定義來取得 superblock 和 inode 的資訊
而為了知道檔案要映射到硬碟的哪個區域,simplefs 使用結構體
simplefs_extent
來管理硬碟的起始地址,長度,以及起始 block 的地址,sb_bread
則是透過 superblock 和 inode 來得知檔案的 block 的大小範圍,最後在找到index
後,利用iblock
來找出這個檔案在硬碟的 block 編號範圍。最後則是靠至著
get_free_blocks
在硬碟中找尋空的位置並且透過map_bh
將buffer_head
映射到映射到硬碟上。