文件I/O文件描述符
對于內核而言,所有打開的文件都通過文件描述符引用。文件描述符是一個非負整數,每一個進程默認有三個已經打開的文件描述符與之關聯,分別是標準輸入0,標準輸出1和標準錯誤2。POSIX規范在頭文件unistd.h中對此定義了三個符號常量STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO。
任何一個進程可以同時打開的文件數是有限制的,這個限制通常是由limits.h頭文件中的常量OPEN_MAX定義的,它的值隨系統的不同而不同,但POSIX規范要求它至少為16。
open函數
#include <fcntl.h>int open(const char *pathname, int oflag, ... /* mode_t mode */);
open函數用來打開或創建一個文件,若成功返回文件描述符,否則返回-1。
pathname是要打開或創建文件的名字。
oflag參數是下列一個或多個常量執行按位或運算的結果殺
- O_RDONLY 只讀打開
- O_WRONLY 只寫打開
- O_RDWR 讀寫打開
上面三個常量必須指定一個并且只能指定一個,下面一些常量則是可選的:
- O_APPEND 將寫入追加到文件的尾端
- O_CREAT 若文件不存在,則創建它。使用該選項時,需要第三個參數mode,用來指定新文件的訪問權限位
- O_EXCL 如果同時指定了O_CREAT,而文件已經存在,則會出錯
- O_TRUNC 如果此文件存在,而且為只寫或讀寫模式成功打開,則將其長度截短為0
- O_NOCTTY 如果pathname指的是終端設備,則不將該設備分配作為此進程的控制終端
- O_NONBLOCK 如果pathname指的是一個FIFO文件、塊設備文件或字符設備文件,則此選項將文件的本次打開操作和后續的I/O操作設置為非阻塞模式
下面三個標志也是可選的。它們是Single UNIX Specification(以及POSIX.1)中同步輸入和輸出選項的一部分:
- O_DSYNC 使每次write等待物理I/O操作完成,但是如果寫操作并不影響讀取剛寫入的數據,則不等待文件屬性被更新
- O_RSYNC 使每一個以文件描述符作為參數的read操作等待,直到任何對文件同一部分進行的未完成寫操作都完成
- O_SYNC 使每次write都等待物理I/O操作完成,包括由write操作引起的文件屬性更新所需的I/O
mode參數僅在oflag參數指定了O_CREAT選項時才被使用,用來指定新文件的訪問權限位,這些標志在頭文件sys/stat.h中定義:
- S_IRUSR 讀權限,文件屬主
- S_IWUSR 寫權限,文件屬主
- S_IXUSR 執行權限,文件屬主
- S_IRGRP 讀權限,文件所屬組
- S_IWGRP 寫權限,文件所屬組
- S_IXGRP 執行權限,文件所屬組
- S_IROTH 讀權限,其他用戶
- S_IWOTH 寫權限,其他用戶
- S_IROTH 執行權限,其他用戶
有幾個因素會對文件的訪問權限產生影響。首先,指定的訪問權限只有在創建文件時才會使用。其次,用戶掩碼會影響到被創建的文件的訪問權限,也就是說,open給出的mode值與用戶掩碼的反值做AND運算后的結果,才是文件的真實訪問權限。由open返回的文件描述符一定是最小的未被使用的描述符數值。
creat函數
#include <fcntl.h>int creat(const char *pathname, mode_t mode);
也可以調用creat函數創建一個新文件,該函數等效于:open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode); 函數執行成功返回打開的文件描述符,否則返回-1。
creat的一個不足之處是它以只寫方式打開所創建的文件。
close函數
#include <unistd.h>int close(int filedes);
close函數關閉一個打開的文件,執行成功返回0,否則返回-1。
關閉一個文件時還會釋放該進程加在該文件上的所有鎖。當一個進程終止時,內核會自動關閉它所有打開的文件。
lseek函數
#include <unistd.h>off_t lseek(int filedes, off_t offset, int whence);
每個打開的文件都有一個與其相關聯的當前文件偏移量。它通常是一個非負整數,用以度量從文勁啊開始處計算的字節數。通常,讀、寫操作都從當前文件偏移量處開始,并使偏移量增加所讀寫的字節數。按系統的默認情況,當打開一個文件時,除非指定O_APPEND選項,否則該偏移量被設置為0。
可以調用lseek顯示地為一個打開的文件設置其偏移量。對參數offset的解釋與參數whence的值有關:
- 若whence是SEEK_SET,則該文件的偏移量設置為距文件開始處offset個字節。
- 若whence是SEEK_CUR,則該文件的偏移量設置為當前值加offset,offset可為正或負。
- 若whence是SEEK_END,則該文件的偏移量設置為文件長度加offset,offset可為正或負。
若lseek執行成功,則返回新的文件偏移量,否則返回-1。
通常,文件的當前偏移量應當是一個非負整數,但是,某些設備也可能允許負的偏移量。但對于普通文件,則其偏移量必須是非負值。因為偏移量可能是負值,所以在比較lseek的返回值時應當謹慎,不要測試它是否小于0,而要測試它是否等于-1。
lseek僅將當前的文件偏移量記錄在內核中,它并不引起任何I/O操作。然后,該偏移量用于下一個讀或寫操作。
文件偏移量可以大于文件的當前長度,在這種情況下,對該文件的下一次寫將加長該文件,并在文件中構成一個空洞,所有位于文件中但并沒有寫過的字節都被讀為0。
read函數
#include <unistd.h>ssize_t read(int filedes, void *buf, size_t nbytes);
調用read函數從打開的文件中讀取數據,如果執行成功,則返回讀到的字節數,若已到文件末尾則返回0,出錯則返回-1。
write函數
#include <unistd.h>ssize_t write(int filedes, const void *buf, size_t nbytes);
調用write函數向打開的文件寫入數據。執行成功返回寫入的字節數,出錯則返回-1。
其返回值通常與參數nbytes的值相同,否則表示出錯。
下面是一個示例程序,用來演示底層I/O函數的用法。
1 #include <unistd.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <stdlib.h> 5 6 int main(void) 7 { 8 char block[4096]; 9 int in, out;10 int nread;11 12 in = open("file.in", O_RDONLY);13 out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP);14 while ((nread = read(in, block, 4096)) > 0) {15 write(out, block, nread);16 }17 18 exit(0);19 }