所有打開的文件都通過文件描述符引用,文件描述符是一個非負整數,本質是一個數組下標。
當打開或創建一個文件的時候,內核向進程返回一個文件描述符。
open,creat返回文件描述符來標識該文件,將其作爲參數傳遞給write和read。
UNIX系統shell使用文件描述符0與進程的標準輸入相關聯,1和標準輸出相關聯,2和標準錯誤輸出相關聯。:
2 open函數
打開一個文件
- #include <fcntl.h>
- int open(const char * pathname, int oflag, ...);
- //若成功返回文件描述符,不成功返回-1
oflag說明次函數的多個選項:
O_RDONLY 只讀打開 0
O_WRONLY 只寫打開 1
O_RDWR 讀、寫打開 2
以上這三個參數必須且只能定義一個,下列常量是可選的
O_APPEND 每次寫入時追加到文件尾部
O_CREAT 如果文件不存在,則創建。使用第三個參數來限制創建文件的權限。
O_EXCL 如果指定了O_CREAT,則如果存在該文件,則會出錯。
O_TRUNC 如果只讀或只寫打開的文件存在,將其長度截短爲0
O_NOCTTY 如果pathname指定的是終端設備,則不將該設備分配作爲此進程的控制終端。
O_NONBLOCK 如果pathname指定的是一個FIFO,一個塊特殊文件或一個字符特殊文件,則此選項爲文本的本次打開操作和後續的I/O操作設置非阻塞模式。
以下參數是Single UNIX Specification
O_DSYNC 使每次write操作等待物理I/O完成,如果寫操作不影響讀取剛寫入的數據,則不等待文件屬性被更新
O_RSYNC 使以文件描述符爲參數的read等待,直到任何對文件同一部分進行的未決寫操作都完成。
O_SYNC 使每次write操作等待物理I/O完成。
3 creat函數
創建一個文件
- #include <fnctl.h>
- int creat(const char * pathname, mode_t mode);
- //若成功則返回一個以只寫打開的文件描述符,失敗返回-1
open(pathname, O_RDONLY | O_TRUNC | O_CREAT, mode);
這個函數的缺點是返回一個以只寫打開的文件描述符,建議使用open函數來創建文件:
open(pathname, O_RDWR | O_CREAT | O_TRUNC, mode);
4 close函數
關閉一個打開的文件
- #include <unistd.h>
- int close(int filedes);
- //若成功返回0,錯誤返回-1
*編程原則:誰打開誰關閉,誰申請誰釋放
5 lseek函數
每打開一個文件的時候,都有一個與其相關的“當前文件偏移量”,用以度量從文件開始處計算的文件字節數。
按照系統默認情況,當文件打開時,除非指定O_APPEND選項,文件偏移量從0開始。
可以調用lseek來顯示的爲打開文件設置其偏移量。
- #include <unistd.h>
- off_t lseek(int filedes, off_t offset, int whence);
- //若成功返回文件的偏移量,失敗返回-1
SEEK_SET 將文件偏移量設置爲距從文件開始處offset個字節
SEEK_CUR 將文件偏移量設置爲從當前處到offset個字節,offset可以爲正/負
SEEK_END 將文件偏移量設置爲文件長度加offset
可以用下面的方式打開文件的偏移量
- off_t currpos;
- currpos = lseek(fd, 0, SEEK_CUR);
從打開的文件讀取數據
- #include <unistd.h>
- ssize_t read(int filedes, void * buf, size_t nbytes);
- //若成功返回讀到的字節數,若以到達文件結尾返回0,若出錯返回-1
size_t 不帶符號的整數
以下情況讀到的字節數少於要求的字節數
1.在讀到要求字節數之前已經讀到文件結尾
2.當從終端設備文件讀時
3.從網絡讀時
4.從管道或者FIFO讀時
5.從某些面向記錄的設備讀時(磁帶)
6.信號影響
讀操作從文件的當前偏移量開始,讀取完畢該偏移量加上讀取的實際字節數
7 write函數
向打開的文件中寫入數據
- #include <unistd.h>
- ssize_t write(int filedes, const void * buf, size_t nbytes);
- //若成功返回寫入的字節數,若失敗返回-1
8 文件共享
內核使用三種數據結構來表示打開的文件,它們之間的關係決定了在文件共享方面一個進程對另一個進程可能產生的影響。
1.每個進程在進程表中都有一個記錄項,記錄項中包含一張打開文件描述符表,可將其視爲一個矢量,每一個文件描述符佔用一項,與每個文件描述符相關聯的是:
a.文件描述符標誌
b.指向一個文件表項的指針
2.內核爲每個打開文件維持一個文件表,每個文件表含:
a.文件狀態標誌
b.當前文件偏移量
c.指向該文件v節點表項的指針
3.每打開一個文件都有一個v節點結構。包含了文件類型和對此文件進行各種操作的函數的指針。
如果有兩個獨立的進程各自打開了同一個文件,打開文件的每一個進程都將得到一個文件表項,但是對於一個給定的文件只有一個v節點表項。
這種安排使得每個進程都有它自己的對該文件的當前偏移量。
但是對於多個進程對同一文件進行寫操作的時候就會產生不可預期的錯誤,所以要介紹原子操作。
9 原子操作
爲了避免多進程操作同一文件會產生覆蓋的影響,有如下函數,該函數首先操作lseek,然後在進行讀寫操作
- #include <unistd.h>
- ssize_t pread(int filedes, void * buf, size_t nbytes, off_t offset);
- ssize_t pwrite(int filedes, const void * buf, size_t nbytes, off_t offset);
- //其返回值和read,write類似
複製一個現存的文件描述符
#include <unistd.h>
int dup(int filedes);
int dup(int filedes, int filedes2);
//若正確,返回新的文件描述符,錯誤返回-1
由dup返回的文件描述符一定是當前可用的最小的文件描述讀
dup2函數則用filedes2來指定新文件描述符,如果filedes2已經存在,則先將其關閉。若filedes2等於filedes則返回filedes2,而不關閉
11 sync, fsync, fdatasync函數
保證了磁盤上實際文件系統與緩衝區高速緩存中內容的一致性
- #include <unistd.h>
- int fsync(int filedes);
- int fdatasync(int filedes);
- //若成功返回0,失敗返回-1
- void sync(void);
通常系統的守護進程update會定期調用sync,命令sync也是調用sync函數
fsync對文件描述符filedes指定的單一文件起作用,並且等待實際寫磁盤操作結束。同時也會同步更新文件的屬性。
fdtatsync和fsync類似,不過它值針對與數據部分。
12 fcntl函數
可以改變以打開文件的屬性
- #include <fcntl.h>
- int fcntl(int filedes, int cmd, ...);
- //若成功則返回值依賴於cmd,失敗返回-1
1.F_DUPFD 複製一個現有的文件描述符,與dup,dup2函數類似,返回一個文件描述符
2.F_GETFD或F_SETFD 獲得/設置文件描述符標記,返回文件描述符標記
3.F_GETFL或F_SETFL 獲得/設置文件狀態標誌,返回文件狀態標誌
O_RDONLY 只讀打開
O_WRONLY 只寫打開
O_RDWR 讀寫打開
O_APPEND 追加
O_NONBLOCK 非阻塞模式
O_SYNC 等待寫完成(數據和屬性)
O_DSYNC 等待寫完成(數據)
O_RSYNC 同步讀寫
O_FSYNC 等待寫完成
O_ASYNC 異步I/O
前三個訪問標誌是互斥的,因此首先必須使用屏蔽字O_ACCMODE取得訪問模式位,在比較
4.F_GETOWN或F_SETOWN 獲得/設置異步I/O所有權,返回一個正的進程ID或一個負的進程組ID
5.F_GETLF、F_SETLK或F_SETLKW 獲得/設置記錄鎖
13 ioctl函數
I/O操作的雜物箱
- #include <unistd.h> // System V
- #include <sys/ioctl.h> // BSD and linux
- #include <stropts.h> // XSI STREAMS
- int ioctl(int filedes, int request, ...);
- //若出錯返回-1,成功返回其他值
在此原型中,我們表示的只是ioctl函數本身所要求的頭文件。通常還需要另外的設備專用頭文件<termios.h>
14 /dev/fd
在較新的系統都提供名爲/dev/fd的目錄。打開/dev/fd/n等效於複製描述符n