標準輸入:0
標準輸出:1
錯誤輸出:2
符號常量爲:STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO
open或者openat:
int open(const char* path,int oflag,....);
int openat(int fd,const char *path,int oflag,...);
path:要打開或者要創建的名字
oflag:說明此函數的多個選項
O_RDONLY 只讀打開
O_WRONLY 只寫打開
O_RDWR 讀寫打開
大多數的實現將O_RDONLY定義爲0,O_WRONLY定義爲1,O_RDWR定義爲2
O_EXEC 只執行打開
O_SEARCH 只搜索打開
這5個常量中必須指定一個且只能指定一個。下列常量則是可選的:
O_APPEND 每次都追加到文件的尾端
O_CLOEXEC 把FD_CLOEXEC常量設置爲文件描述符標誌
O_CREAT 若文件不存在則創建它。使用這個選項的時候,
open參數需同時說明第三個參數mode(openat則是第四個)
用mode指定該新文件的訪問權限位,可以使用進程umask
值修改它
O_DIRECTORY 如果path不是目錄,則出錯
O_EXCL 如果同時指定了O_CREAT,而文件已經存在,則出錯
O_NOCTTY 如果path引用的是終端設備,則不將該設備分配作爲此進程的
終端控制
O_NOFOLLOW 如果path引用的是一個符號連接,則出錯
O_NONBLOCK 如果path引用的是一個FIFO、一個塊特殊文件或一個字符
特殊文件,則此選項爲文件的本次打開操作和後續的I/O
操作設置非組塞模式
O_SYNC 每次write等待物理I/O操作完成,包括由該write操作引起
的文件屬性跟新所需的I/O
O_TRUNC 如果此文件存在,而且爲只寫或者是讀-寫成功打開,則將其
長度截斷爲0
O_TTY_INIT 如果打開一個還未打開的終端設備,設置非標準termios參數
值,使其符合signal UNIX Specification
O_DSYNC 使每次write要等待物理I/O操作完成,但是如果寫操作並不
影響讀取剛寫入的數據,則不需等待文件屬性被更新
O_RSYNC 使每一個文件描述符作爲參數進行的read操作等待,直至
所有的對文件同一部分掛起的寫操作都完成了
creat函數:創建一個新的文件
int creat(const char *path,mode_t mode);
等效於open(path,O_WRONLY|O_CREAT|O_TRUNC,mode);
不足是它以只寫的方式打開所創建的文件。
使用open實現:
open(path,O_RDWR | O_CREAT | O_TRUNC,mode);
close函數:打開一個文件
#include <unistd.h>
int close(int fd);
關閉一個文件的時候還會釋放該進程加在該文件上的所有記錄鎖。
當一個進程終止的時候,內核自動關閉它所有的打開文件。
lseek函數:當前文件偏移量
調用lseek顯式爲一個打開文件設置偏移量
#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence);
其中offset於參數whence有關:
SEEK_SET:設置偏移量爲離文件開始處offset個字節
SEEK_CUR:設置偏移量爲離當前位置加offset,可正可負
SEEK_END:設置偏移量爲離文件末尾加offset
off_t currpos;
currpos=lseek(fd,0,SEEK_CUR);
如果文件描述符指向的是一個管道、FIFO、網絡套接字,則lseek
返回-1,並將errno設置爲ESPIPE.
whence:0(絕對偏移量)
whence:1(相對當前位置的偏移量)
whence:2(相對文件末尾的偏移量)
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void){
if(lseek(STDIN_FILENO,0,SEEK_CUR)==-1){
printf("cannot seek\n");
}
else
printf("seek ok\n");
exit(0);
}
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);
若返回成功,表示的是以寫的字節數,若失敗,返回的是-1。
pread和pwrite:
#include <unistd.h>
ssize_t pread(int fd,void *buf,size_t nbytes,off_t offset);
返回值:讀到的字節數,若已到文件尾,返回0;若出錯,返回-1
ssize_t pwrite(int fd,const void *buf,size_t nbytes,off_t offset);
返回值:若成功,返回已寫的字節數;若出錯,返回-1
調用pread相當於調用lseek後調用read,但是pread有區別:
1、調用pread時,無法中斷其定位和讀操作
2、不更新當前文件偏移量
調用pread相當於調用lseek後調用write,但是pwrite有類似的區別
dup和dup2函數:複製一個現有的文件描述符
#include <unistd.h>
int dup(int fd);
int dup2(int fd,int fd2);
有dup返回的新文件描述符一定是當前可用文件描述符中的最小的數值。
對於dup2,可以用fd2參數指定新描述符的值。如果fd2已經打開,則先將其關閉。
如若fd等於fd2,則dup2返回fd2,而不關閉它。否則,fd2的FD_CLOSEXEC文件
描述符標誌就被清除,這樣fd2在進程調用exec時是打開狀態.
調用dup(fd);
等效於fcntl(fd,F_DUPFD,0);
而調用dup2(fd,fd2);
等效於:
close(fd2);
fcntl(fd,F_DUPFD,fd2);
dup2並不完全等同於以上,區別如下:
1、dup2是一個原子操作,而close和fcntl包括兩個函數調用。
可能在close和fcntl之間調用了信號捕獲函數,它們可能修改文件
描述符,如果不同線程改變了文件描述符也會出現問題
2、dup2和fcntl有一些不同的errno
sync、fsync和fdatasync函數:
傳統的UNIX系統實現在內核中設有緩衝區高速緩存或者頁高速緩存,大多數磁盤
I/O都通過緩衝區進行。
當我們向文件寫入數據時,內核通常先將數據複製到緩衝區中,然後排入隊列,晚些時候
再寫入磁盤。
通常,當內核需要重用緩衝區來存放其他磁盤塊數據時,它會把所以延遲寫數據塊寫入磁盤
爲了保證磁盤上實際文件系統與緩衝區中內容的一致性,UNIX系統提供了sync、fsync、
fdatasync函數。
#include <unistd.h>
int fsync(int fd);
int fdatasync(int fd);
void sync(void);
sync只是將所以修改過的塊緩衝區排入寫隊列,然後就返回,它並不等待實際寫磁盤
操作結束。
通常,成爲update的系統守護進程週期性地調用(一般隔30秒)sync函數。這就
保證了定期沖洗(flush)內核的塊緩衝區。命令sync(l)也調用sync函數
fsync只對由文件描述符fd指定的一個文件起作用,並且等待 寫磁盤操作結束。
fsync可用於數據庫這樣的應用程序。
fdatasync函數類似於fsync函數,但它隻影響文件的數據部分。而除數據外fsync
還會同步更新文件的屬性。
簡單說文件描述符:
文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核爲每一個進程所維護
的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回
一個文件描述符。
fcntl函數:改變已經打開文件的屬性
#include <fcntl.h>
int fcntl(int fd,int cmd,.../*int arg*/);
返回值:成功,則依賴於cmd,出錯,返回-1。
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);
F_DUPFD 複製文件描述符fd。新文件描述符作爲函數只返回。它是尚未打開的
各描述符大於或者等於第3個參數值(取爲整形值)中各值的最小值。
新描述符與fd共享同一文件表項。但是,新描述符有它自己的一套文件
描述符標誌,其FD_CLOEXEC文件描述符標誌被清除(這表示該描述符在
exec時仍保持有效)
F_DUPFD_CLOEXEC 複製文件描述符,設置與新描述符關聯的FD_CLOEXEC文件描述符標
志的值,返回新文件描述符。
F_GETFD 對應與fd的文件描述符標誌作爲函數值返回。當前只定義了一個文件
描述符標誌FD_CLOEXEC。
F_SETFD 用於設置文件描述符標誌。新標誌值按第3個參數(取爲整數值)設置
現在很多程序不使用FD_CLOEXEC,而是將此標誌設置爲0,(系統默認)
在exec時不關閉,1在exec關閉
F_GETFL 對應與fd的文件狀態標誌作爲函數值返回
F_SETFL 將文件狀態標誌設置爲第3個參數的值,(取整形值)。
可以更改的標誌爲:O_APPEND O_NONBLOCK O_SYNC O_DSYNC
O_RSYNC O_FSYNC O_ASYNC
F_GETOWN 獲取當前接受SIGIO和SIGURG信號的進程ID或進程組ID。
F_SETOWN 設置接受SIGIO和SIGURG信號的進程ID或進程組ID。
正的arg指定一個進程ID,負的arg表示等於arg絕對值的一個進程組ID
fcntl的返回值與命令有關。如果出錯,所以命令都返回-1,如果成功返回其他值
F_DUPFD:返回的是新的文件描述符
F_GETFD:返回的時相應的標誌
F_GETFL:返回的時相應的標誌
F_GETOWN:返回一個正的進程ID或者一個負的進程組ID
例子:
O_ACCMODE<0003>:讀寫文件操作時,用於取出flag的低2位
O_RDONLY<00>:只讀打開
O_WRONLY<01>:只寫打開
O_RDWR<02>:讀寫打開
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main(int argc,char *argv[]){
int val;
if(argc!=2){
printf("usage:a.out <descriptor#>");
}
if((val=fcntl(atoi(argv[1]),F_GETFL,0))<0)
printf("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:
printf("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);
}
運行:
./a.out 0 < /dev/tty
./a.out 1 > temp.foo
./a.out 2 2>>temp.foo
./a.out 5 5<>temp.foo
字句5<>temp.foo表示的時文件描述符5上打開文件temp.foo以供讀、寫。
修改文件描述符標誌或者是文件狀態標誌(示例函數):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
void set_fl(int fd,int flags)
{
int val;
if((val=fcntl(fd,F_GETFL,0))<0)
{
printf("fcntl F_GETFL error");
}
val |= flags;
if(fcntl(fd,F_SETFL,val)<0)
printf("fcntl F_SETFL error");
}
其中將一條語句改爲:
val &=~flags;//表示的時關閉某個文件標誌
如果加上set_fl(STDOUT_FILENO,O_SYNC);開啓了同步寫操作
這樣使得每次write都要等待,直到數據已寫到磁盤上再返回。
ioctl函數:
ioctl函數一直是I/O操作的雜物箱。
#include <unistd.h>
#include <sys/ioctl.h>
int ioctl(int fd,int request,...)
/dev/fd:
較新的系統都提供名爲/dev/fd的目錄,其目錄項是名爲0,1,2等文件
函數的調用:
fd=open("/dev/fd/0",mode);
大多數系統忽略它所指定的mode,而另外一些系統則要求mode必須是所引用的文件
在這裏是標準輸入,初始打開是所使用的打開模式的一個子集。
上面等效於:fd=dup(0);
若描述符0先前打開爲只讀,那麼我們也只能對fd進行讀操作。即使系統忽略打開模式,
而且下列調用是成功的:
fd=open("/dev/fd/0",O_RDWR);
我們仍然不能對fd進行寫操作。
文件IO
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.