磁盤
文件 = 文件屬性 + 文件內容
文件內容 —— 數據塊,文件屬性 —— inode
Linux 文件在磁盤中的存儲,是將 屬性 與 內容 分開存儲的
內存:掉電易失,磁盤:永久性存儲介質

磁盤訪問的基本單元:扇區 —— 每個扇區一般存儲 512 byte / 4 kb 數據
磁盤由無數個扇區構成,定位扇區的步驟(CHS Cylinder-Head-Sector 尋址方式):6個磁頭(Head)定位,哪一個磁道 / 柱面(Cylinder),定位扇區(Sector)。
但現代大容量硬盤已改用 LBA(Logical Block Addressing)線性尋址方式:將磁盤在邏輯上 抽象成 線性結構 —— 無數個扇區組成,每個扇區都有自己的下標
LBA <=> CHS
這兩種地址可以相互轉換,但使用者直接使用 LBA 地址,磁盤內部自己轉換為 CHS地址尋找。
邏輯上,磁盤就是一個 元素為 扇區 的一維數組,數組的下標 就是每一個 扇區的 LBA地址,操作系統就可以用 一個數字(下標) 來訪問磁盤的扇區。
文件系統
塊 概念
硬盤是典型的 塊 設備,操作系統讀取硬盤數據時,不會一個個扇區進行讀取;為提高效率,一次性讀取一個 塊,一個 塊 包含連續的多個扇區。
硬盤的每個分區 是被劃分為多個 塊;
一個 塊 包含幾個連續扇區,是由格式化的時候確定的,不可更改,常見一個塊大小 4 KB,即 8個連續扇區(512 b)組成一個 塊。
塊 是文件讀取的最小單位。
分區 概念
一個磁盤被分為多個分區(C、D、E),分區實質上說,是對硬盤的一種格式化
如何分區?
柱面?是分區的最小單位(我覺得 磁道 更形象),本質就是設置 每個分區的 起始柱面和結束柱面 號碼。
每個分區的 boot sector 啟動塊 大小確定,1 KB,用以存儲 磁盤分區信息和啟動信息。
inode 概念
每個文件的 文件數據 都儲存在 塊 中,對應的 屬性信息 儲存在 inode 中(不包括文件名),一個 inode 有一個唯一標識符 —— inode 號(用以標識文件),一般 128 B 大小。
block group 塊組
ext2 文件系統根據 分區的大小劃分為多個 block group。
super block 超級塊
存放文件系統本身的結構信息,描述整個分區的文件系統信息:
block 和 inode 的總量、未使用的數量、一個 block 和 inode 的大小,最近一次的掛載時間,最近一次寫入數據的時間,最近一次檢驗磁盤的時間 等其他文件系統的相關信息。
所以 super block 的數據如果被破壞,整個文件系統結構就被破壞了;
但是,為了保證文件系統在磁盤的 部分扇區 出現物理問題時還能工作,一個 文件系統 的 super block 在 每個或者多個 block group 中進行備份,且數據保持一致。
GDT 塊組描述符表
Group Descriptor Table 塊組描述符表,表中包含多個 塊組描述符,每個 描述 一個 塊組 的屬性信息:
這個塊組從哪里開始是 inode table,從哪里開始是 data blocks,還有多少空閑的?inode、data blocks。
注意:整個文件系統只有一份“塊組描述符表”(Group Descriptor Table)。
這張表通常放在 塊組 0(有時是塊組 0 和 1 的備份) 里,其余所有塊組不再存放完整的描述符表副本。(ext4 為了冗余會把描述符表做備份,但也只是少數幾個塊組有,并非“每個塊組都相同”。)
data blocks 數據塊
存放文件內容的區域,以 塊 的形式呈現,常見 一個 塊(4 KB) 大小為 8 個 扇區(512 B);
文件系統的 塊 必須 ≥ 扇區大小,起到 預加載 的作用,提高讀取效率。
對普通文件:數據存儲在 數據塊 中;
對目錄:該目錄下的所有文件名和目錄名 存儲在 所在目錄的 數據塊 中,除了文件名外,ls -l 命令看到的其他文件信息,保存在 該文件的 inode 中。
數據塊中的 block 塊 按照分區整體劃分,不可跨分區。
inode table 節點表
當前分區 所有 inode 的集合,inode table 中的 inode 編號以分區為單位,整體劃分,不可 跨分區。
每個 inode 的結構中,會包含 12 個直接數據塊指針,和幾個 一級、二級、三級間接塊指針:這樣保證了 大文件的存儲
block bitmap 塊位圖
位圖中的 bit 位 的位置 與 塊號 的形成映射,bit 位 的內容,表示 該位置 對應的 塊 是否被使用;
所以,刪除一個文件時,無須清空其 data blocks 的內容,只需找到這個文件的 inode,inode 中有指向該文件的 塊指針、大小 等信息,將其 數據塊 對應的 塊位圖 中的位置 的 bit位 置空,就算是在 邏輯上 刪掉了。
inode bitmap inode位圖
和 塊位圖 類似,表示 inode table中?對應位置的 inode 是否被使用;
所以,刪除一個文件時,對其?block bitmap 塊位圖 和?inode bitmap inode位圖 做處理即可,提升效率。
小結
每個分區相互?獨立、包括 inode 編號、數據塊編號?等、
一個分區中可用的 inode 總數,是確定的,用完了就沒辦法了(有可能 inode 用完了,data block 塊還有,因為 inode 是確定的,預分配好的;也有可能 data block 用完了,但是 inode 還有)
每個 塊組 中能用多少個 inode ,塊組最開始的 super block、GDT 中有相應信息。
單個文件 = 一個 inode + 若干data blocks
(先描述,再組織)
格式化 : 每個分區在被使用前,都必須提前將部分文件系統的屬性信息,提前設置進對應的分區中,以便后續使用
inode 表征了文件的所有屬性,但是,文件名并不屬于文件屬性!
文件的增刪查改
新建文件
通過新建文件的路徑:確定在哪一個分區里 —— 然后查 GDT,inode 使用情況 —— 查 inode bitmap,分配空閑 inode
—— 填入文件屬性 —— 準備寫入 —— 確認寫入的數據大小(例如 write 中有參數問你 寫入數據大小)
—— 查block bitmap ,分配數據塊 —— 并把數據塊的編號,填入 inode 的屬性之一:與數據塊的 映射數組
—— 然后通過映射數組,把數據寫入到對應的數據塊中
刪除文件
數據內容刪除:inode 的 與 該文件所需的數據塊 的 映射數組中 找到該文件的 所有數據塊,將這些數據塊編號 對應的 block bitmap 中 置零
inode文件屬性刪除 : 然后 inode bitmap 也置零
刪除 = 允許被覆蓋
為什么不用清空data block 和 inode 內容的原因:過多的 IO 會大幅降低系統效率
數據恢復:數據塊被覆蓋之前,都可以想辦法恢復
查找文件
根據文件路徑確定 分區 —— 根據文件名 確定 inode編號,根據 inode編號 確定是哪個塊組
—— 根據 inode 編號,查 inode bitmap 確定是否有效文件
—— 從 inode bitmap 索引到 在 inode table 中的該 inode
—— 在 該 inode 中,找到 與數據塊的 映射數組(也就是塊指針,直接的或者間接的),
—— 將 映射數組中的 數據塊 的內容,載入內存(注意是整個數據塊,數據不一定占滿了整個數據塊)(當然如果文件過大,應該遵循 惰性加載)
—— 因為文件有大小,inode中存儲了占用的字節數,所以從該數據塊開始,讀取 文件大小的 字節
修改文件
先查找文件,找到后加載進內存,然后 IO
目錄文件
linux 下,一切皆文件!
文件 = 內容 + 屬性
目錄也是文件,有自己獨立的 inode,有自己的屬性(上一個圖中 藍色就是目錄文件)
那么
目錄的數據塊存什么?
存的是 該目錄下,文件的 文件名和 對應文件 inode 編號的 映射關系
1、所以 同一個目錄下,不能有同名文件,否則會沖突
2、目錄中 如果沒有 w 權限,無法創建文件:因為 這個文件的 inode編號 與 文件名的 映射關系,就寫不進 目錄文件中
3、目錄下沒有 r 權限,無法查看文件
4、目錄下,沒有 x 可執行權限,無法進入這個目錄
可是 —— 目錄文件的 inode 怎么獲取?
得從上一級 已經打開的目錄文件中 找, 一路找到 根目錄,然后從目錄一路返回
類似 遞歸, 需要把路徑中所有的目錄全部解析,出口是 / 根目錄
Linux 路徑解析 : 實際上,任何文件的打開,都從 該文件路徑的 根目錄開始,依次打開每一個目錄,依次訪問
系統會對 常見的常打開的目錄文件的 inode 進行 dentry緩存,這樣提高效率
所以在,訪問任何文件時,都需要有路徑
軟硬鏈接
軟鏈接
首先,軟鏈接是 一個獨立的文件,具有獨立的 inode
ln 命令:建立鏈接,從 后者,指向 前者
-s : 建立 軟鏈接
軟鏈接 的文件的 內容:所指向文件的 路徑
硬鏈接
硬鏈接 不是一個獨立的文件,因為 沒有獨立的 inode
ln 命令:建立硬鏈接,從 后者,指向 前者
建立硬鏈接之后,該文件 inode 屬性中的 引用計數,變為 2,表示有多少個文件名指向本文件
并且,硬鏈接的inode 與 源文件一樣,這表示,就是同一個文件
硬鏈接干了什么? :本質就是 在特定目錄的數據塊中,新增 文件名和指向文件的 inode編號的 映射關系
這也表明 多個文件名可以映射 同一個 inode編號
對比分析
每個 inode 內部都有一個 引用計數的 計數器 ( 有多少個 文件名 指向本 inode)
引用計數-- 直到 0 ,文件才會被刪掉
硬鏈接文件,會增加該文件的引用計數。
軟連接不影響 該文件的 引用計數,軟鏈接像是 windows 中的 ‘快捷方式',數據塊里保存了指向文件的 路徑。
應用
軟鏈接
可以讓文件更簡單的訪問、可以鏈接一個 目錄很深的可執行程序、
硬鏈接
以上圖為例,linux 目錄中,??. 當前目錄 是 /home/suo/test_git/linux/0825? 的硬鏈接、、 .. 上級目錄 是? /home/suo/test_git/linux? ?的硬鏈接、
為什么 . 當前目錄 的引用計數是 5 ?
因為 當前目錄中,我有 5 個文件,每個文件中都有 2 個隱藏的硬鏈接 .? 和 .. ,這個 .. 就是我當前目錄?/home/suo/test_git/linux/0825? 的硬鏈接。
為什么 .. 上級目錄的引用計數是 23 ?
因為 在 上級目錄? /home/suo/test_git/linux? 中,有 23個文件,每個文件里都有個 .. 上級目錄? 的硬鏈接,包括這個目錄? /home/suo/test_git/linux/0825??
所以 cd .. 的細節實現 :
.. 是上級目錄?/home/suo/test_git/linux 的硬鏈接 —— 所以從根目錄一路解析,獲取到了 目錄文件 linux/? 的 inode —— 所以 這個 inode 的路徑也就獲得了,切換到 上級目錄的路徑即可
所以硬鏈接 —— 可以維持 linux 下的 目錄結構
但是 :linux 中 不允許對目錄建立硬鏈接,容易出現 環 的問題
但 . 和 .. 本身就是目錄的硬鏈接,所以 這兩個硬鏈接是隱藏文件,而且,系統在搜索文件時,默認不會搜索這兩個 硬鏈接 的 目錄文件。避免了環的問題