8.2.4與程序相關的檔案結構
在具體介紹這幾個結構以前,我們需要解釋一下檔案描述符、開啟的檔案描述、系統開啟檔案表、使用者開啟檔案表的概念以及它們的聯絡。
1.檔案物件
在linux中,程序是通過檔案描述符(file descriptors,簡稱fd)而不是檔名來訪問檔案的,檔案描述符實際上是乙個整數。linux中規定每個程序能最多能同時使用nr_open
個檔案描述符,這個值在fs.h中定義,為1024*1024(2.0版中僅定義為256)。
每個檔案都有乙個32位的數字來表示下乙個讀寫的位元組位置,這個數字叫做檔案位置。每次開啟乙個檔案,除非明確要求,否則檔案位置都被置為0,即檔案的開始處,此後的讀或寫操作都將從檔案的開始處執行,但你可以通過執行系統呼叫lseek(隨機儲存)對這個檔案位置進行修改。linux中專門用了乙個資料結構file來儲存開啟檔案的檔案位置,這個結構稱為開啟的檔案描述(open file description)。這個資料結構的設定是煞費苦心的,因為它與程序的聯絡非常緊密,可以說這是vfs中乙個比較難於理解的資料結構。
首先,為什麼不把檔案位置乾脆存放在索引節點中,而要多此一舉,設乙個新的資料結構呢?我們知道,linux中的檔案是能夠共享的,假如把檔案位置存放在索引節點中,則如果有兩個或更多個程序同時開啟同乙個檔案時,它們將去訪問同乙個索引節點,於是乙個程序的lseek操作將影響到另乙個程序的讀操作,這顯然是不允許也是不可想象的。
另乙個想法是既然程序是通過檔案描述符訪問檔案的,為什麼不用乙個與檔案描述符陣列相平行的陣列來儲存每個開啟檔案的檔案位置?這個想法也是不能實現的,原因就在於在生成乙個新程序時,子程序要共享父程序的所有資訊,包括檔案描述符陣列。
我們知道,乙個檔案不僅可以被不同的程序分別開啟,而且也可以被同乙個程序先後多次開啟。乙個程序如果先後多次開啟同乙個檔案,則每一次開啟都要分配乙個新的檔案描述符,並且指向乙個新的file結構,儘管它們都指向同乙個索引節點,但是,如果乙個子程序不和父程序共享同乙個file結構,而是也如上面一樣,分配乙個新的file結構,會出現什麼情況了?讓我們來看乙個例子:
假設有乙個輸出重定位到某檔案a的shell script(shell指令碼),我們知道,shell是作為乙個程序執行的,當它生成第乙個子程序時,將以0作為a的檔案位置開始輸出,假設輸出了2k的資料,則現在檔案位置為2k。然後,shell繼續讀取指令碼,生成另乙個子程序,它要共享shell的file結構,也就是共享檔案位置,所以第二個程序的檔案位置是2k,將接著第乙個程序輸出內容的後面輸出。如果shell不和子程序共享檔案位置,則第二個程序就有可能重寫第乙個程序的輸出了,這顯然不是希望得到的結果。
至此,已經可以看出設定file結構的原因所在了。
file結構中主要儲存了檔案位置,此外,還把指向該檔案索引節點的指標也放在其中。file結構形成乙個雙鏈表,稱為系統開啟檔案表,其最大長度是nr_file,在fs.h中定義為8192。
file結構在include/linux/fs.h中定義如下:
struct
file ;
每個檔案物件總是包含在下列的乙個雙向迴圈鍊錶之中:
· 「未使用」檔案物件的鍊錶。該鍊錶既可以用做檔案物件的記憶體快取記憶體,又可以當作超級使用者的備用儲存器,也就是說,即使系統的動態記憶體用完,也允許超級使用者開啟檔案。由於這些物件是未使用的,它們的f_count域是null,該鍊錶首元素的位址存放在變數free_list中,核心必須確認該鍊錶總是至少包含nr_reserved_files
個物件,通常該值設為10。
· 「正在使用」檔案對的象鍊錶:該鍊錶中的每個元素至少由乙個程序使用,因此,各個元素的f_count
域不會為null,該鍊錶中第乙個元素的位址存放在變數anon_list中。
如果vfs需要分配乙個新的檔案物件,就呼叫函式get_empty_filp( )。該函式檢測「未使用」檔案物件鍊錶的元素個數是否多於nr_reserved_files,如果是,可以為新開啟的檔案使用其中的乙個元素;如果沒有,則退回到正常的記憶體分配。
2.使用者開啟檔案表
每個程序用乙個files_struct結構來記錄檔案描述符的使用情況,這個files_struct結構稱為使用者開啟檔案表,它是程序的私有資料。files_struct結構在include/linux/sched.h中定義如下:
struct
files_struct ;
fd域指向檔案物件的指標陣列。該陣列的長度存放在max_fds域中。通常,fd域指向files_struct結構的fd_array域,該域包括32個檔案物件指標。如果程序開啟的檔案數目多於32,核心就分配乙個新的、更大的檔案指標陣列,並將其位址存放在fd域中;核心同時也更新max_fds域的值。
對於在fd陣列中有入口位址的每個檔案來說,陣列的索引就是檔案描述符(file descriptor
)。通常,陣列的第乙個元素(索引為0)是程序的標準輸入檔案,陣列的第二個元素(索引為1)是程序的標準輸出檔案,陣列的第三個元素(索引為2)是程序的標準錯誤檔案(參見圖8.3)。請注意,借助於dup( )、dup2( )和fcntl( ) 系統呼叫,兩個檔案描述符就可以指向同乙個開啟的檔案,也就是說,陣列的兩個元素可能指向同乙個檔案物件。當使用者使用shell結構(如2>&1)將標準錯誤檔案重定向到標準輸出檔案上時,使用者總能看到這一點。
open_fds域包含open_fds_init域的位址,open_fds_init域表示當前已開啟檔案的檔案描述符的點陣圖。max_fdset域存放點陣圖中的位數。由於資料結構fd_set有1024位,通常不需要擴大點陣圖的大小。不過,如果確實必須的話,核心仍能動態增加點陣圖的大小,這非常類似檔案物件的陣列的情形。
當開始使用乙個檔案物件時呼叫核心提供的fget( )函式。這個函式接收檔案描述符fd作為引數,返回在current->files->fd[fd]中的位址,即對應檔案物件的位址,如果沒有任何檔案與fd對應,則返回null。在第一種情況下,fget( )使檔案物件引用計數器f_count的值增1。
stdin 0
stdin 1
stdin 2 3
圖8.3 檔案描述符陣列
當核心完成對檔案物件的使用時,呼叫核心提供的fput( ) 函式。該函式將檔案物件的位址作為引數,並遞減檔案物件引用計數器f_count的值,另外,如果這個域變為null,該函式就呼叫檔案操作的「釋放」方法(如果已定義),釋放相應的目錄項物件,並遞減對應索引節點物件的i_writeaccess域的值(如果該檔案是寫開啟),最後,將該檔案物件從「正在使用」鍊錶移到「未使用」鍊錶。
3.關於檔案系統資訊的fs_struct結構
第三個結構是fs_struct ,在2.4以前的版本中在include/linux/sched.h 中定義為:
struct
fs_struct ;
在2.4中,單獨定義在include/linux/fs_struct.h中:
struct
fs_struct ;
count域表示共享同一fs_struct 表的程序數目。umask域由umask()系統呼叫使用,用於為新建立的檔案設定初始檔案許可權。
fs_struct中的dentry結構是對乙個目錄項的描述,root、pwd及altroot三個指標都指向這個結構。其中,root所指向的dentry結構代表著本程序所在的根目錄,也就是在使用者登入進入系統時所看到的根目錄;pwd指向程序當前所在的目錄;而altroot則是為使用者設定的替換根目錄。實際執行時,這三個目錄不一定都在同乙個檔案系統中。例如,程序的根目錄通常是安裝於「/」節點上的ext2檔案系統,而當前工作目錄可能是安裝於/msdos的乙個dos檔案系統。因此,fs_struct結構中的rootmnt、pwdmnt及altrootmnt就是對那三個目錄的安裝點的描述,安裝點的資料結構為vfsmount。
Linux與程序相關的資訊
文字 英文縮寫 含義 pid process id 程序號 ppid parent id 父程序的pid號 user uid 執行該程序的使用者名稱及其uid tty 啟動該程序的終端 pri priority 程序的優先順序,數字越大表示優先順序越低 nice 程序的謙讓度,表示程序對cpu時間要...
與程序相關的命令ps kill
ubuntu中主要有如下操作程序的命令 ppid 父程序的 pid pid 程序的pid s 程序狀態,s 是指sleep睡眠狀態 t 是掛起狀態 r 是執行狀態 running z,叫殭屍狀態,使用ps e 檢視殭屍程序,ps el grep z 不斷的呼叫ps fp next ppid 和ps ...
程序與執行緒的相關操作
內容概要 內容詳情 一 程序補充 殭屍程序與孤兒程序 殭屍程序 主程序 執行完成之後不會直接結束,而是要等待所有子程序執行完 他們的資源之後才能結束。孤兒程序 主程序已經死亡 非正常死亡 兒子程序還在執行中。守護程序 守護程序 守護著某個程序,一旦守護的程序結束,守護的程序也會隨之結束 from m...