[APUE 讀書筆記] 第三章 文件 I/O
大概四年前陸陸續續讀過 APUE,那個時候還是第二版,并且那個時候對于很多概念沒有什么清楚的認識,讀起來很快就給忘了。 時光荏苒,四年很快過去了,我已經從當時剛轉專業的大二學生,變成了正在讀 CS 的研一學生。最近突然發現,原來我對 Unix 系統以及網絡的協議棧感興趣。趁著現在還在上學,搞來了一本第三版打算系統學起來。經過這幾年的沉淀,對于書中講述的很多問題不再是比較陌生,或者是讀完了沒什么感覺。感觸最深的就是多線程和多進程部分,當時對于這些知識在生活中或者在現成的技術中是如何的應用還沒什么概念,所以看的時候可能也沒有用心。現在對于它們有了全新的理解,所以重新看一遍覺得確實受益匪淺。
不過,看書不能從中間取一段看,知識都是環環相扣的,特別是 APUE 這種經典,只能從頭開始看。于是將我覺得重要的內容記錄下來,留作以后查閱。
術語不帶緩沖指的是每個?read?和?write?都調用內核中的一個系統調用,不帶緩沖的 I/O 函數不是 ISO C 的組成部分。
對于內核而言,所有打開的文件都通過文件描述符(非負整數)引用。
按照慣例,Unix 系統 shell 把文件描述符?0?與進程的標準輸入關聯(STDIN_FILENO),文件描述符?1?與標準輸出關聯(STDOUT_FILENO),文件描述符?2?與標準錯誤關聯(STDERR_FILENO)。
文件描述符的變化范圍是?0~OPEN_MAX - 1。
由?open?和?oepnat?函數返回的文件描述符一定是最小的未用的描述符數值。
openat?函數希望解決兩個問題:第一,讓線程可以使用相對路徑名打開目錄中的文件,而不再只能打開當前工作目錄。第二,可以避免?time-of-chech-to-time-of-use (TOCTTOU)?錯誤。
TOCTTOU?錯誤的基本思想:如果有兩個基于文件的函數調用,其中第二個調用依賴于第一個調用的結果,那么程序是脆弱的。因為兩個調用并不是原子操作,在兩個函數調用之間文件可能改變了,這樣也就造成了第一個調用的結果就不再有效。
關閉一個文件時還會釋放該進程加在該文件上的所有記錄鎖。
關閉一個文件時,內核自動關閉它所有的打開文件。很多程序都利用了這一功能而不顯示地用?close?關閉打開文件。
按系統默認的情況,當打開一個文件時,除非指定?O_APPEND?選項,否則該偏移量被設置為?0。
通常,文件的當前偏移量應當是一個非負整數,但是,某些設備也可能允許負的偏移量。但對于普通文件,其偏移量必須是非負值。因為偏移量可能是負值,所以在比較?lseek?的返回值時應當謹慎,不要測試它是否小于?0,而要測試它是否等于?-1。
文件偏移量可以大于文件的當前長度,在這種情況下,對該文件的下一次寫將加長該文件,并在文件中構成一個空洞,這一點是允許的。位于文件中但沒有寫過的字節都被讀為?0。文件中的空洞并不要求在磁盤上占用存儲區。
盡管可以實現 64 位文件偏移量,但是能否創建一個大于?2 GB?的文件則依賴于底層文件系統的類型。
write?出錯的一個常見原因是磁帶已滿,或者超過了一個給定進程的文件長度限制。
創建 v 節點結構的目的是對在一個計算機系統上的多文件系統類型提供支持。
Unix 系統為追加到文件尾端這樣的操作提供了一種原子操作方法,即在打開文件時設置?O_APPEND?標志,這樣做使得內核在每次寫操作之前,都將進程的當前偏移量設置到該文件的尾端,于是在每次寫之前就不需要調用?lseek。
對?open?函數的?O_CREATE?和?O_EXCL?選項同時指定后,如果該文件已經存在時,open?將失敗。
新描述符的?close-on-exec?標志總是由?dup?函數清除。
復制一個文件描述符的另一種方法是使用?fcntl?函數。
sync?函數只是將所有修改過的塊緩沖區排入寫隊列,然后就返回,它并不等待實際寫詞盤操作結束。通常,稱為?update?的系統守護進程周期性地調用(一般每隔?30?秒)sync?函數。
fsync?函數只對由文件描述符?fd?指定的一個文件起作用,并且等待寫詞盤操作結束才返回。
fcntl?的返回值與命令有關,如果出錯,所有命令都返回 -1,如果成功則返回某個其他值。
在修改文件描述符標志位時必須謹慎,首先獲得現在的標志值,然后按照期望修改它。不能只是執行?F_SETFD或?F_SETFL?命令,這樣會關閉以前設置的標志位。
Unix 系統中,通常?write?只是將數據排入隊列,而實際的寫詞盤操作則可能在以后的某個時刻進行。
終端 I/O 是使用?ioctl?最多的地方。
函數?open?的?oflag?參數
Flag
備注
O_RDONLY
只讀打開
O_WRONLY
只寫打開
O_RDWR
讀、寫打開
O_EXEC
只執行打開
O_EXEC
只搜索打開(應用于目錄)
以上 5 個常量中必須制定一個且只能指定一個。還有一些常量是可選的,這里不贅述。
函數?lseek?對參數?offset?的解釋
對參數?offset?的解釋與參數?whence?的值有關?若?whence?是?SEEK_SET,則將該文件的偏移量設置為距文件開始處?offset?個子節。?若?whence?是?SEEK_CUR,則將該文件的偏移量設置為其當前值加?offset,offset?可為正或負。 * 若?whence?是?SEEK_END,則將該文件的偏移量設置為文件長度加?offset,offset?可為正或負。
若?lseek?成功執行,則返回新的文件偏移量,為此可以用下列方式確定打開文件的當前偏移量:
off_t?currpos?=?lseek(fd,?0,?SEEK_CUR);
這種方法也可涌來確定所涉及的文件是否可以設置偏移量。如果文件描述符指向的是一個管道、FIFO 或網絡套接字,則?lseek?返回?-1,并將?errno?設置為?ESPIPE。
函數?read
由多種情況可使實際讀到的字節數少于要求讀到的字節數:?讀普通文件時,在讀到要求字節數之前已到達了文件尾端。?當從終端設備讀時,通常一次最多讀一行。?當從網絡讀時,網絡中的緩沖機制可能造成返回值小于所要求讀的字節數。?當從管道或 FIFO 讀時,如若管道包含的字節少于所需的數量,那么?read?將只返回實際可用的字節數。?當從某些面向記錄的設備(如磁帶)讀時,一次最多返回一個記錄。?當一信號造成中斷,而已經讀了部分數據量時。
函數?pread?和?pwrite
調用?pread?相當于調用?lseek?后調用?read,但是?pread?又與這種順序調用有下列重要區別:?調用?pread?時,無法中斷其定位和操作。?不更新當前文件偏移。 調用?pwrite?相當于調用?lseek?后調用?write,但也與它們有類似的區別。
函數?dup?和?dup2
由?dup?返回的新文件描述符一定是當前可用文件描述符中的最小數值。對于?dup2,可以用?fd2?參數指定新描述符的值。如果?fd2?已經打開,則先將其關閉。如若?fd?等于?fd2,則?dup2?返回?fd2,而不關閉它。 這些函數返回的新文件描述符與參數?fd?共享同一個文件表項。
本文轉載自異步社區
任務調度 網絡
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。