Linux下的FILE*結構體
FILE*結構體解析
struct file結構體定義在include/Linux/fs.h中定義。檔案結構體代表一個開啟的檔案,系統中的每個開啟的檔案在核心空間都有一個關聯的
struct file。它由核心在開啟檔案時建立,並傳遞給在檔案上進行操作的任何函式。在檔案的所有例項都關閉後,核心釋放這個資料結構。在核心建立和驅動原始碼中,struct
file的指標通常被命名為file或filp。如下所示:
struct file {
union {
struct list_head fu_list; //檔案物件連結串列指標linux/include/linux/list.h
struct rcu_head fu_rcuhead; //RCU(Read-Copy Update)是Linux 2.6核心中新的鎖機制
} f_u;
struct path f_path; //包含dentry和mnt兩個成員,用於確定檔案路徑
#define f_dentry f_path.dentry //f_path的成員之一,當前檔案的dentry結構
#define f_vfsmnt f_path.mnt //表示當前檔案所在檔案系統的掛載根目錄
const struct file_operations *f_op; //與該檔案相關聯的操作函式
atomic_t f_count; //檔案的引用計數(有多少程序開啟該檔案)
unsigned int f_flags; //對應於open時指定的flag
mode_t f_mode; //讀寫模式:open的mod_t mode引數
loff_t f_pos;//當前檔案指標位置
off_t f_pos; //該檔案在當前程序中的檔案偏移量
struct fown_struct f_owner; //該結構的作用是通過訊號進行I/O時間通知的資料。
unsigned int f_uid, f_gid;// 檔案所有者id,所有者組id
struct file_ra_state f_ra; //在linux/include/linux/fs.h中定義,檔案預讀相關
unsigned long f_version;//記錄檔案的版本號,每次使用之後遞增
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;//使用這個成員來指向分配的資料
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
其中有兩個非常重要的欄位:檔案描述符和緩衝區
檔案描述符fd:
fd只是一個小整數,在open時產生。起到一個索引的作用,程序通過PCB中的檔案描述符表找到該fd所指向的檔案指標filp。
檔案描述符的操作(如: open)返回的是一個檔案描述符,核心會在每個程序空間中維護一個檔案描述符表, 所有開啟的檔案都將通過此表中的檔案描述符來引用;
而流(如: fopen)返回的是一個FILE結構指標, FILE結構是包含有檔案描述符的,FILE結構函式可以看作是對fd直接操作的系統呼叫的封裝, 它的優點是帶有I/O快取。
每個程序在PCB(Process Control Block)即程序控制塊中都儲存著一份檔案描述符表,檔案描述符就是這個表的索引,檔案描述表中每個表項都有一個指向已開啟檔案的指標,現在我們明確一下:已開啟的檔案在核心中用file結構體表示,檔案描述符表中的指標指向file結構體。
緩衝區:
1、緩衝區機制
根據應用程式對檔案的訪問方式,即是否存在緩衝區,對檔案的訪問可以分為帶緩衝區的操作和非緩衝區的檔案操作:
① 帶緩衝區檔案操作:高階標準檔案I/O操作,將會在使用者空間中自動為正在使用的檔案開闢記憶體緩衝區。
② 非緩衝區檔案操作:低階檔案I/O操作,讀寫檔案時,不會開闢對檔案操作的緩衝區,直接通過系統呼叫對磁碟進行操作(讀、寫等),當然用於可以在自己的程式中為每個檔案設定緩衝區。
兩種檔案操作的解釋和比較:
(1)非緩衝的檔案操作訪問方式,每次對檔案進行一次讀寫操作時,都需要使用讀寫系統呼叫來處理此操作,即需要執行一次系統呼叫,執行一次系統呼叫將涉及到CPU狀態的切換,即從使用者空間切換到核心空間,實現程序上下文的切換,這將損耗一定的CPU時間,頻繁的磁碟訪問對程式的執行效率造成很大的影響。
(2)ANSI標準C庫函式是建立在底層的系統呼叫之上,即C函式庫檔案訪問函式的實現中使用了低階檔案I/O系統呼叫,ANSI標準C庫中的檔案處理函式為了減少使用系統呼叫的次數,提高效率,採用緩衝機制,這樣,可以在磁碟檔案進行操作時,可以一次從檔案中讀出大量的資料到緩衝區中,以後對這部分的訪問就不需要再使用系統呼叫了,即需要少量的CPU狀態切換,提高了效率。
2、緩衝型別標準I/O提供了3種類型的緩衝區。
(1)全緩衝區:這種緩衝方式要求填滿整個緩衝區後才進行I/O系統呼叫操作。對於磁碟檔案的操作通常使用全緩衝的方式訪問。第一次執行I/O操作時,ANSI標準的檔案管理函式通過呼叫malloc函式獲得需要使用的緩衝區,預設大小為8192。
(2)行緩衝區:在行緩衝情況下,當在輸入和輸出中遇到換行符時,標準I/O庫函式將會執行系統呼叫操作。當所操作的流涉及一個終端時(例如標準輸入和標準輸出),使用行緩衝方式。因為標準I/O庫每行的緩衝區長度是固定的,所以只要填滿了緩衝區,即使還沒有遇到換行符,也會執行I/O系統呼叫操作,預設行緩衝區的大小為1024。
(3)無緩衝區:無緩衝區是指標準I/O庫不對字元進行快取,直接呼叫系統呼叫。標準出錯流stderr通常是不帶緩衝區的,這使得出錯資訊能夠儘快地顯示出來。
注:
①標準輸入和標準輸出裝置:當且僅當不涉及互動作用裝置時,標準輸入流和標準輸出流才是全緩衝的。
②標準錯誤輸出裝置:標準出錯絕不會是全緩衝方式的。
③對於任何一個給定的流,可以呼叫setbuf()和setvbuf()函式更改其緩衝區型別。
接下來,我們通過程式演示一下緩衝區的作用
我們可以看到輸出到螢幕只有5條輸出命令而輸出到檔案有7條輸出命令。
根據輸出結果我們可以看出printf和fwrite重複寫了兩次,沒有重複列印的是write.。
printf和fwrite都是庫函式,而write則是系統直接呼叫的:結合已有知識,我們瞭解到當使用庫函式命令時,列印訊息並沒有直接寫到輸出位置上,而是先把資料寫到輸出緩衝區,在重新整理至輸出位置。
1、當輸出目標位置為輸出到顯示器時,則重新整理方式是行重新整理;
2、當輸出目標位置為輸出到檔案中時,重新整理方式由行緩衝變為全緩衝,全緩衝是指當把緩衝區寫滿後才能重新整理。(或者強制重新整理)程式碼中printf和fwrite第一次列印在fork操作之前,第二次列印則在fork操作之後,原因是在fork操作前,printf和fwrite的輸出命令將資料先寫到緩衝區中,此時只執行了這兩條命令,由於是全緩衝的重新整理方式,所以這兩條命令並不足以將快取寫滿,所以資料暫存在緩衝區中;然後進行fork建立子程序,由於fork創建出的父子程序程式碼共享,而資料不共享,各自私有一份,緩衝區中的資料都屬於資料,因為父程序的殘留資料還在緩衝區中,所以fork完畢後,父子程序將快取中的資料各自存一份有父程序殘留資料的資料,所以當父子程序各自重新整理時,父子程序會各自執行一次printf和fwrite的輸出命令,所以命令就從原來的兩條變為四條。
struct file 的其他重要成員有:
1.mode_t f_mode;
檔案模式確定檔案是可讀的或者是可寫的(或者都是), 通過位 FMODE_READ 和FMODE_WRITE. 你可能想在你的 open 或者 ioctl 函式中檢查這個成員的讀寫許可, 但是不需要檢查讀寫許可, 因為核心在呼叫你的方法之前檢查. 當檔案還沒有為那種存取而開啟時讀或寫的企圖被拒絕, 驅動甚至不知道這個情況.
2.loff_t f_pos;
當前讀寫位置. loff_t 在所有平臺都是 64 位( 在 gcc 術語裡是 long long ). 驅動可以讀這個值,如果它需要知道檔案中的當前位置, 但是正常地不應該改變它; 讀和寫應當使用它們作為最後引數而收到的指標來更新一個位置, 代替直接作用於 filp->f_pos. 這個規則的一個例外是在 llseek 方法中, 它的目的就是改變檔案位置.
3.unsigned int f_flags;
這些是檔案標誌, 例如 O_RDONLY, O_NONBLOCK, 和 O_SYNC. 驅動應當檢查O_NONBLOCK 標誌來看是否是請求非阻塞操作; 其他標誌很少使用. 特別地, 應當檢查讀/寫許可, 使用 f_mode 而不是f_flags. 所有的標誌在標頭檔案