本章開始討論UNIX系統,先說明可用的文件I/O函數---打開文件、讀寫文件等ios
UNIX系統中的大多數文件I/O只需用到5個函數:open、read、write、lseek以及closegit
open函數 返回一個最小的未用描述符網絡
#include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
其中path參數是打開或建立文件的名字,flags參數由下列一個或多個常量進行「或」運算獲得:app
O_RDONLY 只讀打開異步
O_WRONLY 只寫打開函數
O_RDWR 讀、寫打開ui
O_EXEC 只執行打開spa
O_SEARCH 只搜索打開(應用於目錄)3d
這5個常量中必須指定一個,下列常量則是可選的:code
O_APPEND 每次寫時都追加到文件的尾端
O_CLOEXEC 把FD_CLOEXEC常量設置爲文件描述符標誌
O_CREAT 若此文件不存在則建立它。使用此選項時,open函數必須同時說明第三個參數mode
O_DIRECTORY 若是path引用的不是目錄,則出錯
O_EXEL 若是同時指定了O_CREAT,而文件已經存在,則出錯
O_NOCTTY 若是path引用的是終端設備,則不講該設備分配做爲此進程的控制終端
O_NOFOLLOW 若是path應用的是一個符號連接,則出錯
O_NONBLOCK 若是path引用的是一個FIFO、一個塊特殊文件或一個字符特殊文件,則設置此文件本次打開操做跟後續的I/O操做爲非阻塞方式
O_SYNC 使每次write等待物理I/O操做完成
O_TRUNC 若是此文件存在,並且爲只寫或讀寫成功打開,則將其長度截斷爲0
O_TTY_INIT 若是打開一個還未打開的終端設備,設置非標準termios參數值
creat函數
#include <fcntl.h> int creat(const char *path,mode_t mode);
該函數至關於open(path,O_WRONLY|O_CREATE|O_TRUNC,mode);
在第四章咱們會詳細說明文件訪問權限,並說明如何指定mode
close函數
#include <unistd.h> int close(int fd);
close函數用於關閉一個打開文件
lseek函數
#include <unistd.h> off_t lseek(int fd,off_t offset,int whence);
對參數offset的解釋與參數whence的值有關。
若whence是SEEK_SET,則將該文件的偏移量設置爲距距文件開始處offset個字節
若whence是SEEK_CUR,則將該文件的偏移量設置爲其當前值加offset,offset可爲正或負
若whence是SEEK_END,則將該文件的偏移量設置爲文件長度加offset,offset可正可負
若lseek成功執行,則返回新的文件偏移量,爲此能夠用下列方式肯定打開文件的當前偏移量
off_t currpos; currpos=lseek(fd,0,SEEK_CUR);
若是文件描述符指向的是一個管道、FIFO、或者網絡套接字,則lseek返回-1,並將errno設置爲ESPIPE。
read函數
#include <unistd.h> ssize_t read(int fd,void *buf,size_t nbytes);
如read成功,則返回讀到的字節數。如已到達文件的尾端,則返回0。
讀操做從文件的當前偏移量處開始,在成功返回以前,該偏移量將增長實際讀到的字節數。
write函數
#include <unistd.h> ssize_t write(int fd,const void *buf,size_t nbytes);
其返回值一般與參數nbytes的值相同,不然表示出錯。write出錯的一個常見的緣由是磁盤已寫滿,或者超過了一個給定進程的文件長度限制。
對於普通文件,寫操做從文件的當前偏移量處開始,若是打開該文件時,指定了O_APPEND選項,則在每次寫操做以前,將文件偏移量設置在文件的當前結尾處。
在一次成功寫以後,該文件偏移量增長實際寫得字節數。
文件共享
進程爲打開文件維護3張表
若是兩個獨立進程各自打開了同一個文件,則有下圖所示的關係
函數dup和dup2
#include <unistd.h> int dup(int fd); int dup2(int fd,int fd2);
兩個函數均可用來複制一個現有的文件描述符
dup返回的新文件描述符必定是當前可用文件描述符中的最小數值。
dup2能夠用fd2參數指定新描述符的值。若是fd2已經打開,則先將其關閉。若fd等於fd2,則返回fd2,而不關閉它。
這些函數返回的新文件描述符與參數fd共享一個文件表項,以下圖所示:
digit1>&digit2表示要將描述符digit1重定向至描述符digit2的同一文件
理解./a.out > outfile 2>&1與./a.out 2>&1 >outfile的區別
fcnt函數
#include <fcntl.h> int fcntl(int fd,int cmd,.../* int arg */);
fcntl函數能夠改變已經打開文件的屬性,它有如下5種功能
1 複製一個已有的描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC)
2 獲取/設置文件描述符標誌(cmd=F_GETFD或F_SETFD)
3 獲取/設置文件狀態標誌(cmd=F_GETFL或F_SETFL)
4 獲取/設置異步I/O全部權(cmd=F_GETOWN或F_SETOWN)
5 獲取/設置記錄鎖(cmd=F_GETLK、F_SETLK或F_SETLKW)
咱們先說明cmd中的前面8種
F_DUPFD 複製文件描述符fd,返回新的文件描述符。它是還沒有打開的各描述符中大於或等於第三個參數值中各值的最小值
F_DUPFD_CLOEXEC 複製文件描述符,設置與新描述符關聯的FD_CLOEXEC文件描述符標誌的值,返回新的文件描述符
F_GETFD 對應於fd的文件描述符標誌作爲函數值返回,當前只定義了一個文件描述符標誌FD_CLOEXEC
F_SETFD 對於fd設置文件描述符標誌。
F_GETFL 對應於fd的文件狀態標誌做爲函數值返回,下圖列出fcntl的文件狀態標誌
其中,3種訪問方式標誌(O_RDONLY,O_WRONLY,O_RDWR)並不各佔一位,所以首先必須用屏蔽字O_ACCMODE取得訪問方式位,而後將結果與這3個值得每個比較。
F_SETFL 將文件狀態標誌設置爲第三個參數的值。能夠更改的幾個標誌是O_APPEND、O_NONBLOCK、O_SYNC、O_DSYNC、O_RSYNC、O_FSYNC和O_ANSYNC
F_GETTOWN 獲取當前接收SIGIO和SIGURG信號的進程ID或進程組ID。
F_SETOWN 設置接收SIGIO和SIGURG信號的進程ID或進程組ID
下面程序將打印文件狀態標誌說明
#include "apue.h" #include <fcntl.h> int main(int argc, char *argv[]) { int val; if (argc != 2) err_quit("usage: a.out <descriptor#>"); if ((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0) err_sys("fcntl error for fd %d", atoi(argv[1])); switch (val & O_ACCMODE) { case O_RDONLY: printf("read only"); break; case O_WRONLY: printf("write only"); break; case O_RDWR: printf("read write"); break; default: err_dump("unknown access mode"); } if (val & O_APPEND) printf(", append"); if (val & O_NONBLOCK) printf(", nonblocking"); if (val & O_SYNC) printf(", synchronous writes"); #if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC) if (val & O_FSYNC) printf(", synchronous writes"); #endif putchar('\n'); exit(0); }
下面函數用於設置文件狀態標誌
#include "apue.h" #include <fcntl.h> void set_fl(int fd, int flags) /* flags are file status flags to turn on */ { int val; if ((val = fcntl(fd, F_GETFL, 0)) < 0) err_sys("fcntl F_GETFL error"); val |= flags; /* turn on flags */ if (fcntl(fd, F_SETFL, val) < 0) err_sys("fcntl F_SETFL error"); }