在C程序中,文件由文件指針或者文件描述符表示。ISO C的標准I/0庫函數(fopen, fclose, fread, fwrite, fscanf, fprintf等)使用文件指針,UNIX的I/O函數(open, close, read, write, ioctl)使用文 件描述符。下面重點來說下,文件描述符是如何工作的。
文件描述符相當於一個邏輯句柄,而 open,close等函數則是將文件或者物理設備與句柄相關聯。句柄是一個整數,可以理解為進程特定的文件描述 符表的 索引。先介紹下面三個概念,後面講下open、close等操作以後,文件和文件描述符產生什麼關系,以 及fork後文件描述符的繼承等問題。
文件描述符表:用戶區的一部分,除非通過使用文件描述符的函 數,否則程序無法對其進行訪問。對進程中每個打開的文件,文件描述符表都包含一個條目。
系統文件表 :為系統中所有的進程共享。對每個活動的open, 它都包含一個條目。每個系統文件表的條目都包含文件偏移 量、訪問模式(讀、寫、or 讀-寫)以及指向它的文件描述符表的條目計數。
內存索引節點表: 對系統中 的每個活動的文件(被某個進程打開了),內存中索引節點表都包含一個條目。幾個系統文件表條目可能對應 於同一個內存索引節點表(不同進程打開同一個文件)。
1、舉例: 執行myfd = open( "/home/lucy/my.dat", O_RDONLY); 以後,上述3個表的關系原理圖如下:
系統文件表包含 一個偏移量,給出了文件當前的位置。若2個進程同時打開一個文件(如上圖A,B)做讀操作,每個進程都有自 己相對於文件的偏移量,而且讀入 整個文件是獨立於另一個進程的;如果2個進程打開同一個文件做寫操作, 寫操作是相互獨立的,每個進程都可以重寫另一個進程寫入的內容。
如果上面進程在open以後又執行了 close()函數,操作系統會刪除文件描述符表的第四個條目和系統文件表的對應條目(若指向它的描述符表 唯一), 並對內存索引節點表條目中的計數減1,如果自減以後變為0,說明沒有其他進程鏈接此文件,將索 引節點表條目也刪除,而這裡進程B也在open這個文件,所 以索引節點表條目保留。
2、文件描述符 的繼承
通過fork()創建子進程時,子進程繼承父進程環境和上下文的大部分內容的拷貝,其中就包括文 件描述符表。
(1)對於父進程在fork()之前打開的文件來說,子進程都會繼承,與父進程共享相同的文 件偏移量。如下圖所示(0-1-2 表示 標准輸入-輸出-錯誤):
系統文件表位於系統空間中,不會被 fork()復制,但是系統文件表中的條目會保存指向它的文件描述符表的計數,fork()時需要對這個計數進行維 護, 以體現子進程對應的新的文件描述符表也指向它。程序關閉文件時,也是將系統文件表條目內部的計數 減一,當計數值減為0時,才將其刪除。
(2)相反,如果父進程先進程fork,再打開my.dat,這時父子進 程關於my.dat 的文件描述符表指向不同的系統文件表條目,也不再共享文件偏移量(fork以後2個進程分別 open,在系統文件表中創建2個條目);但是關於標准輸入, 標准輸出,標准錯誤,父子進程還是共享的。