與linux打交道,儘管可能你只是一個高級語言的碼農,仍是或多或少的要和遇到d這種術語。今天抽空看了下傳說中的fd,雖然尚未深刻了解linux操做系統,所以也談不上真的深入理解了fd,但仍是掃盲了些許,至少之後再碰到相關術語,不至於一臉茫然。node
說明:看了一些網上的文章,大多數語句都是在理解的基礎上直接搬過來的,感謝那些整理的人們,就不一一列舉出處了。linux
文件系統是操做系統用於明確存儲設備(常見的是磁盤,也有基於NAND Flash的固態硬盤)或分區上的文件的方法和數據結構;即在存儲設備上組織文件的方法。
Unix能夠把一個能隨機存取的存儲介質(如:硬盤、軟盤和光盤)上的存儲空間劃分紅一致多個區域,每一個區域均可以像獨立的物理設備同樣單獨進行管理和數 據存取,這樣的存儲區域,便是邏輯設備。在邏輯設備上按照必定的格式進行劃分,就構成了邏輯文件系統,簡稱文件系統。網絡
在Linux系統中一切皆能夠當作是文件,文件又可分爲:普通文件、目錄文件、連接文件和設備文件。文件描述符(file descriptor)是內核爲了高效管理已被打開的文件所建立的索引,其是一個非負整數(一般是小整數),用於指代被打開的文件,全部執行I/O操做的系統調用都經過文件描述符。程序剛剛啓動的時候,0是標準輸入,1是標準輸出,2是標準錯誤。若是此時去打開一個新的文件,它的文件描述符會是3。POSIX標準要求每次打開文件時(含socket)必須使用當前進程中最小可用的文件描述符號碼。數據結構
也就是說,在linux系統中,全部的文件操做,都是經過fd來定位資源和狀態的,不論是讀寫文件,仍是進行網絡通訊,都須要與相應的fd打交道。socket
理解具體狀況,須要瞭解由內核維護的3個數據結構:函數
這3個數據結構之間的關係以下圖所示:spa
系統爲每一個進程維護一份文件描述符表,該表的每個條目都記錄了單個文件描述符的相關信息,包括:操作系統
內核對全部打開的文件維護一個系統級別的打開文件描述表(open file description table)。表中的條目稱爲打開文件描述體(open file description),存儲了與一個打開的文件相關的所有信息,包括:指針
就像進程用pid來描述和定位同樣,在linux系統中,文件使用inode號來描述,inode存儲了文件的不少元信息。對象
每一個文件系統會爲存儲於其上的全部文件(包括目錄)維護一個i-node表,單個i-node包含如下信息:
i-node存儲在磁盤設備上,內核在內存中維護了一個副本,這裏的i-node表爲後者。副本除了原有信息,還包括:引用計數(從打開文件描述體)、所在設備號以及一些臨時屬性,例如文件鎖。
open系統調用執行的操做:新建一個i-node表元素,讓其對應打開的物理文件(若是對應於該物理文件的inode元素已經創建,就不作任何操做);新建一個文件表的元素,根據open的第2個參數設置file status flags字段,將current file offset字段置0,將v-node ptr指向剛創建的i節點表元素;在文件描述符表中,尋找1個還沒有使用的元素,在該元素中填入一個指針值,讓其指向剛創建的文件表元素。最重要的是:將該元素的下標做爲open的返回值返回。
這樣一來,當調用read(write)時,根據傳入的文件描述符,OS就能夠找到對應的文件描述符表元素,進而找到文件表的元素,進而找到i節點表元素,從而完成對物理文件的讀寫。
在編寫文件操做的或者網絡通訊的軟件時,初學者通常可能會遇到「Too many open files」的問題。這主要是由於文件描述符是系統的一個重要資源,雖說系統內存有多少就能夠打開多少的文件描述符,可是在實際實現過程當中內核是會作相應的處理的,通常最大打開文件數會是系統內存的10%(以KB來計算)(稱之爲系統級限制),查看系統級別的最大打開文件數可使用sysctl -a | grep fs.file-max命令查看。與此同時,內核爲了避免讓某一個進程消耗掉全部的文件資源,其也會對單個進程最大打開文件數作默認值處理(稱之爲用戶級限制),默認值通常是1024,使用ulimit -n命令能夠查看,咱們也能夠經過命令去修改該限制。
咱們仍是以這幅圖爲例:
在進程A中,文件描述符1和30都指向了同一個打開的文件句柄(標號23)。這多是經過調用dup()、dup2()、fcntl()或者對同一個文件屢次調用了open()函數而造成的。
進程A的文件描述符2和進程B的文件描述符2都指向了同一個打開的文件句柄(標號73)。這種情形多是在調用fork()後出現的(即,進程A、B是父子進程關係,子進程繼承父進程打開的文件),或者當某進程經過UNIX域套接字將一個打開的文件描述符傳遞給另外一個進程時,也會發生。再者是不一樣的進程獨自去調用open函數打開了同一個文件,此時進程內部的描述符正好分配到與其餘進程打開該文件的描述符同樣。
此外,進程A的描述符0和進程B的描述符3分別指向不一樣的打開文件句柄,但這些句柄均指向i-node表的相同條目(1976),換言之,指向同一個文件。發生這種狀況是由於每一個進程各自對同一個文件發起了open()調用。同一個進程兩次打開同一個文件,也會發生相似狀況。