文件描述符
所有打開(kāi)的文件都通過(guò)文件描述符引用。操作(讀寫(xiě))該文件描述符就相當(dāng)于操作該文件宪拥。
文件描述符是一個(gè)非負(fù)的整數(shù)。
同時(shí)文件描述符0,1,2都已經(jīng)對(duì)應(yīng)標(biāo)準(zhǔn)輸出(輸出在終端上),標(biāo)準(zhǔn)輸入(從標(biāo)準(zhǔn)終端輸入)稍算,標(biāo)準(zhǔn)錯(cuò)誤。
對(duì)應(yīng)STDIN_FILENO
,STDOUT_FILENO
,STDERR_FILENO
役拴。
這三個(gè)常量定義在<unistd.h>
中糊探。
open和openat
#include <fcntl.h>
int open(const char * path ,int flag, mode_t =NULL)
int openat(int fd,const char * path ,int flag, mode_t =NULL)
兩者的主要區(qū)別是,open
只能相對(duì)于程序運(yùn)行的目錄河闰,而openat
可以使相對(duì)于fd
指定的目錄進(jìn)行打開(kāi)操作科平,同時(shí)fd
的特殊值AT_FDCWD
指的是當(dāng)前目錄。
同時(shí)可以避免TOCTTOU
,意思是如果一個(gè)操作需要兩個(gè)文件操作才能完成姜性,那么中間可能cpu調(diào)度的原因瞪慧,第一個(gè)文件操作已經(jīng)被更改,從而后一個(gè)操作也會(huì)不正確部念∑茫可以用來(lái)獲取特殊權(quán)限。
flag
這個(gè)參數(shù)主要指定了打開(kāi)模式:
一下五個(gè)只能出現(xiàn)一個(gè):
-
O_RDONLY
只讀打開(kāi)儡炼,0 -
O_WDONLY
只寫(xiě)打開(kāi)妓湘,1 -
O_RDWE
讀寫(xiě)打開(kāi),2 -
O_EXEC
只 執(zhí)行打開(kāi)乌询。 -
SEARCH
只搜索打開(kāi)榜贴,用于目錄
一下這些常量是可選的使用|
運(yùn)算與上面的結(jié)合。
-
O_APPEND
每次寫(xiě)入到尾端 -
O_CLOEXEC
當(dāng)調(diào)用exec()
函數(shù)成功后妹田,文件描述符會(huì)自動(dòng)關(guān)閉 -
O_CREAT
如果打開(kāi)的文檔不存在唬党,就自動(dòng)創(chuàng)建鹃共,同時(shí)需要指定mode_t
參數(shù) -
O_DICECTORY
如果打開(kāi)的不是目錄jiubaocuo -
O_EXCL
如果同時(shí)指定O_CREAT
和O_EXCL
,當(dāng)文件存在的時(shí)候就出錯(cuò)初嘹。 O_NOCTTY
-
O_NOFOLLOW
如果打開(kāi)的是一個(gè)連接符號(hào)及汉,就出錯(cuò) -
O_NONBLOCK
非阻塞打開(kāi),如果沒(méi)讀到數(shù)據(jù)就出錯(cuò)返回屯烦。 -
O_SYNC
寫(xiě)入數(shù)據(jù)是坷随,需要等到數(shù)據(jù)真正寫(xiě)入到硬盤(pán)時(shí)才返回,包括文件屬性的更新驻龟。
ext4下温眉,同步寫(xiě)和延時(shí)寫(xiě)差距不大?hfs下差距很大翁狐。和系統(tǒng)有關(guān)类溢? -
O_DSYNC
寫(xiě)入數(shù)據(jù)是,需要等到數(shù)據(jù)真正寫(xiě)入到硬盤(pán)時(shí)才返回露懒,如果不影響讀取剛寫(xiě)入的數(shù)據(jù)闯冷,則不需要等待文件屬性更新。 -
O_RSYNC
讀操作會(huì)等待所有對(duì)該文件的寫(xiě)操作完成以后才執(zhí)行懈词。 -
O_DICECT
看不懂
creat()
#include <fnctl.h>
int creat(const char* path,mode_t mode)
//出錯(cuò)返回-1
缺點(diǎn)是都是以只寫(xiě)操作打開(kāi)該文檔蛇耀,所以如果要讀,需要關(guān)閉該文件描述符重新打開(kāi)坎弯。
一般使用open(path,O_RDWR|O_CREAT,mode)
代替纺涤。
close()
#include <unistd.h>
int close(int fd);
//成功返回0 ,出錯(cuò)返回-1
關(guān)閉文檔抠忘,釋放所有記錄鎖撩炊。
進(jìn)程關(guān)閉以后,會(huì)關(guān)閉所有該進(jìn)程所打開(kāi)的文件崎脉。
lseek()
#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence);
//出錯(cuò)返回-1拧咳,否則返回新的穩(wěn)健偏移量
whence
偏移的起點(diǎn)
- SEEK_SET
將文件的偏移量設(shè)置為距文件開(kāi)頭offset個(gè)字節(jié),也就是說(shuō)囚灼,起點(diǎn)是開(kāi)頭 - SEEK_CUR
偏移起點(diǎn)文當(dāng)前起點(diǎn)呛踊,offset可正可負(fù) - SEEK_END
偏移起點(diǎn)為文件的總長(zhǎng)度。也就是說(shuō)起點(diǎn)在末尾啦撮。
如果將文件描述符對(duì)一個(gè)管道FIFO或者套接字使用,那么返回-1
汪厨,并且赃春,errno為ESPIPE
因?yàn)檫@三個(gè)都是流式的,只能讀寫(xiě)劫乱,不能設(shè)置偏移量织中。
如果將偏移量定位在結(jié)尾之后開(kāi)始寫(xiě)入數(shù)據(jù)锥涕,那么中間的空洞被讀為0。
read()
#include <unistd.h>
ssize_t read(int fd,void *buf,size_t n);
//成功返回讀到的字節(jié)數(shù)狭吼,到文件尾返回0层坠,出錯(cuò)返回-1
ssize_t是一個(gè)帶符號(hào)的整數(shù)。而size_t
是一個(gè)不帶符號(hào)的整數(shù)刁笙。
讀到的字節(jié)小于設(shè)置的字節(jié)數(shù)原因:
- 讀到末尾
本次返回讀到的數(shù)據(jù)破花,下一次返回0 - 從終端設(shè)備讀,每次只能一行
- 從網(wǎng)絡(luò)中讀是疲吸,網(wǎng)絡(luò)的緩存機(jī)制可能造成
- 管道和FIFO中時(shí)座每,管道中的字節(jié)數(shù)少于所需數(shù)量
- 信號(hào)中斷前已經(jīng)讀到數(shù)據(jù)
讀文件從文件當(dāng)前偏移量開(kāi)始,成功返回之前摘悴,改變偏移量峭梳。
write()
#include <unistd.h>
ssize_t write(int fd,const buf*,size_t n);
//成功返回寫(xiě)入的字節(jié)數(shù)蹂喻,錯(cuò)誤返回-1
從偏移量位置開(kāi)始寫(xiě)葱椭。
如果設(shè)置了O_APPEND
則每次寫(xiě)操作之前將文件偏移量設(shè)置在文件的結(jié)尾處。
根據(jù)局部性原理口四,系統(tǒng)會(huì)預(yù)讀比所指定數(shù)量多的數(shù)據(jù)孵运,并假設(shè)它們很快會(huì)被用到。
文件共享
多進(jìn)程間操作同一文件窃祝。
文件描述符標(biāo)志和文件狀態(tài)標(biāo)志
- 文件描述符標(biāo)志
是體現(xiàn)進(jìn)程的文件描述符的狀態(tài)掐松,fork進(jìn)程時(shí),文件描述符被復(fù)制粪小;
目前只有一種文件描述符:FD_CLOEXEC
大磺。指明是否復(fù)制進(jìn)新的進(jìn)程。 - 文件狀態(tài)標(biāo)志
是體現(xiàn)進(jìn)程打開(kāi)文件的一些標(biāo)志探膊,fork時(shí)不會(huì)復(fù)制file 結(jié)構(gòu)杠愧,而是兩個(gè)進(jìn)程文件描述符指向同一個(gè)file(當(dāng)FD的exec標(biāo)志為0時(shí))
內(nèi)和使用三種結(jié)構(gòu)表示打開(kāi)的文件
- 內(nèi)核的記錄項(xiàng):文件描述符標(biāo)志,文件表項(xiàng)逞壁,V節(jié)點(diǎn)表項(xiàng)
每個(gè)進(jìn)程在進(jìn)程表中都有一個(gè)記錄項(xiàng)流济,每個(gè)描述符占一項(xiàng)。
每一項(xiàng)都有一個(gè)標(biāo)志腌闯,該標(biāo)志表示該文件描述符的特性绳瘟,可以使用fnctl()
獲取和改變。
一個(gè)指向文件表項(xiàng)的指針(指向內(nèi)核中) - 內(nèi)核的文件表
內(nèi)核記錄文件的狀態(tài)標(biāo)志姿骏,文件表項(xiàng)中記錄著文件狀態(tài)標(biāo)志:阻塞糖声,同步的方式。在該進(jìn)程中打開(kāi)的文件的偏移量(不同進(jìn)程中可以不同)。還有一個(gè)指向V節(jié)點(diǎn)表項(xiàng)的指針蘸泻。 - V節(jié)點(diǎn)結(jié)構(gòu)
V節(jié)點(diǎn)表項(xiàng)是所有進(jìn)程共有一份琉苇,表示文件的信息:長(zhǎng)度,修改時(shí)間等悦施。
應(yīng)該是存在硬盤(pán)上并扇。
不同進(jìn)程公用一份。
原子操作的IO
讀寫(xiě)
#include <unistd.h>
ssize_t pread(int fd,void *buf,size_t n,off_t offset);
ssize_t pwrite(int fd,const void *buf,size_t n,off_t offset);
//返回值同以前
但是有用嗎抡诞?offset
的值如何確定穷蛹?
復(fù)制文件描述符
#include <unistd.h>
int dup(int fd);
int dup2(int fd,init fd2);
//出錯(cuò)返回-1
第二個(gè)函數(shù),在fd2
出復(fù)制fd1
,如果fd2
處已經(jīng)有一個(gè)文件打開(kāi)了沐绒,那么先關(guān)閉俩莽,在打開(kāi)。是一個(gè)院子操作乔遮。
如果相同扮超,那么步步操作,直接返回蹋肮。
復(fù)制以后出刷,兩個(gè)文件描述符共享一個(gè)文件表項(xiàng)。
緩沖
#incldue <unistd.h>
int sync(void);
int fsync(int fd);
int fdataasync(int fd)
//錯(cuò)誤返回-1
- sync
將所有修改過(guò)的緩存坯辩,排入寫(xiě)隊(duì)列馁龟。也就是實(shí)際寫(xiě)入硬盤(pán),但是不等待寫(xiě)入結(jié)束漆魔。
對(duì)所有文件
系統(tǒng)周期性執(zhí)行該函數(shù)坷檩。 - fsync
對(duì)指定fd,將修改過(guò)的緩存寫(xiě)入硬盤(pán)改抡,并且修改文件屬性矢炼,結(jié)束后返回 - fdatasync
對(duì)指定fd,將修改過(guò)的緩存寫(xiě)入硬盤(pán)阿纤,結(jié)束后返回句灌,可以不用等待修改文件屬性結(jié)束。
fcntl()
#include <fcntl.h>
int fcntl(int fd, int cmd,...)
第三個(gè)參數(shù)是一個(gè)整數(shù)(在get時(shí)欠拾,不需要該參數(shù)胰锌,設(shè)為0),或是一個(gè)指針
功能如下:
- 復(fù)制文件描述符
cmd = F_DUPFD 或 F_DUPFD_CLOEXEC
cmd = F_DUPFD
返回未用最小的整數(shù)作為新的描述符返回藐窄,但是該文件描述符有自己獨(dú)立的文件描述符標(biāo)志资昧,同時(shí)清除了O_CLOEXEC
標(biāo)志。而dup()
打開(kāi)的是共享的荆忍。
cmd = F_DUPFD_CLOEXEC同
dup2`榛搔,需要第三個(gè)參數(shù) - 設(shè)置獲取文件描述符狀態(tài)
cmd = F_GETFD 和 F_SETFD
目前可設(shè)置和獲取的只有一種诺凡,是否在新的線程中復(fù)制一份。
set時(shí)需要第三個(gè)參數(shù)践惑。0不關(guān)閉,1關(guān)閉嘶卧。 - 設(shè)置獲取文件狀態(tài)標(biāo)志
cmd = F_GETFL 和 F_SETFL
指的是尔觉,讀寫(xiě),阻塞芥吟,寫(xiě)模式(append)侦铜,同步讀寫(xiě).
也就是打開(kāi)open
的參數(shù)。
在獲取的時(shí)候钟鸵,由于讀寫(xiě)的五中模式互斥钉稍,所以需要使用一個(gè)屏蔽字O_ACCMODE與運(yùn)算,取得訪問(wèn)方式位棺耍,然后挨個(gè)比較
int val = fcntl(fd,F_GETFL,0)贡未;
switch (val & O_ACCMODE)
case O_RDONLY:
case .....
而其余可選的狀態(tài)標(biāo)志,不需要蒙袍。直接進(jìn)行與運(yùn)算
if (val & O_APPEN)
{
}
但是設(shè)置的時(shí)候只能是堵塞模式俊卤,緩存模式。設(shè)置的時(shí)候需要先獲取害幅,然后進(jìn)行|=
然后再設(shè)置消恍。不然會(huì)清除之前的設(shè)置。
- 設(shè)置獲取異步IO
cmd = F_GETOWN 和 F_SETOWN
- 獲取設(shè)置記錄所
cmd = F_GETLK F_SETLK F_SETLKW
/dev/fd/n
打開(kāi)文件以现,等于復(fù)制文教描述符狠怨。
int fd=open("/dev/fd/1",O_RDWR);
雖然設(shè)置了讀寫(xiě),但是只能使用之前打開(kāi)的模式邑遏,比如之前是寫(xiě)模式佣赖,那么使用這種方式打開(kāi)也不能讀。