01. C/系統調用文件操作
C/系統調用文件操作
02. 文件系統(ext2)結構
Linux ext2文件系統,上圖為磁盤文件系統圖(內核內存映像肯定有所不同),磁盤是典型的塊設備,硬盤分區被劃分為一個個的block。一個塊的大小(有1MB,2MB或4MB)是由格式化的時候確定的,并且不可以更改。而上圖中啟動塊引導快的大小是確定的。每個塊組都有著相同的結構組成。
超級塊和塊組描述符表 會在多個塊組中備份,以提高容錯能力。其余結構(數據塊位圖、inode 位圖、inode 表、數據塊)是每個塊組獨立管理的。
文件訪問:
- 通過目錄數據塊找到文件名對應的inode
- 從inode表中讀取inode號,在文件混合索引內找到獲取數據塊指針
- 根據指針訪問文件內容
文件創建:
- 在位示圖找到為o的inode分配并寫入inode表
- 在文件數據塊找到空閑塊分配給它
- 更新數據塊,并添加文件名和inode的映射關系
文件刪除:
- 位示圖和數據塊位圖置0,并在目錄數據塊刪除此二元關系
2.1 文件物理結構
2.1.1 連續分配
文件存儲在物理上連續的磁盤塊上,通過始址和長度(磁盤所占塊數)定位。
特點:
- 順序訪問高效:磁頭移動少,適合順序讀寫(如視頻流)。
- 有外部碎片,擴展困難(文件動態增長與增刪)
2.1.2 鏈接分配
文件分散存儲在磁盤塊上,每塊包含指向下一塊的指針(鏈式結構)。鏈接分配有隱式鏈接和顯示鏈接(FAT
)兩種
隱式鏈接特點:
- 無外部碎片,能動態擴展(增刪)。
- 指針占用塊空間,訪問第
i
塊必須從表頭遍歷鏈表
文件分配表(FAT):集中存儲塊鏈接關系,避免塊內指針開銷,但需將FAT常駐內存以提高性能。
2.1.3 索引分配
為每個文件創建索引塊,存儲其所有數據塊的地址。
特點:
- 支持隨機訪問:直接通過索引定位任意塊。
- 無外部碎片:靈活分配空間。
2.2 超級塊
存放文件系統本身的結構信息。記錄的信息主要有:bolck 和 inode的總量,未使用的block和inode的數量,一個block和inode的大小,最近一次掛載的時間,最近一次寫入數據的時間,最近一次檢驗磁盤的時間等其他文件系統的相關信息。Super Block的信息被破壞,可以說整個文件系統結構就被破壞了
struct ext2_super_block {__le32 s_inodes_count; /* 索引節點總數 */__le32 s_blocks_count; /* 塊總數 */__le32 s_r_blocks_count; /* 保留的塊數 */__le32 s_free_blocks_count; /* 空閑塊計數器 */__le32 s_free_inodes_count; /* 空閑索引節點計數器 */__le32 s_first_data_block; /* 第一個數據塊的塊號,總是為1 */...__le32 s_first_meta_bg; /* 第一個元塊組 */__u32 s_reserved[190]; /* 填充到塊的末尾 */
};
2.3 塊組描述符表
描述塊組屬性信息
2.4 位示圖
塊組中 Inode 的分配狀態(已用/空閑)
struct ext2_inode {__le16 i_mode; /* 文件類型和訪問權限,查看S_ISREG()等函數 */__le16 i_uid; /* 所有者 Uid 的低 16 位,擁有者id */// 文件長度,最高位沒使用,最大表示2GB文件,大于2GB文件再使用i_dir_acl字段__le32 i_size; /* 大小(字節) */__le32 i_atime; /* 訪問時間 */__le32 i_ctime; /* 索引節點創建時間 */__le32 i_mtime; /* 文件數據最后改變時間 */__le32 i_dtime; /* 刪除時間 */__le16 i_gid; /* 組 ID 的低 16 位,用戶組id */__le16 i_links_count; /* 硬鏈接計數 */
...
};
2.5 文件描述符fd
fd是操作系統中用于標識和訪問已打開文件或I/O資源的整數標識符。存放
- 每個進程的
task_struct
包含一個files_struct
結構,內部維護一個 fd 數組 file*fd_array[]
中存放多個file*
- 數組索引(下標)即為
fd
,數組內部元素指向內核中的file
對象
在file*fd_array[]
中下標為0
、1
、2
分別預留給stdin
,stdout
,stderr
,所以新分配的原則是選擇未使用最小的開始分配。使用close()
即可釋放任意已分配的fd,進程結束未關閉的fd
由內核自動回收。
2.6 重定向
fgets
是語言層面的讀取函數,其底層也是用了系統標準輸入接口。其中寫死了只能對0號文件描述符
進行讀取操作。而現在上邊的代碼將0號文件描述符
給了新的文件。故該程序將讀出log.txt
文件中的內容。這里完成了輸入重定向。
系統調用實現重定向:
int dup2(int oldfd, int newfd);
oldfd
:要被復制已打開的fd
newfd
:新的描述符編號
返回值:成功返回 newfd
,失敗返回 -1
并設置 errno
。
讓oldfd
的值變為newfd
旳值,效果等同于上面先將fd=1
指向的stdout
關閉,然后再將fd=1
分配給新打開的文件。
如:dup2(fd,1)
將原本向顯示器輸出的內容輸出到fd
指向的文件中。
2.7 inode
存儲文件或目錄的元數據(不包括文件名)
注:
- 在Unix文件系統中,每個文件必須對應一個inode結點,而 inode結點的總數量是有上限的。如:僅用8KB作為inode區,假設每個inode大小為64B,則該文件系統最多只能存儲8KB/64B=128個 inode結點,相應地,該系統最多只能支持128個文件。
- 當一個進程通過open系統調用打開某個文件時,操作系統需要將該文件對應的 inode結點讀入主存。
2.8 數據塊
- 作用:存儲文件的實際內容或目錄的結構信息。
- 類型:
- 文件數據塊:存儲普通文件的實際內容(如文本、圖片等)。
- 目錄數據塊:存儲該目錄下的條目(文件名 + Inode 號)。
- 符號鏈接數據塊:若鏈接路徑較短,直接存儲在 Inode 中;較長時占用數據塊。
- 特殊文件:如設備文件(/dev/sda)的元數據存儲在 Inode 中,無需數據塊。
文件數據塊:
當一個文件過大時,則需要多個塊儲存數據。
目錄文件數據塊:
存放的是目錄下文件名和inode的二元映射關系。
2.9 緩沖區
緩沖區的存在,能夠提高數據效率。一般c語言庫函數寫入文件采用的是全緩沖,而寫入顯示器是航緩存。
如果一塊數據多次分批寫入外存效率低開銷大,但是一次性寫入效率最高。
- 全緩沖:文件輸出,效率高但實時性差,緩沖區滿
I/O
。 - 行緩沖:**終端(C庫)**輸出,遇到
\n
或緩沖區滿時刷新。 - 無緩沖:如
stderr
,立即輸出。
緩沖區刷新:
- 用戶強制刷新
- 進程退出
例如有語句char *str="helloworld"
,首先將該字符串存放在FILE結構體
內的C緩沖區,滿足條件后將數據刷新到內核緩沖區,在這個區域由OS
維護和操作。
首先關閉文件描述符為1
,open
分配的log.txt
文件的fd為1
,后面printf
和fprintf
會對新打開的文件(fd=1
)執行新操作,改變了刷新機制。但是close(fd)
使得文件描述符被關閉,C緩沖區
內容無法向OS文件內核緩沖區
刷新,故不會將數據刷新到log.txt
內。
03. 軟硬鏈接
二者區別:軟鏈接會創建新的inode,而硬連接不會。
硬鏈接:
- 在目錄條目中新增一個文件名,指向同一 inode。
- inode 的
引用計數
會遞增(可通過stat
命令查看)
軟鏈接:
- 創建一個新的 inode 和文件,文件內容為目標路徑字符串。
- 訪問軟鏈接時,內核會遞歸解析路徑。
04. 動靜態庫
待寫…