搞懂Linux下的幾種文件類型

在Linux系統下,有七類文件類型:html

  • 普通文件(-)
  • 目錄(d)
  • 軟連接(字符連接L)
  • 套接字文件(S)
  • 字符設備(S)
  • 塊設備(B)
  • 管道文件(命名管道P)

普通文件、目錄、軟連接無需多解釋。shell

管道文件

管道分爲匿名管道和命名管道。管道都是一端寫入、另外一端讀取,它們是單方向數據傳輸的,它們的數據都是直接在內存中傳輸的,管道是進程間通訊的一種方式,例如父進程寫,子進程讀。編程

在shell中匿名管道就是一個管道符號"|",例如ls | grep xxx,其中ls對應的進程是這個獨立進程組中的父進程,grep對應的進程是子進程,父進程寫子進程讀。bash

在編程語言中,匿名管道是經過建立兩個文件句柄或文件描述符(例如A、B)來實現的,一個文件句柄用於寫數據(例如A寫入端,數據寫入A將自動推入B中),另外一個文件句柄用於讀數據(即B)。網絡

對於命名管道,即有名稱的管道,命名管道將文件保留在文件系統中,它也稱爲FIFO,也就是first in first out。雖然命名管道文件保留在文件系統中,可是這個文件只是使用命名管道的一個入口,在使用命名管道傳輸數據的時候,仍然是在內存中進行的,也就是說並不會由於保留在文件系統上命名管道的效率就低了。dom

在shell中,可使用mknod命令或mkfifo命令建立命名管道,在寫某些特殊需求的shell腳本時,命名管道很是有用。實際上,在Bash 4以後就支持協程(使用coproc命令)的功能了(ksh和zsh老早就支持協程),可是協程的需求都能經過命名管道來實現。socket

通常的管道都是單向通訊的,沒法實現雙向通訊的功能,也就是隻能一邊寫一邊讀,不能兩邊都能讀、寫。若是要實現雙向通訊,能夠建立兩根管道(這樣就有4個文件句柄,兩個讀端,兩個寫端),或者使用更方便的套接字。編程語言

套接字(Socket)

套接字用來實現兩端通訊,正如上面分析的,能夠實現雙向管道的進程間通訊功能。不只如此,套接字還能經過網絡實現跨主機的進程間通訊功能。函數

套接字須要成對纔有意義,也就是分爲兩端,每一端都有用於讀、寫的文件描述符(或文件句柄),至關於兩根雙向通訊的管道。操作系統

套接字根據協議族的方式分爲兩大類:網絡套接字(AF_INET類型,根據ipv4和ipv6分爲inet4和inet6)和Unix Domain套接字(AF_UNIX類型)。固然,從協議族往下,套接字可細分爲不少種類型,例如INET套接字能夠分爲TCP套接字、UDP套接字、鏈路層套接字、Raw套接字等等。其中網絡套接字是網絡編程的基礎和核心。

Unix Domain套接字

對於單機的進程間通訊,使用Unix Domain套接字比Inet套接字更好,由於Unix Domain套接字沒有網絡通訊組件,也就是少了不少網絡功能,它更加輕量級。實際上,某些語言在某些操做系統平臺上實現的管道功能就是經過Unix Domain來實現的,可想而知其高效率。

Unix Domain套接字有兩個文件句柄(例如A、B),這兩個文件句柄都是同時可讀、可寫的句柄。進程1向A寫入數據,將自動推送到B上,進程2可從B上讀取從A寫入的數據,同理進程2向B中寫入數據將自動推送到A上,進程1可從A上讀取從B寫入的數據。以下:

進程1            進程2
------------------------
A   ----------->  B
B   ----------->  A

在編程語言中,建立Unix Domain Socket天然有對應的函數輕鬆建立(可man socketpair)。對於bash shell,能夠經過nc命令(NetCat)來建立,或者乾脆使用兩個命名管道來實現對應的功能。若有須要,可自行了解如何在bash shell中使用Unix Domain套接字。

網絡套接字

對於跨網絡的進程間通訊,須要使用網絡套接字。每一個網絡套接字都由5部分組成,它們稱爲套接字的5元組。格式以下:

{protocol, src_addr, src_port, dest_addr, dest_port}

即協議、源地址、源端口、目標地址、目標端口。

每端套接字在內核空間都有兩個buffer(即一對socket有4個buffer),每一端都有recv buffer和send buffer。進程1向本身的套接字的send buffer寫入數據,將發送到對端的recv buffer中,而後對端的進程2就能夠從recv buffer中讀取數據,反之亦然。

可是在真正能夠讀、寫網絡套接字以前,網路套接字還須要一些設置。服務端套接字建立(socket()函數,建立後就會有一個文件句柄或文件描述符供讀、寫操做)後,還要綁定地址(經過bind()函數)和監聽端口(經過listen()函數),客戶端則只須要建立套接字後,直接使用connect()函數向服務端套接字發起鏈接請求便可。

對於TCP套接字,客戶端發起鏈接請求即表示要和服務端進行三次握手(內核完成,和用戶空間進程無關)。將這三次握手的每一次進行細分,第一次客戶端發送SYN請求,服務端接收到SYN後,內核將這個鏈接放進syn queue中並設置狀態爲syn-recv,而後發送ack+syn給客戶端,當接收到客戶端回覆ack後,內核將鏈接從syn queue移到established queue(或accept queue)中並將鏈接的狀態標記爲established。最後等待用戶空間的進程發起accept()系統調用讓內核將其從accept queue中移除。被accept()後的鏈接表示已經創建好的鏈接,能夠真正實現兩端進程間的數據傳輸。

更多關於TCP套接字的原理,參見個人另外一篇文章:不可不知的socket和TCP鏈接過程

塊設備和字符設備

塊設備是硬件設備,經過隨機(不必定是順序)訪問固定大小的數據塊(chunk)來區分。固定大小的chunk稱爲塊(block)。最多見的塊設備是硬盤,但也存在許多其餘塊設備,如軟盤驅動器、藍光閱讀器和閃存。注意,這些都是掛載文件系統的設備,文件系統就像是塊設備的通用語言。

字符設備經過連續的流數據訪問,一個字節接着一個字節。典型的字符設備是終端(終端分多種,由物理的也有虛擬的)和鍵盤

區分塊設備和字符設備最簡單的方法是看數據訪問的方式。能隨機訪問獲取數據的是塊設備,必須按字節順序訪問的是字符設備

若是能夠這裏讀一點數據,那裏讀一點數據,最後串成一整段連續的數據,那麼這個就是塊設備,就像硬盤上的數據是不連續的,有可能須要經過隨機訪問的方式獲取一段數據。好比磁盤上一個稍大一點的文件,可能前10k數據是連續的數據塊或在連續的扇區內,以後的10k數據在離它很遠甚至在不一樣的柱面上。

若是一段數據中的每一個字節都跟訪問時的字節順序是同樣的,即字節前後順序從訪問獲取時到最後處理數據的過程當中都是徹底一致的,那麼這個就是字符設備。換句話說,字符設備能夠看做是流設備。就像鍵盤輸入數據同樣,連續敲兩個字鍵,這兩個鍵對應的字節數據在被接收的時候必定是先敲的在前面,後敲的在後面。同理終端設備也是以同樣的,程序將數據輸出到終端時,程序先輸出字母a再輸出數字3,那麼顯示在終端上時必定是a在前,3在後。

相關文章
相關標籤/搜索