Unix環境高級編程筆記(一)文件I/O

File I/O

介紹

  • 我們通過描述文件I / O可用的函數來開始對UNIX系統的討論——打開文件、讀取文件、編寫文件,等等。在UNIX系統上,大多數文件I/O只能使用5個函數來執行:open、read、write、lseekclose
  • 然後,我們將研究各種緩衝區大小對讀寫函數的影響
  • 本章中描述的函數通常被稱爲無緩衝I/O。術語unbuffered的意思是每個讀或寫調用內核中的一個系統調用。這些沒有緩衝的I/O函數不是ISO C的一部分,而是POSIX.1和單一UNIX規範的一部分。
  • 每當我們描述多個進程之間的資源共享時,原子操作的概念變得很重要。我們通過文件I/O和open函數的參數來研究這個概念。這將導致討論如何在多個進程之間共享文件以及涉及哪些內核數據結構。在描述這些特性之後,我們將介紹dup、fcntl、sync、fsync和ioctl函數。

文件描述符(File Descriptors)

  • 對於內核,所有打開的文件都由文件描述符引用。文件描述符是一個非負整數, 當我們打開一個現有文件或創建一個新文件時,內核將向進程返回一個文件描述符;當我們想要讀取或寫入一個文件時,我們使用open或creat返回的文件描述符作爲讀取或寫入的參數來標識該文件。
  • 按照慣例,UNIX系統將:
    • 文件描述符0與標準輸入(STDIN_FILENO1)相關聯
    • 文件描述符1與標準輸出(STDOUT_FILENO1)相關聯
    • 文件描述符2與標準錯誤(STDERR_FILENO1)相關聯

open 和 openat 函數

  • 通過調用open函數或openat函數來打開或創建文件。

    函數詳細用法這裏不再贅述,linux環境下大家打開終端,輸入命令:
man 2 open

這裏主要說明兩者區別,fd參數將openat函數與open函數區分開。 有三種可能性:

  1. path參數指定絕對路徑名。 在這種情況下,將忽略fd參數,並且openat函數的行爲類似於open函數。
  2. path參數指定一個相對路徑名,fd參數是一個文件描述符,該描述符指定要在文件系統中評估相對路徑名的起始位置。 通過打開要在其中評估相對路徑名的目錄來獲得fd參數。
  3. path參數指定相對路徑名,而fd參數具有特殊值AT_FDCWD。 在這種情況下,將從當前工作目錄開始評估路徑名,並且openat函數的行爲類似於open函數。

openat函數是添加到最新版本POSIX.1的函數之一,可以解決兩個問題。 首先,它爲線程提供了一種使用相對路徑名打開當前工作目錄以外的目錄中文件的方法。 在後續章節中我們將學到,同一進程中的所有線程共享相同的當前工作目錄,因此,同一進程中的多個線程很難同時在不同的目錄中工作。 其次,它提供了一種避免使用時間檢查(TOCTTOU)錯誤的方法。

TOCTTOU錯誤的基本思想是,如果程序進行兩次基於文件的函數調用,而第二次調用取決於第一次調用的結果,則該程序容易受到攻擊。 因爲這兩個調用不是原子調用,所以文件可以在兩個調用之間切換,從而使第一個調用的結果無效,從而導致程序錯誤。 文件系統名稱空間中的TOCTTOU錯誤通常通過欺騙特權程序以減少特權文件的權限或修改特權文件以打開安全漏洞來處理破壞文件系統權限的嘗試。

creat 函數


同理,我們使用man命令查看具體用法。
我們可以發現,這個函數等同於:

open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);

creat的一個不足之處是僅打開文件進行寫入。 在提供新版本的open之前,如果我們要創建要寫入然後要回讀的臨時文件,則必須調用creat,close然後再打開。 更好的方法是使用open函數,如

open(path, O_RDWR | O_CREAT | O_TRUNC, mode);

close 函數

在這裏插入圖片描述
關閉文件也會釋放該進程可能在文件上擁有的所有記錄鎖。
這部分我們之後章節補充。

lseek 函數

每個打開的文件都有一個關聯的“當前文件偏移量”,通常是一個非負整數,用於測量從文件開頭算起的字節數。 讀寫操作通常從當前文件偏移量開始,並使偏移量增加讀取或寫入的字節數。 默認情況下,打開文件時,此偏移量初始化爲0,除非指定了O_APPEND選項。
在這裏插入圖片描述
偏移量的解釋取決於whence參數的值。

  • 如果whence爲**SEEK_SET**,則文件的偏移量將設置爲從文件開頭開始的偏移字節。
  • 如果whence是**SEEK_CUR**,則文件的偏移量將設置爲其當前值加上偏移量。偏移量可以爲正或負。
  • 如果whence爲**SEEK_END**,則文件的偏移量將設置爲文件大小加上偏移量。偏移量可以爲正值或負值。
  • lseek僅將當前的文件偏移量記錄在內核中,它並不引起任何I/O操作。然後該偏移量用於下一個讀寫操作。

read 函數

在這裏插入圖片描述
成功時返回讀取到的字節數(爲零表示讀到文件描述符), 此返回值受文件剩餘字節數限制.當返回值小於指定的字節數時 並不意味着錯誤;這可能是因爲當前可讀取的字節數小於指定的字節數(比如已經接近文件結尾,或者正在從管道或者終端讀取數據,或者 read()被信號中斷). 發生錯誤時返回-1,並置 errno 爲相應值.在這種情況下無法得知文件偏移位置是否有變化.

write 函數

在這裏插入圖片描述
其返回值通常與 nbytes 的值相同,否則表示出錯。write出錯的一個常見的原因是磁盤已寫滿,或者超過了一個給定進程的文件長度限制。

原子操作

原子操作(atomic operation)指的是由多步組成的一個操作。如果該操作原子地執行,則要麼執行完所有的步驟,要麼一步也不執行,不可能只執行所有步驟的一個子集。


  1. These constants are defined in the <unistd.h> header ↩︎ ↩︎ ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章