Linux —— 檔案描述符
檔案描述符 Fd
當程式開啟檔案或建立新檔案時,核心會返回一個檔案描述符(非負整數),用來指向被開啟的檔案,所有執行I/O操作的系統呼叫(read、write)都會通過檔案描述符。
檔案描述符可以理解為程式檔案描述表這個表的索引,或者把檔案描述表看做一個陣列的話,檔案描述符可以看做是陣列的下標。當需要進行I/O操作的時候,會傳入fd作為引數,先從程式檔案描述符表查詢該fd對應的那個條目,取出對應的那個已經開啟的檔案的控制程式碼,根據檔案控制程式碼指向,去系統fd表中查詢到該檔案指向的inode,從而定位到該檔案的真正位置,從而進行I/O操作。
特點:
- 每個檔案描述符會與一個開啟的檔案相對應
- 不同的檔案描述符也可能指向同一個檔案
- 相同的檔案可以被不同的程式開啟,也可以在同一個程式被多次開啟
相關的三張表:
-
程式級的檔案描述符表
struct task_struct { //... struct files_struct *files // 程式級別的檔案描述符表 //... };
-
系統級的檔案描述符表
核心對系統所有開啟的檔案維護了一個
開啟檔案表
,表中每一項稱為開啟檔案控制程式碼
,一個開啟檔案控制程式碼描述了一個開啟檔案的全部資訊- 當前檔案偏移量(呼叫read()和write()時更新,或使用lseek()直接修改)
- 開啟檔案時所使用的狀態標識(即,open()的flags引數)
- 檔案訪問模式(如呼叫open()時所設定的只讀模式、只寫模式或讀寫模式)
- 與訊號驅動相關的設定
- 對該檔案i-node物件的引用
- 檔案型別(例如:常規檔案、套接字或FIFO)和訪問許可權
- 一個指標,指向該檔案所持有的鎖列表
- 檔案的各種屬性,包括檔案大小以及與不同型別操作相關的時間戳
-
檔案系統的inode表
每個檔案系統會為儲存於其上的所有檔案維護一個inode表
檔案描述符表、開啟檔案表、inode表之間的關係:
程式A檔案描述符1和20指向同一個開啟檔案控制程式碼,是因為多次呼叫open()等函式開啟同一個檔案導致。
程式A的檔案描述符2和程式B的檔案描述符2指向同一個開啟檔案控制程式碼可能是因為呼叫fork()後出現的,子程式會繼承父程式的開啟檔案描述符表,也就是子程式繼承父程式開啟檔案。;或者某程式通過unix域套接字將一個開啟的檔案描述符傳遞給另一個程式;或者不通程式獨自呼叫open函式開啟同一個檔案是正好分配到與其他程式開啟該檔案描述符一樣。
程式A的描述符0和程式B的描述符3分別指向不同的開啟檔案控制程式碼,但這些控制程式碼均指向i-node表的相同條目,即同一個檔案,發生這種情況是因為每個程式各自對同一個檔案發起了open()呼叫。同一個程式兩次開啟同一個檔案,也會發生類似情況。
檔案指標 *FILE
C語言中使用的是檔案指標而不是檔案描述符作為I/O的控制程式碼,“檔案指標(file pointer)”指向程式使用者區中的一個被稱為FILE結構的資料結構。當通過檔案指標操作檔案時,需要呼叫C語言stdio.h中提供的檔案API(fopen()、fread()等)。
檔案描述符在POSIX系統呼叫中直接可見,檔案指標是C語言在其基礎上的包裝。
int open(const char *path, int access,int mode)
FILE *fopen(char *filename, char *mode)
檔案路徑 到 檔案指標:filepath --fopen()-->FILE*;
檔案路徑 到 檔案描述符:filepath--open()--fd;
檔案描述符 到 檔案指標:fd--fdopen()-->FILE*;
檔案指標 到 檔案描述符:FILE*--fileno()--->fd;
索引節點 Inode
index node是類unix系統中儲存檔案系統中物件後設資料的資料結構。
inode主要儲存以下資料:
- inode編號
- 檔案大小
- 佔用的塊數目與塊大小
- 檔案型別(普通檔案、目錄、管道,etc.)
- 儲存該檔案的裝置號
- 連結數目
- 讀、寫、執行許可權
- 擁有者的使用者ID和組ID
- 檔案的最近訪問、資料最近修改時間
- inode最近修改時間
stat
命令可以檢視後設資料,`df -i檢視每個硬碟分割槽的inode總數和已經使用的數量。除了檔名以外的所有資訊,都存在inode中。
inode也會消耗硬碟空間,所以硬碟格式化的時候,作業系統自動將硬碟分成兩個區域。一個是資料區,存放檔案資料;另一個是inode區(inode table),存放inode所包含的資訊。
每個inode節點的大小,一般是128位元組
或256位元組
。inode節點的總數,在格式化時就給定,一般是每1KB或每2KB就設定一個inode。假定在一塊1GB的硬碟中,每個inode節點的大小為128位元組,每1KB就設定一個inode,那麼inode table的大小就會達到128MB,佔整塊硬碟的12.8%。
每個檔案都有一個inode,因此有可能inode已經用完但是硬碟還未存滿的情況。linux系統不使用檔名而使用inode來識別檔案。
表面上,使用者通過檔名,開啟檔案。實際上,系統內部這個過程分成三步:首先,系統找到這個檔名對應的inode號碼;其次,通過inode號碼,獲取inode資訊;最後,根據inode資訊,找到檔案資料所在的block,讀出資料。
目錄檔案就是由一系列目錄項組成的資料結構,每個目錄項包含檔名和inode號碼兩部分。
Inode特殊作用
- 有時,檔名包含特殊字元,無法正常刪除。這時,直接刪除inode節點,就能起到刪除檔案的作用。
- 移動檔案或重新命名檔案,只是改變檔名,不影響inode號碼。
- 開啟一個檔案以後,系統就以inode號碼來識別這個檔案,不再考慮檔名。因此,通常來說,系統無法從inode號碼得知檔名。
第3點使得軟體更新變得簡單,可以在不關閉軟體的情況下進行更新,不需要重啟。因為系統通過inode號碼,識別執行中的檔案,不通過檔名。更新的時候,新版檔案以同樣的檔名,生成一個新的inode,不會影響到執行中的檔案。等到下一次執行這個軟體的時候,檔名就自動指向新版檔案,舊版檔案的inode則被回收。
擴充
磁碟結構
檔案儲存在硬碟上,硬碟的最小儲存單位叫做”扇區”(Sector)。每個扇區儲存512位元組
(相當於0.5KB)。
作業系統讀取硬碟的時候,不會一個個扇區地讀取,這樣效率太低,而是一次性連續讀取多個扇區,即一次性讀取一個”塊”(block)。這種由多個扇區組成的”塊”,是檔案存取的最小單位。”塊”的大小,最常見的是4KB,即連續八個 sector組成一個 block。
由上,可用(柱面號,盤面號,扇區號)
來定位任意一個“磁碟塊”,我們經常提到檔案資料存放在外存中的幾號塊(邏輯地址),這個塊號就可以轉換成(柱面號,盤面號,扇區號)的地址形式。
可根據該地址讀取一個“塊”,操作如下:
① 根據“柱面號”移動磁臂,讓磁頭指向指定柱面(也稱磁軌)
② 啟用指定盤面對應的磁頭;
③ 磁碟旋轉的過程中,指定的扇區會從磁頭下面劃過,這樣就完成了對指定扇區的讀/寫。