大多數函數只需要五個函數實現IO操作:open, read, write, lseek, close.
一. 文件描述符:
文件描述符時一個非負整數(0~OPEN_MAX - 1).當打開現有文件或者創建新文件時,內核向進程返回一個文件描述符一邊標示一個文件。
內核文件描述符要區別於shell文件描述符:shell內定了幻數0, 1, 2分別表示標準輸入,標準輸出和標準錯誤。
二. 文件操作函數:
1.open或openat:
(1). 打開文件,成功返回文件描述符,失敗返回-1.
#include <fcntl.h>
int open(const char *path, int oflag, ... /*mode_t mode */);
int openat(int fd, const char *path, int oflag, ... /*mode_t mode */);
(2). 參數:
a. path表示文件路徑;
b. oflag用來說明該函數的多個選項,如:O_RDONLY, O_WRONLY, O_RDWR分別表示只讀,只寫和可讀寫,而O_CREAT選項表示當打開文件不存在時創建這個文件,O_DIRECTORY選項表示如果path應用的不是目錄則出錯。這些選項都可以通過man page查詢獲得。
c. …參數:ISO C用這種方法表示剩餘參數爲可變參數,即在需要時可以變參。
(3). 文件描述符fd參數將open和openat區分開來:
a. 當path指定的爲絕對路徑時,fd參數被忽略,兩函數相等。
b. 怕path指出相對路徑名時,fd參數相對路徑名在文件系統中的開始地址。fd參數是通過打開相對路徑名所在的目錄來獲取的。
c. path參數指定了相對路徑名,fd參數具有特殊值AT_FDCWD。在這種情況下路徑名在當前工作目錄中獲取,openat和open類似。
2.creat
(1). 創建只寫文件,成功返回只寫文件描述符,失敗返回-1.
#include <fcntl.h>
int creat(const char *path, mode_t mode);
此函數等效於:
open(path, O_RDWR|O_CREAT|O_TRUNC, mode);
(2). 參數:
a. path爲路徑
b. mode爲權限模式
(3). creat函數只能以只寫方式打開所創建的文件。早期版本中使用該函數,現在多用open函數代替
open(path, O_RDWR|O_CREAT|O_TRUNC, mode);
3.close
(1). 關閉文件,成功返回0,出錯返回-1.
#include <unistd.h>
int close(int fd);
(2). 當文件關閉同時會釋放所有加在該文件上的記錄鎖。
(3). 當一個文件終止時,內核會自動關閉它所打開的所有文件。所以close函數一般不現式使用。
4.lseek
(0). 文件偏移量:通常是一個非負整數,用以度量從文件開始處計算的字節數。
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
(1). 設置文件偏移量,成功返回返回新的文件偏移量,失敗返回-1.
(2). 參數:
a. offset參數解釋和whence值有關:
-> whence爲SEEK_SET時,文件偏移量將被設爲距文件開始offset個字節。
-> whence爲SEEK_CUR時,文件偏移量將被設爲當前偏移量加上offset(offset正負都可)。
-> whence爲SEEK_END時,文件偏移量將被設爲文件長度加offset(offset正負都可)。
(3). 管道,FIFO, 網絡套接字不可設置偏移量,若對其操作,返回-1,errno爲ESPIPE。
(4). 一般情況文件的偏移量爲一個非負整數,但是某些特殊設備時允許負的偏移量的,so~,判斷文件偏移量是否設置成功應該看看他的lseek返回值是否爲-1,而不是判斷它是不是負數。
(5). lseek僅僅時將文件偏移量記錄在內核中,他本身並不引起IO操作,這個偏移量的存在只是給下一次的讀和寫操作的。
(6). 文件空洞:文件偏移量時可以大於文件當前長度的,這樣如果得到偏移量後的執行一次寫操作,寫的位置和文件開頭就存在了一片空白區,這段區域被字符0填滿,稱之爲空洞,(空洞不要求佔用磁盤存儲區)。
5.read
(1). 讀文件,返回讀到的字節數,弱國已經讀到文件末尾了,則返回0,失敗返回-1。
#include <unisted.h>
ssize_t read(int fd, void *buf, size_t nbytes);
(2). 在下述情況下,函數實際讀到的字節數少於要求讀到的字節數。
a. 要求讀的大小大於文件本身大小時:
在還沒讀到文件要求大小時文件已經讀到末尾了.例如:如文件只有30字節,而要求 讀100字節,這樣read函數將返回30,下次調用read時它將返回0.b. 從終端讀設備讀時:
一次只讀一行,和標準輸出按行刷新對應.c. 從網絡讀數據時:
網絡緩存機制導致返回值小於要求讀的大小.d.從管道讀取數據時:
返回實際管道可用數據.e. 從面向記錄設備讀時:
一次只能讀一條記錄.f. 讀過程中遇到信號中斷時:
返回實際讀到的數據.g. 返回值類型(ssize_t)爲帶符號整數:
爲確保函數可以返回正整數字節,0(到文件尾),-1(出錯).
6.函數write.
(1). 寫入函數,向文件描述符爲fd的已打開文件中寫數據,成功返回寫入字節數,出錯返回-1.
(2). write函數的返回值通常與nbytes值相同,否則表示出錯.
(3). 出錯原因常見爲:
a.磁盤已滿.
b.超過給定進程的文件長度限制.
(4).普通文件執行寫使用write時,寫操作是從文件當前偏移量處開始.
在文件打開時設置追加屬性O_APPEND,這樣在每次寫操作時文件偏移量就會被設置在文件末尾處,寫成功後,文件偏移量增加寫入文件長度.
例:用read和write函數實現簡單函數複製:
#include <apue.h> //apue.h頭文件中包含有write,read以及基本操作頭文件
#define BUFFSIZE 4096
int main(int argc, char **argv)
{
int n;
char buf[BUFFSIZE];
//從標準輸入讀文件,每次讀BUFFSIZE個字節,讀到buf數組中
while((n == read(STDIN_FILENO, buf, BUFFSIZE)) > 0){
//read正確時,寫入文件到標準輸出中,一次寫n個字節
if(write(STDOUT_FILENO, buf, n) != n){
err_sys("write error!");
}
}
//read函數返回-1時,輸出錯誤信息並退出
if(n < 0){
err_sys("read error!");
}
exit(0);
}