- 判斷 Performance 的前提: 要決定一個系統要用哪種 allocation method,需要先判斷這個系統比較多是需要 sequential access 還是 random access --- # Allocation methods ## Contiguous allocation <font color = "red">需要事先宣告大小</font> ==support sequential access, random access== 不管是 sequential access 還是 random access,contiguous allocation 只需要一次 access 就能取得一個 block。因為我們可以直接把一個檔案的起始位置放在 memory 裡面,所以我們很容易可以計算任何第 i 個 Block 的位址,然後就能直接去讀取那個 block。 - Contiguous allocation 適合 small files ## Linked allocation ==support sequential access== <font color = "red">沒有外碎、不用事先宣告大小,但有 pointer overhead、reliability 差</font> 降低 pointer overhead 的方法: 把好幾個 blocks 收集成 clusters,以 <font color = "snake">cluster</font> 為單位 allocate。 這個方法可以增加 throughput,因為 seek 次數減少,不過缺點是會有內碎,且 random I/O access performance 降低,因為即使只需要一點點 data,也要 transfer 很大量的 data。 如果是 Linked allocation,我們也可以直接把下一個 block 的位址放在 memory 裡面, ### 變型:FAT FAT 即 File-allocation Table,是 Linked allocation 的變型,沒有支援 FAT 的話 Linked allocation 對 random access 的 performance 就很差 ![image](https://hackmd.io/_uploads/HJDEtQ9u6.png) 儲存空間開頭的一部分拿來放一個表,這個表的每個 entry 就是一個 block,表的 index 用 block number 來編 directory 的 entry 放檔案的第一個 block 的號碼,由圖例可以看到第一個 block 的號碼是 217,所以我們到 FAT 的第 217 個 entry,看到裡面的值是 618, 618 是這個檔案在 217 後下一個 block 的號碼,如果要再找到再下一個 block,就重複同樣的步驟,到第 618 個 entry 找到 339 ⋯⋯,重複這樣的步驟可以不斷找到下一個 block 的號碼,直到最後一個 block,最後一個 block 的值會是一個特別的值 ``eof``(end-of-file) FAT 中,沒有用到的 block 裡面的值會是 0 ,所以如果要 allocate 新的 block,只要到表中找到第一個值是 0 的 block,把原本值是 ``eof`` 的 entry 值改成這個 entry 的號碼,再把這個 entry 的值改成 ``eof`` 就好了 FAT 的問題就是可能會有很多的 disk head seek,disk head 要先移動到開頭讀 FAT ,再去找要找的 block 的位置,最後才去找這個 block,最糟的情況下,每個 block 都要重複找 block 的位置、找這個 block 本身。 好處是<font color = "red"> random access time 降低,但 disk head seek overhead 太高,除非 FAT 有被 cached</font> > FAT random access 好,是因為可以直接讀 FAT 表來看每個 block 的位置 ## Indexed allocation ==support random access== <font color = "red">沒有外碎、但 pointer overhead 比 linked 還高</font> > 假設只有用到兩個 block,linked 只需兩個 ptr,但 indexed 仍要 allocate 整個 index block,變成只有兩個 ptr 是 non-NULL 因為每個 file 都要有一個 index block,所以 index block 不能太大,否則浪費太多空間,但又不能太小,否則裝不下足夠的 ptr 解決方法有三種, :::success - linked theme - multilevel index - combined (結合上述兩種) ::: ### Combined: Unix inode Combined 例子:Unix inode > $\rightarrow$ 關於 inode 的背景知識,有需要可先跳到下方。 恐龍列出的 inode 內容包含: :::info - user / group identifiers of the file > Owner UID, group UID - times of the last modification and access > 總共有四種類型的 timestamps (ACMD times): > 1. ++a++time: access time > 2. ++c++time: inode change time > 3. ++m++time: data modification time > 4. ++d++time: deletion time - count of the number of hard links (directory entriess) to the file - type of the file > type: plain file, directory, symbolic link, character decive, block device, socket - 15 pointers to the disk blocks containing the data contents of the file > 前十二個 pointers 指向 direct blocks,也就是說這些 pointers 為「包含 file data 的 blocks」的 addresses;接下來三個為 indirect blocks,分別為 single / double / triple indirect blocks。 ::: ![image](https://hackmd.io/_uploads/SJr1BQ5dp.png) > 109 交 ![image](https://hackmd.io/_uploads/rJxCpX9ua.jpg) ### inode 因為 file system 需要 track 每個檔案的 info,像是: - 這的檔案包含哪些 blocks - 檔案有多大 - 檔案的 owner 是誰、aceess rights - 有被 access 或 modify 幾次 上述這些所有跟檔案有關的資訊,我們把它稱作 <font color = "snake">metadata</font>。 > 實際上是 file system 裡面「所有不是 pure user data 的資訊」全部都叫做 metadata。 要存放這些資訊(metadata),檔案系統通常會用一種 structure 叫做 <font color = "snake">inode</font>,而且這些 inode 也需要存放在 disk 上面,所以我們需要為 inode 預留一些空間,這部分空間稱作 <font color = "snake">inode table</font>,也就是 disk 上的一個 inode 的 array。 > - 也正是因為這些 inode 是放在 array 裡,才會有 inode 這個名字,代表著 index node 的簡稱。 ![image](https://hackmd.io/_uploads/rk5ANzTOp.png) > 假設我們將 disk 分成 64 個 blocks,因為 disk 裡大部分的資料都是 user data,所以我們假設 block 8-63 是 (user) data,前面 block 3-7 ,標示 ``I`` 的深灰色處則是五個 block 的 inode table(不過實際上 inode 沒有這麼大,通常只有 128 或 256 bytes) > 假設一個 Block = 4Kb(常用大小),如果 inode 大小是 256 bytes,一個 Block 可放 $\cfrac{2^{12}} {2^8} = 2^4 =16$ 個 inodes > 因為我們現在 inode table 佔 5 個 Blocks,所以我們可以放 $16 \times 5 = 80$ 個 inode > 而 80 就是我們這個 file system 最多可以有的 file 數量 >> 如果我們把同樣的 file system 建在一個更大的 disk 上,我們就可以 allocate 一個更大的 inode table,於是就可以放更多的檔案啦~ 有了 data blocks 和 inode 以後,接下來我們還需要的是: 追蹤這些 data blocks 和 inode 是不是已經被使用的方法。 舉例來說像是 ++free list++(指向第一個 free block,接著指到第二個⋯⋯)這邊我們選用的 allocation structure 是 <font color = "snake">bitmap</font>,bitmap 就是很簡單的把對應到的 object 或是 block 標 ``1`` 來表示正在被使用,標 ``0`` 表示 free ![image](https://hackmd.io/_uploads/rysAifT_T.png) > 假設 inode table 和 data region 我們都各用一個 block 來放他們各自的 bitmap(第 1, 2 個黑色的 block,``i`` 代表 inode table,``d`` 代表 data region) 實際上用一整個 block 來存 bitmap 有點浪費,因為一個 block 的大小是 4Kb,等同 $(4 \times 8)\ Kbits = 32 \ K bits$,因為一個 bit 可以拿來表示一個 object / block 的使用狀態,所以我們可以 track 32K 個東西有沒有在用,但我們根本就只有 80 個 inode 和 56個 data blocks XD 不過為了看起來比較簡單,就還是用一個 block 的大小來當例子 最後,還剩下一格還沒有用到的 block,這個 block 就拿來當作 <font color = "snake">Superblock</font>,裡面放關於這個 file system 的各種 info,像是裡面有多少 inodes、有多少 data blocks,或是 inode table 從哪裡開始⋯⋯ ![image](https://hackmd.io/_uploads/ryxRg0Mpua.png) > 加上了 superblock (用 ``S`` 代表) 所以有了 superblock 以後,如果我們要 mount 一個 file system, OS 就會先去讀這個 superblock,接著設一些 parameter 的 initial value,接著就可以把 volume attach 到 file system tree了 ! ![image](https://hackmd.io/_uploads/B10clQTOa.png) > 把上述所有東西整理出細節就變成這樣 假設我們要讀第 32 號的 inode,file system 就會先看 inode table 從哪裡開始(此處是 $12Kb$),接著計算 offset,也就是第 32 個 inode 離 inode table 的起始位置有多遠(``32 x sizeof(inode)`` 也就是 $32 \times 256 \ byte = 8192 byte$),加起來就可以得到 inode 的 byte address 了 算一算我們的 base + offset: $\rightarrow$ $12 \ Kb + 8192 \ bytes$ $= 12 \ Kb + 2^{13} \ bytes$ $= 12 \ Kb +8 \ Kb$ $= 20 \ Kb$ 因此我們只要到 byte address $= 20 \ Kb$ 就能找到第 32 號的 inode 可是! 這裡會有一個問題就是 disk 根本就不是 byte addressable!也就是說,我們不能隨便要存取哪個 byte 就直接去 access 那個 byte,因為 disk 的基本單位是 sector,所以我們只能來算這個 byte address 是在 sector 第幾號: 假設一個 sector 的大小是 512 bytes(一個常用的大小) $\rightarrow$ $20 \ Kb = 20 \times 1024 \ bytes$ $\rightarrow$ $\cfrac{20 \times 1024}{512} = 40$ 即第 40 個 sector 所以為了要得到第 32 號 inode, file system 會 read sector 40 ## File Operation 以下 operation 假設: - file system mounted,因此 superblock in memory - 其他所有東西(例如 inode, directories)都還在 disk 上 ### open() 假設要打開一個檔案叫做 ``bar`` $\rightarrow$ ``open("/foo/bar,O_RDONLY)`` 1. 首先 file system 要先 traverse path name 來找到 ``bar`` 的 inode,去取得關於這個 file 的一些基本資訊 > 基本資訊如 permission、檔案大小等等 2. 因為 traversal 必定從 root 開始,並且在 mount 的時候 file system 就知道 root 的 inode number,所以 file system 第一件事就是把含 inode number 2 (root inode) 的 block 從 disk 讀進來 > UNIX file system 的 root inode number 通常是 2 3. 讀進 root inode 以後, file system 就會在裡面找指向 data blocks 的 pointers,這些 pointers 指的 blocks 會包含 root directory 的內容,file system 就從這裡面去找 ``foo`` 4. 一旦找到 ``foo``,就會也找到 ``foo`` 的 inode number 5. 接著就是遞迴去 traverse 整個 path,直到找到我們要找的 inode 為止,在這個例子裡也就是找到 ``bar`` 的 inode 6. ``open("/foo/bar,O_RDONLY)`` 的最後一步就是把 ``bar`` 的 inode 讀到 memory 7. 上述完成以後, file system 就會做最後的 permission check,然後 allocate ``fd`` 再 return ### read() 在做完 open() 以後,program 就可以再發 ``read()`` system call 去讀檔案 1. 第一個 ``read()`` 會讀進這個 file 的第一個 block > offset = 0 除非有用 ``lseek()`` 特別去改 2. 因為要把第一個 block 讀進來,inode 就會去找第一個 block 的位置 3. 也會更新 inode 的 last accessed time 4. 為這個 file descriptor update in-memory 的 open file table > ![image](https://hackmd.io/_uploads/SkPqxghKT.png) 5. 改 file offset,這樣下次 ``read()`` 就可以讀到第二個 block ### write() 跟 ``read()`` 不一樣的是,``write()`` 可能需要 allocate 一個新的 block(除非是要 overwrite 某個計有的 block) 除了把 data 寫到 disk,首先也要先決定要把哪個 block allocate 給這個 file,還有也要更新其他 disk 上的 structure (例如 bitmap, inode) 因此,做一次 ``write()`` 需要五次 I/O 1. read data bitmap, 2. update (write) bitmap 來 mark 新 allocated 的 block as used 3. read inode 4. write inode 標示新 allocated 的 block 的位置 5. 真的寫到 block --- ## Caching 從前一段 file operation 的例子可以看到,這些 operation 每次要做都要花一堆 I/O,造成 performance 變差,因此 file systems 就<font color = "green">用 system memory(DRAM) 來 cache 重要的 blocks</font> 早期的 file system 用 <font color = "snake">fixed size cache</font> 去放這些常用的 blocks,但是 fixed size cache 需要++在 boot time 就 allocated++,可是這樣的做法有點浪費,因為說不定某些時候 file system 就用不到這麼大的空間,而且 file cache 裡用不到的 pages 也不能拿來做其他的用途 因此,現代的 file system 用的是 <font color = "snake">dynammic partitioning</font>,結合 virtual memory pages 和 file system pages 成 <font color = "snake">unified page cache</font>,這樣一來,memory 的分配就比較有彈性,可以根據 virtual memory 和 file system 哪個需要的比較多在不同的時間點去自由分配 ## Reference - 恐龍 - [Operating Systems: Three Easy Pieces 第 40 章](https://pages.cs.wisc.edu/~remzi/OSTEP/) > 幾乎所有 inode 的內容都出自這裡,是很簡單好懂的線上資源,推薦!這本書的好處是講得非常簡單清晰,基本上沒太多基礎也能讀;缺點是有點過於簡單,還是需要跟恐龍交互參照一下 - The linux kernel: [index nodes](https://www.kernel.org/doc/html/latest/filesystems/ext4/inodes.html#)