---
# System prepended metadata

title: File allocation method
tags: [allocation method, inode, 研究所, 計系, FAT, indexed allocation, contiguous allocation, linked allocation]

---

- 判斷 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#)