文件I/O --- 通用的I/O模型 (一、系統調用)

一、提問:

  1. 如何使用C語言訪問UNIX系統下的文件?
  2. 內核是如何記錄文件打開的信息的?
  3. 使用中有什麼需要注意的地方?

1.1參考資料

  • Linux/UNIX系統編程手冊(上冊) 第4章

1.2基本知識:

  • 所有執行I/O操作的系統調用都以文件描述符,一個非負整數(通常是小整數),來指代打開的文件(類似於代號,別名)。
  • 文件描述符用以表示所有類型的已打開的文件,包括管道(pipo),FIFO,socket,終端,設備和普通文件。針對每個進程,文件描述符都自成一套(進程間文件描述符是獨立的,互不干擾)。
  • 程序中始終默認打開3個常用的文件描述符,使用中建議採用<unistd.h>所定義的POSIX標準名稱。
表1:標準文件描述符
文件描述符 用 途 POSIX名稱 stdio流
0 標準輸入 STDIN_FILENO stdin
1 標準輸出 STDOUT_FILENO stdout
2 標準錯誤 STDERR_FILENO stder

二、系統調用

2.1文件打開

int fd = open(const char* pathname, int flags, mode_t mode);    //文件名,打開方式  權限

open 出錯時返回-1。反之,返回一個正數,表示文件描述符 fd,用以在後續函數中調用指代打開的文件。
打開對應文件,並由內核維護一個文件對象。以fd指代這個文件對象。

flags 對位掩碼參數,
1、O_RDONLY, O_WRONLY, O_RDWR 可以指定文件打開方式分別爲:只讀,只寫或讀寫。 
2、O_CREAT 表示當文件不存在時,創建文件。
3、O_APPEND 表示總是在文件末尾追加數據。
4、O_NONBLOCK 表示以非阻塞方式打開

mode參數指定由open()調用創建文件的訪問權限,如果open()並未創建文件,可以忽略或省略mode參數。
1、新建文件的訪問權限還會受到進程的umask值和(可能存在的)父目錄的默認控制訪問列表的影響。
2、通常爲八進制,即 0777 來表示。

  1. 更多的flags參數——參考資料1 的 p60。
  2. open()函數錯誤——參考資料1 的 p63。

 

int creat(const char* pathname, mode_t mode)

成功返回文件描述符,失敗返回-1.
  • 早期的UNIX實現中,open()只有兩個參數,無法創建新文件。通常使用creat()系統調用來創建並打開新文件。現已不多見。

 

2.2文件關閉

int close(int fd);    

關閉文件描述符,釋放對應的文件對象
成功返回0,失敗返回-1

if(close(fd) == -1)
    perror("close");
上述代碼能夠捕獲的錯誤:
1、企圖關閉一個未打開的文件描述符。
2、企圖兩次關閉同一個文件描述符。
3、特定文件系統在關閉操作中診斷出的錯誤條件

 

2.3文件的讀取

#include<unistd.h>

ssize_t read(int fd, void* buf, size_t count); 

成功返回讀取的字節數,讀到EOF(文件結束或網絡通信中對端斷開)返回0,失敗返回-1.
參數buf 指定用來存放輸入數據的內存緩衝區地址,緩衝區應至少有count個字節。
參數count 指定最多能讀取的字節數。
  1. ssize_t 數據類型屬於有符號的整數類型, size_t 數據類型屬於無符號整數類型。
  2. 一次read()調用所讀取的字節數可以小於請求的字節數。默認情況下從終端讀取字符,一遇到換行符(\n),read()調用就會結束。
  3. read()調用在網絡通信,阻塞IO情況下,當對端斷開時,read返回0 。於此類似的還有 recv函數。
  4. read()能夠從文件中讀取任意序列的字節。可能是文本數據,也可能是二進制整數或二進制形式的C語言數據結構。故無法遵循C語言對字符串處理的約定,在字符串尾部追加標識字符串結束的空字符。所以,如果輸入緩衝區結尾需要一個表示終止的空字符,必須顯示追加;也可以在使用前,通過memset或bzero將緩衝區每個字符設置爲'\0' 。

使用案例:

char buffer[MAX_READ + 1];
ssize_t numRead;

numRead = read(STDIN_FILENO, buffer, MAX_READ);
if(-1 == numRead)
{
    perror("read");
}

buffer[numRead] = '\0';
printf("The input data was: %s\n", buffer);

 

2.4 文件的寫入

#include<unistd.h>

ssize_t write(int fd, void* buf, size_t count); 

成功返回實際寫入的字節數,失敗返回-1 。
參數fd 指代數據要寫入的文件。
參數buf 指定欲從buffer寫入文件的數據字節數。
參數count 指定最多能讀取的字節數。
  1. "部分寫" , write()調用返回值小於count參數值。對於磁盤文件,原因可能是磁盤已滿或者是進程資源對文件大小的限制(RLIMIT_FSIZE)。
  2. 與printf和scanf有所區別,read和write是將文件讀至緩衝區或從緩衝區寫入文件,是否會偏移呢?
    a.讀寫過程中文件描述詞fd不會改變,始終指向文件。
    b. read 過程中雖然是相同的文件描述詞,但是會存在一個偏移,不會讀取已經讀取過的文件內容。也就是說對於文件中存      放“HelloWorld”,調用兩次相同的read,第一次會讀進緩衝區“Hello”,第二次讀進緩衝區"World" 。緩衝區中內容會被覆蓋。
    c. write過程類似,文件中會產生偏移,而對於緩衝區不會,兩次調用write,緩衝區中存放“HelloWorld”,       文件中會            是"HelloHello"。
     d.write和read共用一個文件中的偏移。

2.5 改變文件偏移量

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

whence /wens/ adv  從哪裏;從何處。
offset參數 表示從whence開始的偏移量,單位爲字節數。
whence參數 可以取值爲 SEEK_SET,SEEK_CUR,SEEK_END,分別表示文件開頭,光標當前位置以及文件尾。
  1. 文件第一個字節的偏移量爲0 。
  2. SEEK_END指向文件末尾的最後一個字節的下一個字節。
  3. 文件空洞 (參考資料1 p68) 減少磁盤空間的佔用;與mmap聯合使用,加快將文件寫入文件的速度。

 

#include<unistd.h>

int ftruncate(int fd,  off_t length);

成功返回0, 失敗返回-1。
將fd指定文件的大小,改爲參數length指定大小。
  1. fd參數必須表示的是打開的可寫入的文件描述詞。
  2. 如果原來文件比length大,則超過部分會被刪除。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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