文件描述符理解

Linux文件描述符

參考出處:https://www.jianshu.com/p/cded914786d5

與linux打交道,儘管可能你只是一個高級語言的碼農,仍是或多或少的要和遇到d這種術語。今天抽空看了下傳說中的fd,雖然尚未深刻了解linux操做系統,所以也談不上真的深入理解了fd,但仍是掃盲了些許,至少之後再碰到相關術語,不至於一臉茫然。node

說明:看了一些網上的文章,大多數語句都是在理解的基礎上直接搬過來的,感謝那些整理的人們,就不一一列舉出處了。linux

1. Linu文件系統簡介

文件系統是操做系統用於明確存儲設備(常見的是磁盤,也有基於NAND Flash的固態硬盤)或分區上的文件的方法和數據結構;即在存儲設備上組織文件的方法。
Unix能夠把一個能隨機存取的存儲介質(如:硬盤、軟盤和光盤)上的存儲空間劃分紅一致多個區域,每一個區域均可以像獨立的物理設備同樣單獨進行管理和數 據存取,這樣的存儲區域,便是邏輯設備。在邏輯設備上按照必定的格式進行劃分,就構成了邏輯文件系統,簡稱文件系統。網絡

  • 普通文件 這種文件包含了某種形式的數據,這些數據不管是文件仍是二進制對於 UNIX 內核而言都是同樣的。對普通文件內容的解釋有處理該文件的應用程序進行。
  • 目錄文件 目錄文件包含了其餘文件的名字以及指向與這些文件有關信息的指針。對一個目錄文件具備讀權限的任一進程均可以讀取該目錄的內容,可是隻有內核才能直接寫目錄文件。
  • 塊特殊文件 這種文件類型提供對設備帶緩衝的訪問,每次訪問以固定長度爲單位進行。
  • 字符特殊文件 這種文件類型提供對設備不帶緩衝的訪問,每次訪問長度可變。系統中的全部設備要麼是字符特殊文件,要麼是塊特殊文件。
  • FIFO 這種類型文件用於進程間通訊。也稱爲命名管道(namedpipe)。
  • 套接字(socket) 這種文件類型用於進程間的網絡通訊。
  • 符號連接(symbolic link) 這種文件類型指向另外一個文件。

2. 什麼是文件描述符fd

在Linux系統中一切皆能夠當作是文件,文件又可分爲:普通文件、目錄文件、連接文件和設備文件。文件描述符(file descriptor)是內核爲了高效管理已被打開的文件所建立的索引,其是一個非負整數(一般是小整數),用於指代被打開的文件,全部執行I/O操做的系統調用都經過文件描述符。程序剛剛啓動的時候,0是標準輸入,1是標準輸出,2是標準錯誤。若是此時去打開一個新的文件,它的文件描述符會是3。POSIX標準要求每次打開文件時(含socket)必須使用當前進程中最小可用的文件描述符號碼。數據結構

也就是說,在linux系統中,全部的文件操做,都是經過fd來定位資源和狀態的,不論是讀寫文件,仍是進行網絡通訊,都須要與相應的fd打交道。socket

理解具體狀況,須要瞭解由內核維護的3個數據結構:函數

  • 進程級文件描述符表(file descriptor table)
  • 系統級打開文件表(open file table)
  • 文件系統i-node表(i-node table)

這3個數據結構之間的關係以下圖所示:spa

 

 

文件描述符表

系統爲每一個進程維護一份文件描述符表,該表的每個條目都記錄了單個文件描述符的相關信息,包括:操作系統

  • 控制標誌(flags),目前內核僅定義了一個,即close-on-exec
  • 打開文件描述體指針

打開文件列表

內核對全部打開的文件維護一個系統級別的打開文件描述表(open file description table)。表中的條目稱爲打開文件描述體(open file description),存儲了與一個打開的文件相關的所有信息,包括:指針

  • 文件偏移量(current file offset),調用read()和write()更新,調用lseek()直接修改
  • 訪問模式(file status flags),由open()調用設置,例如:只讀、只寫或讀寫等
  • i-node對象指針(v-node ptr),指向一個inode元素,從而關聯物理文件

i-node表

就像進程用pid來描述和定位同樣,在linux系統中,文件使用inode號來描述,inode存儲了文件的不少元信息。對象

每一個文件系統會爲存儲於其上的全部文件(包括目錄)維護一個i-node表,單個i-node包含如下信息:

  • 文件類型(file type),能夠是常規文件、目錄、套接字或FIFO
  • 文件的字節數
  • 文件擁有者的User ID
  • 文件的Group ID
  • 文件的讀、寫、執行權限
  • 文件的時間戳,共有三個:ctime指inode上一次變更的時間,mtime指文件內容上一次變更的時間,atime指文件上一次打開的時間。
  • 連接數,即有多少文件名指向這個inode
  • 文件數據block的位置

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節點表元素,從而完成對物理文件的讀寫。

3. 文件描述限制

在編寫文件操做的或者網絡通訊的軟件時,初學者通常可能會遇到「Too many open files」的問題。這主要是由於文件描述符是系統的一個重要資源,雖說系統內存有多少就能夠打開多少的文件描述符,可是在實際實現過程當中內核是會作相應的處理的,通常最大打開文件數會是系統內存的10%(以KB來計算)(稱之爲系統級限制),查看系統級別的最大打開文件數可使用sysctl -a | grep fs.file-max命令查看。與此同時,內核爲了避免讓某一個進程消耗掉全部的文件資源,其也會對單個進程最大打開文件數作默認值處理(稱之爲用戶級限制),默認值通常是1024,使用ulimit -n命令能夠查看,咱們也能夠經過命令去修改該限制。

4.fork等操做對fd的影響

咱們仍是以這幅圖爲例:

 

 

 

在進程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()調用。同一個進程兩次打開同一個文件,也會發生相似狀況。

總結

    • 因爲進程級文件描述符表的存在,不一樣的進程中會出現相同的文件描述符,它們可能指向同一個文件,也可能指向不一樣的文件
    • 兩個不一樣的文件描述符,若指向同一個打開文件句柄,將共享同一文件偏移量。所以,若是經過其中一個文件描述符來修改文件偏移量(由調用read()、write()或lseek()所致),那麼從另外一個描述符中也會觀察到變化,不管這兩個文件描述符是否屬於不一樣進程,仍是同一個進程,狀況都是如此。
    • 要獲取和修改打開的文件標誌(例如:O_APPEND、O_NONBLOCK和O_ASYNC),可執行fcntl()的F_GETFL和F_SETFL操做,其對做用域的約束與上一條頗爲相似。
    • 文件描述符標誌(即,close-on-exec)爲進程和文件描述符所私有。對這一標誌的修改將不會影響同一進程或不一樣進程中的其餘文件描述符
相關文章
相關標籤/搜索