# FHS
A simple description of the **UNIX** system, also applicable to **Linux**, is this:
"On a UNIX system, everything is a file; if something is not a file, it is a process."
==
A **directory** is just a file containing names of other files. Programs, services, texts, images, and so forth, are **all files**.
為了以有序的方式管理所有這些檔案,人們喜歡將它們視為硬碟上有序的樹狀結構,例如 **MS-DOS(Disk Operation System)**
---
## Sort of files / File type
- Directories: 包含其他檔案的檔案 As previously mentioned
- Special files: 有關 I/O 的特殊檔案,大多數 special files 位於 `/dev` ,待會會說 `/dev`
- Links: 一種讓檔案或目錄在系統檔案樹的其他地方可見的系統,簡單來說就是捷徑
- (Domain) sockets: 一種特殊的檔案類型,類似於 TCP/IP sockets,提供由檔案系統去訪問受到控制的 process 間的網絡
- Named pipes: 大致上像 sockets,但是是一種 process 之間的溝通方式,而不使用 sockets
---
## Partition 分割
源於 Linux 還沒有日誌檔案系統的時代,當時一次電力故障會對 Linux 帶來災難,因此出現分區的技術。分區的使用是出於安全性和穩定性的考量,讓系統某一部分的漏洞不會讓整台電腦都處於危險之中。
舉一個簡單的例子:使用者建立了一個 script、一個程式或是一個網頁,開始填滿磁碟。
- 如果磁碟只有一個大分區,當磁碟滿了,整個系統將停止運作。
- 如果使用者將資料存儲在單獨的分區上,那麼只有該(資料)分區會受到影響,而系統分區和其他的資料分區將能繼續運作。
---
## Partition layout and types
- data partition: 正常的 Linux 系統資料,包括`/`
- swap partition: 從硬碟借點空間當作電腦的 RAM
Kernel 在許多發行版中會有自己的分區,因為它是系統中最重要的檔案,所以你會發現你還有一個 `/boot` 分區,儲存你的 Kernel 和相關資料檔案。
其餘的硬碟通常被劃分為 data partition,儘管非系統重要的資料可能都會位於同一個分區上,但當資料需要被分開存放在不同的分區時,通常會遵循一個固定的模式:
- a partition for user programs (/usr)
- a partition containing the users' personal data (/home)
- a partition to store temporary data like print- and mail-queues (/var)
- a partition for third party and extra software (/opt)
當然這只是冰山一角
## More file system layout
實際上 Linux 檔案系統通常長這樣

> https://www.pathname.com/fhs/
| Directory | 內容
| --- | --- |
| /bin | 存放基本的二進制可執行檔案
| /boot | 放置開機會使用到的檔案,包括 Linux Kernel 檔案以及開機選單與開機所需設定檔。
| /dev | 放特殊檔案或裝置檔案(鍵盤、滑鼠)的位置
| /etc | 系統主要的設定檔,例如網路設定、啟動 script、服務設定
| /home | 存放所有使用者的個人檔案和設定檔。
| /lib | 函式庫、開發軟體的子程式集合
| /lost+found | 當檔案系統發生錯誤時, 將一些遺失的片段放置到這個目錄下。
| /misc | 雜項, 各種各樣的(miscellaneous的縮寫)
| /mnt | 外部檔案系統的標準掛載點,例如 CD-ROM 或數位相機
| /net | 整個遠端檔案系統的標準掛載點
| /opt | 第三方軟體放置的目錄、怕某個檔案汙染系統的話,可以把雜七雜八的東西放在這裡
| /proc | 存放系統Kernel、process、周邊裝置的狀態及網路狀態等
| /root | 系統管理員 (root) 的家目錄。注意 `/` 和 `/root` 之間的區別,前者是根目錄,後者是系統管理員 (root) 的家目錄
| /sbin | 供系統和系統管理員使用的程式。
| /tmp | 系統使用的臨時空間,重啟時會清除,因此不要用來儲存任何東東!
| /usr | 「非系統基本必須的」東西,與使用者相關的 process、library、文檔等
| /var | 主要針對常態性變動的檔案,例如 Printer、快取 (cache)、紀錄檔 (log file) 以及某些軟體運作所產生的檔案,或在燒錄之前保留CD的映像
來看一下圖中有子目錄的目錄們
- /etc
- /default - 某些命令的預設選項,例如 useradd
- /sysconfig - 包含系統設定檔案的目錄:滑鼠、鍵盤、桌面、系統時鐘、電源管理 blablabla...
- /X11 - 在 Linux 上頭的圖形介面!
- /home
- 就不同使用者
- /lib
- /media - 可攜式儲存設備的掛載點
- /mnt
- /cdrom - 掛載光碟機(CD/DVD-ROM)
- /floppy - 掛載磁片(Floppy Disk)
- 隨著技術發展,光碟機和磁片已經很少使用,並且 Linux 現在大多會使用 /media 來自動掛載可攜式儲存設備
- /usr
- /X11R6 - X11R6 是 X Window System Release 6(1994 年發布)
- 現代 Linux 系統多已淘汰,改用 /usr/lib/X11 或 /etc/X11
- /bin - 一般使用者執行的可執行檔
- /games - 早期 Linux 內建或安裝的遊戲會存放在這裡,例如 bsd-games(包含 tetris, snake, fortune)
- /include - 存放 C/C++ 的標頭檔(header files),如 stdio.h, stdlib.h。
- /lib - 函式庫,給應用程式和開發工具使用
- /local - 使用者自行安裝的軟體
- /man - 早期存放 man 命令的手冊頁,但現在改用 /usr/share/man
- /sbin - 只供 root 或管理員使用的執行檔,例如 ifconfig, iptables, fdisk
- /share - 架構無關的共享資料(文檔、字型等)
- /doc - 文件說明與授權資訊
- /man - 手冊頁
- /fonts - 字型檔
- /locale - 本地化(i18n, l10n)
- /src - 原始碼,主要存放 Linux Kernel 與驅動程式原始碼
- /var
- /gdm - GNOME Display Manager 相關數據(登入記錄、錯誤日誌)
- 現代系統可能已改用 /run/gdm 或 /var/lib/gdm
- /lib - 系統與應用程式的狀態數據,不會因重開機而清除,如套件管理、資料庫
- /lock - 鎖定文件,避免多重存取
- 現代 Linux 多改用 /run/lock
- /run - 系統與服務的執行時(runtime)數據,如 PID、socket
- 現代 Linux(systemd)已改為 /run
- /log - 系統與應用程式的日誌檔
- /spool - 佇列數據,等待處理的檔案(排隊機制,類似 queue)
- /tmp - **持久性暫存目錄**,存放可跨重開機的暫存檔案
如何找出一個目錄位於哪個分區? 使用 `df` 命令,用點 `.` 可以顯示當前目錄所屬的分區,並告知該分區上使用的空間量:
```
sandra:/lib> df -h .
Filesystem Size Used Avail Use% Mounted on
/dev/hda7 980M 163M 767M 18% /
```
<iframe src="https://i.sstatic.net/BlpRb.png" width="700px" height="500px" frameborder="0" scrolling="no">
</iframe>

---
## The file system in reality
對於大多數使用者,我們可以通過想像將所有檔案串在一起,形成整個系統的樹狀結構的概念,以樹狀結構去理解檔案系統挺輕鬆的,但電腦肯定對樹或樹狀結構一無所知
新的作業系統在規劃檔案系統時,一般檔案會有屬性(如權限、時間、身份資料紀錄等)以及實際資料的紀錄,同時整個檔案系統會紀錄全部的資訊,因此通常檔案系統會有如下幾個部份:
- superblock:記錄此 filesystem 的整體資訊,包括 inode/block 的總量、使用量、剩餘量, 以及檔案系統的格式與相關資訊等;
- inode:記錄檔案的屬性,一個檔案佔用一個 inode,同時記錄此檔案的資料所在的 block 號碼;
- block:實際記錄檔案的內容,若檔案太大時,會佔用多個 block 。
---
### Superblock (超級區塊)
superblock 為整個檔案系統的總結資訊處,要讀取檔案系統一定要從 superblock 讀起。superblock 主要紀錄資料為:
- block 與 inode 的總量;
- 未使用與已使用的 inode / block 數量;
- block 與 inode 的大小 (block 為 1, 2, 4K,inode 為 128bytes, 256bytes 或 512bytes);
- filesystem 的掛載時間、最近一次寫入資料的時間、最近一次檢驗磁碟 (fsck) 的時間等檔案系統的相關資訊;
- 一個 valid bit 數值,若此檔案系統已被掛載,則 valid bit 為 0 ,若未被掛載,則 valid bit 為 1
---
### inode table (inode 表格)
在檔案系統中,檔案有一個 ==inode==,每一個 inode 都有號碼,而 inode 的內容在記錄檔案的屬性以及該檔案實際資料是放置在哪幾號 block 內。 inode 記錄的檔案資料至少有底下這些:
- 該檔案的存取模式(read/write/excute);
- 該檔案的擁有者與群組(owner/group);
- 該檔案的容量;
- 該檔案建立或狀態改變的時間(ctime);
- 最近一次的讀取時間(atime);
- 最近修改的時間(mtime);
- 定義檔案特性的旗標(flag),如 SetUID...;
- 該檔案真正內容的指向 (pointer);
每個 inode 描述硬碟上的一個資料結構,儲存檔案的屬性,包括檔案資料在硬碟上的實際位置。
當硬碟被初始化並準備接受資料讀寫時,通常會為每個分區建立固定數量的 inodes。
這個數量將會是該分區上可以同時存在的所有類型(包括目錄、特殊檔案、連結等)檔案的最大數量。我們通常計算 1 個 inode 大小是 2~8 KB。
---
### data block (資料區塊)
檔案實際的資料存放在 data block 上面,每個 block 也都會有號碼,提供給檔案來儲存實際資料,也讓 inode 可以紀錄資料放在哪個 block 號碼內。
- 原則上,block 的大小與數量在格式化完就不能夠再改變了(除非重新格式化);
- 每個 block 內最多只能夠放置一個檔案的資料;
- 承上,如果檔案大於 block 的大小,則一個檔案會佔用多個 block 數量;
- 承上,若檔案小於 block ,則該 block 的剩餘容量就不能夠再被使用了(磁碟空間會浪費)。
---
## 檔案讀寫的操作程序
一般來說,檔案系統內的一個檔案被讀取時,流程是這樣的:
1. 讀到檔案的 inode 號碼
2. 由 inode 內的權限設定判定使用者能否存取此檔案
3. 若能讀取則開始讀取 inode 內所紀錄的資料放置於哪些 block 號碼內
4. 讀出 block 號碼內的資料,組合起來成為一個檔案的實際內容。
---
至於新建檔案的流程則是這樣的:
1. 有寫入檔案的需求時,先到 metadata 區找到沒有使用中的 inode 號碼
2. 到該 inode 號碼內,將所需要的權限與屬性相關資料寫入,然後在 metadata 區規範該 inode 為使用中,且更新 superblock 資訊
3. 到 metadata 區找到沒有使用中的 block 號碼,將所需要的實際資料寫入 block 當中,若資料量太大,則繼續到 metadata 當中找到更多的未使用中的 block 號碼,持續寫入,直到寫完資料為止。
4. 同步更新 inode 的紀錄與 superblock 的內容。
---
至於刪除檔案的流程則是這樣的:
1. 將該檔案的 inode 號碼與找到所屬相關的 block 號碼內容抹除
2. 將 metadata 區域的相對應的 inode 與 block 號碼規範為未使用。
3. 同步更新 superblock 資料。
---
## DEMO
### inode 在路徑解析中扮演的角色
- 若是相對路徑(如 `./file.txt`),Linux 會基於當前目錄的 inode 來解析該路徑。
- 若是絕對路徑(如 `/home/user/file.txt`),Linux 會從根目錄 `/` 開始查找。
例如 `/home/user/file.txt` 這個絕對路徑:
- 先找到根目錄 `/` 的 inode。
- 在 `/` 內查找 `home`,找到 `home` 目錄的 inode。
- 在 `home` 內查找 `user`,找到 `user` 目錄的 inode。
- 在 `user` 內查找 `file.txt`,取得其 inode 並存取內容。
```cpp=
#include <iostream>
#include <sys/stat.h>
#include <cstring>
#include <vector>
#include <sstream>
void printInodes(const std::string &path) {
std::vector<std::string> parts;
std::stringstream ss(path);
std::string segment;
// 將路徑拆成一部分一部分
while (std::getline(ss, segment, '/')) {
if (!segment.empty()) {
parts.push_back(segment);
}
}
std::string currentPath = "/";
struct stat fileStat;
std::cout << "Path Segments and Inodes:\n";
// 從根目錄 `/` 開始,一層一層查找
for (const auto &part : parts) {
currentPath += part; // 拼接路徑
if (stat(currentPath.c_str(), &fileStat) == 0) {
std::cout << currentPath << " -> inode: " << fileStat.st_ino << std::endl;
} else {
std::cerr << "Error accessing: " << currentPath << std::endl;
break;
}
currentPath += "/"; // 加上斜線,準備下一層
}
}
int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <file_path>\n";
return 1;
}
printInodes(argv[1]);
return 0;
}
```
```bash=
g++ -o inode_finder inode_finder.cpp
./inode_finder /home/user/file.txt
```
程式運作方式
1. 輸入一個檔案的完整路徑(例如 `/home/user/file.txt`)。
2. 拆解成 `/`, `/home`, `/home/user`, `/home/user/file.txt`,並逐層查詢 inode。
3. 使用 stat() 取得每個路徑的 inode,並輸出到終端機。
Example:
```bash=
Path Segments and Inodes:
/ -> inode: 2
/home -> inode: 12345
/home/user -> inode: 67890
/home/user/file.txt -> inode: 13579
```
這表示:
`/` 的 inode 是 2(Linux 中根目錄通常是 2)。
`/home` 的 inode 是 12345。
`/home/user` 的 inode 是 67890。
`/home/user/file.txt` 的 inode 是 13579。
理解系統如何找到檔案,如果想驗證 inode 是否正確指向檔案,可以試著對比 `ls -i` 輸出