文件對於咱們來講,貌似再普通不過了,windows 使用擴展名來區分不一樣的文件,咱們接觸了 gif、docx、pdf、mp三、mp四、exe 等等好多好多不一樣的文件,可是它們在磁盤中都長一個樣子,對於內核而言,沒有什麼區別。node
文件這個概念就是對 I/O設備 的抽象,一個文件就是一串 m 個字節的序列,全部的 I/O 設備(網絡、磁盤、終端)都被模型化爲文件,全部的輸入輸出都被當作對相應文件的讀和寫來執行。linux
稍微特殊一點的是文件目錄吧,目錄也是一個文件,它是包含一組連接
的文件,其中每一個連接都將一個文件名映射到一個文件,這個文件也多是另外一個目錄。git
每一個目錄至少會有兩個條目,.
是到該目錄自身的連接;..
是到父目錄的連接,你可使用mkdir
命令建立一個目錄,而後查看其內容觀察觀察,linux 用ls
查看,windows 用dir
。程序員
每一個進程都有一個當前工做目錄,linux 是以/
做爲根目錄的,因此絕對路徑就是一個/
開始,以下圖所示,hello.c 的絕對路徑名即爲:/home/droh/hello.c
;若是進程的當前工做目錄爲:/home/droh
,那麼 hello.c 的相對路徑就爲:./hello.c
,而要是工做目錄爲:/home/bryant
,那麼相對路徑名則爲:../home/droh/hello.c
。github
咱們可使用open
函數來打開一個文件,它的做用是把文件名轉換爲一個文件描述符,這個描述符是一個小的非負整數,在後續對此文件的全部操做中標識這個文件。open
函數返回的描述符老是在進程中當前沒有打開的最小描述符。shell
Linux shell 建立的每一個進程開始都有三個打開的文件:標準輸入(描述符爲 0)、標準輸出(描述符爲 1)、標準錯誤(描述符爲 2)。編程
open
函數有三個入參,分別爲filename, flags, mode
,flags 指明瞭如何訪問這個文件,即只讀、只寫、可讀寫;mode 指明瞭新文件的訪問權限位,它經過與進程自帶的umask
進行運算來得到文件的訪問權限,這個運算時:mode & ~umask
。windows
讀文件是從描述符爲 fd 的當前文件位置複製 n 個字節到內存位置 buf,寫文件則是把內存中的字節複製到當前文件中,系統中提供了 read
和write
函數來提供相應的功能。網絡
read
函數的返回值是表明實際傳送的字節數,有趣的是當出錯時,它須要返回 -1,所以使用的是一個有符號長整數,而就僅僅爲了這個 -1,就使得read
的最大值減少了一半。數據結構
固然還有可能會遇到須要讀取的字節比文件實際字節數要多的狀況,這時就會觸發一個稱爲 end-of-file(EOF) 的條件,應用程序死能夠檢測到這個條件的,此時read
會返回 0。
我總算知道在學校作 oj 題時,爲何要把
while(scanf("%s", str[i]))
寫成while(scanf("%s", str[i]) != EOF)
了。
共享文件的方法不少,可是共享文件的概念比較晦澀難懂。
系統內核使用三個相關的數據結構來表示打開的文件,分別爲:描述符表、文件表、v-node 表。每一個進程都有一張描述符表,其表項是由進程打開的文件描述符來索引的;文件表表示全部的打開文件的集合,全部進程共享這個表,關閉一個描述符會減小相應的文件表表項中的引用計數,當引用計數爲零時,內核就睡刪除對應的表項(是否是和垃圾回收機制很像?);v-node表 也是全部進程共享的,表中包含了用戶組、大小等不少信息。
如上圖所示,是一個進程打開了兩個不一樣的文件的樣子,這種狀況下沒有共享文件;而若是以同一個 filename 調用 open 函數兩次,就會發生共享文件的狀況,其關鍵思想是每一個描述符都有它本身的文件位置,以下圖所示。
有了上面的基礎,那理解子進程如何繼承父進程打開的文件就容易的多了,直觀展現出來就是下面這個樣子。
最後來看一個簡單的題:下面程序的輸出是什麼?
int main() {
int fd1, fd2;
fd1 = Open("foo.txt", O_RDONLY, 0);
Close(fd1);
fd2 = Open("baz.txt", O_RDONLY, 0);
printf("fd2 = %d\n", fd2);
exit(0);
}
複製代碼
Unix 進程生命週期開始,打開的前三個描述符已經被使用了,而 open 函數老是返回最低的未打開的描述符,因此第一次調用 open 函數會返回 3,調用 close 函數會釋放描述符 3,因此最後對 open 函數的調用仍是會返回 3,即程序輸出是:fd2 = 3
。
在學生時代,聽到 I/O、文件等名詞時,下意識就認定了對方是個厲害的角色,原本覺得本身也會能操做文件後就不會有這種感受了,而實際狀況依舊如此,聽到老程序員談到 socket 等名詞時,依舊充滿了景仰之情。
團隊老員工告訴我,不少工具看源碼,都是同樣的原理,應該好好看下 socket 編程,讀完這一章,我貌似有點明白同事的意思了。