系統級 I/O
檔案對於我們來說,貌似再普通不過了,windows 使用副檔名來區分不同的檔案,我們接觸了 gif、docx、pdf、mp3、mp4、exe 等等好多好多不同的檔案,但是它們在磁碟中都長一個樣子,對於核心而言,沒有什麼區別。
基本概念
檔案這個概念就是對 I/O裝置 的抽象,一個檔案就是一串 m 個位元組的序列,所有的 I/O 裝置(網路、磁碟、終端)都被模型化為檔案,所有的輸入輸出都被當做對相應檔案的讀和寫來執行。
稍微特殊一點的是檔案目錄吧,目錄也是一個檔案,它是包含一組連結
的檔案,其中每個連結都將一個檔名對映到一個檔案,這個檔案也可能是另一個目錄。
每個目錄至少會有兩個條目,.
是到該目錄自身的連結;..
是到父目錄的連結,你可以使用mkdir
命令建立一個目錄,然後檢視其內容觀察觀察,linux 用ls
檢視,windows 用dir
。
每個程式都有一個當前工作目錄,linux 是以/
作為根目錄的,所以絕對路徑就是一個/
開始,如下圖所示,hello.c 的絕對路徑名即為:/home/droh/hello.c
;如果程式的當前工作目錄為:/home/droh
,那麼 hello.c 的相對路徑就為:./hello.c
,而要是工作目錄為:/home/bryant
,那麼相對路徑名則為:../droh/hello.c
。
我們可以使用open
函式來開啟一個檔案,它的作用是把檔名轉換為一個檔案描述符,這個描述符是一個小的非負整數,在後續對此檔案的所有操作中標識這個檔案。open
函式返回的描述符總是在程式中當前沒有開啟的最小描述符。
Linux shell 建立的每個程式開始都有三個開啟的檔案:標準輸入(描述符為 0)、標準輸出(描述符為 1)、標準錯誤(描述符為 2)。
open
函式有三個入參,分別為filename, flags, mode
,flags 指明瞭如何訪問這個檔案,即只讀、只寫、可讀寫;mode 指明瞭新檔案的訪問許可權位,它通過與程式自帶的umask
進行運算來獲得檔案的訪問許可權,這個運算時:mode & ~umask
。
讀檔案是從描述符為 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 程式設計,讀完這一章,我貌似有點明白同事的意思了。
相關文章
- JAVA I/O系統Java
- 作業系統—I/O 模型作業系統模型
- 系統程式設計 - I/O模型程式設計模型
- Linux命令----分析系統I/O的瓶頸Linux
- 作業系統程式、儲存和I/O作業系統
- Java I/O系統學習系列一:File和RandomAccessFileJavarandomMac
- 計算機I/O與I/O模型計算機模型
- I/O流
- Java I/OJava
- Java I/O系統學習系列二:輸入和輸出Java
- 作業系統I/O模型及輪詢技術演變作業系統模型
- Python教程:精簡概述I/O模型與I/O操作Python模型
- 關於I/O
- c++ I/OC++
- 【java】I/O流Java
- Java(8)I/OJava
- 電商系統O2O商城系統開發
- 佳弗O2O系統
- 效能分析(7)- 未利用系統快取導致 I/O 緩慢案例快取
- Google I/O Extend 2018Go
- 網路I/O模型模型
- NodeJs 非同步 I/ONodeJS非同步
- 理解I/O Completion Port
- python 非同步 I/OPython非同步
- 02. I/O 操作
- Java 非同步 I/OJava非同步
- Hadoop的I/O操作Hadoop
- Linux下的5種I/O模型與3組I/O複用Linux模型
- 如何在Linux系統伺服器中測試儲存/磁碟I/O效能?Linux伺服器
- 【面試】I/O 複用面試
- Java™ 教程(命令列I/O)Java命令列
- 流?I/O 操作?阻塞?epoll?
- I/O模型、Libuv和Eventloop模型OOP
- 由Nodejs來說I/ONodeJS
- Linux I/O排程器Linux
- o2o系統 本地生活服務 微信o2o
- 從網路I/O模型到Netty,先深入瞭解下I/O多路複用模型Netty
- asynchronous i/o (aio) on HP-UXAIUX