文件I/O

  • 所有執(zhí)行I/O操作的系統(tǒng)調(diào)用都以文件描述符(一個(gè)非負(fù)整數(shù))來(lái)指代打開的文件。包括pipe涵紊,F(xiàn)IFO傍妒,socket,終端摸柄,設(shè)備和普通文件颤练。
  • 針對(duì)每一個(gè)進(jìn)程,內(nèi)核都會(huì)為其維護(hù)一個(gè)文件描述符表塘幅,每一個(gè)表項(xiàng)都是對(duì)打開文件句柄的引用+文件描述符標(biāo)志二元組(不是文件狀態(tài)標(biāo)志昔案,且目前為止只定義了一個(gè)標(biāo)識(shí)就是close-on-exec)
  • 通過(guò)文件描述符在文件描述符表中找到相對(duì)應(yīng)的引用,然后可以找到對(duì)應(yīng)的文件表項(xiàng)电媳;在文件表中記錄了以下信息
1. 當(dāng)前文件偏移量
2. 文件狀態(tài)標(biāo)志
3. 文件訪問(wèn)模式
4. 信號(hào)驅(qū)動(dòng)IO相關(guān)
5. inode引用
6. v節(jié)點(diǎn)引用
  • inode中記錄了以下信息
1. 文件訪問(wèn)權(quán)限和文件類型
2. 文件鎖列表
3. 文件的各種屬性(包括文件大小以及相關(guān)時(shí)間屬性)
4. 數(shù)據(jù)塊編號(hào)
  • v節(jié)點(diǎn)是用來(lái)支持虛擬文件系統(tǒng)的(VFS)踏揣,能夠?yàn)椴煌奈募到y(tǒng)提供統(tǒng)一的API,v節(jié)點(diǎn)中保存了各種底層函數(shù)調(diào)用的函數(shù)指針匾乓;此外捞稿,linux上沒(méi)有特定的v節(jié)點(diǎn)的概念,但是本質(zhì)上都是一樣的拼缝,在linux下只有inode節(jié)點(diǎn)娱局,所以相應(yīng)的函數(shù)指針也都放在了i節(jié)點(diǎn)中
  • 兩個(gè)不同的文件描述符,指向同一個(gè)文件表項(xiàng)咧七,那么會(huì)共享同一個(gè)文件偏移量衰齐,這種情況通常是調(diào)用fork以后出現(xiàn);通過(guò)dup或者fcntl復(fù)制文件描述符可以使兩個(gè)fd指向相同的文件表項(xiàng)继阻;通過(guò)兩次open調(diào)用會(huì)使得兩個(gè)不同的文件描述符指向不同的文件表項(xiàng)耻涛,但文件表項(xiàng)中的inode是相同的
  • 在打開文件時(shí)指定O_NONBLOCK或者調(diào)用fcntl設(shè)置非阻塞后,會(huì)出現(xiàn)以下兩類情況
1. 若open調(diào)用未能立即打開文件瘟檩,則返回錯(cuò)誤而非陷入阻塞抹缕;但對(duì)于FIFO文件例外
2. 調(diào)用open成功后,對(duì)于后續(xù)的IO操作也都是非阻塞的墨辛,若IO操作不能立即完成卓研,只能傳輸部分?jǐn)?shù)據(jù),調(diào)用失敗睹簇,返回EAGAIN或者EWOULDBLCOK錯(cuò)誤奏赘,兩者等價(jià)
open
  • open函數(shù)調(diào)用的原型為int open(const char* pathname,int flags,.../*mode_t mode*/),如果pathname是一個(gè)符號(hào)鏈接(軟鏈接)太惠,那么會(huì)對(duì)其解引用志珍,即返回指向其實(shí)際文件的文件描述符。當(dāng)flags沒(méi)有指定O_CREAT標(biāo)志垛叨,則忽略mode參數(shù)。如果open調(diào)用成功,返回當(dāng)前進(jìn)程文件描述符表中最小的未用的文件描述符嗽元;如果失敗敛纲,返回-1并設(shè)置errno
  • flags表示文件狀態(tài)標(biāo)志,常見的關(guān)于文件讀寫權(quán)限的O_RDONLY\O_WRONLY\O_RDWR(三者互斥)
  • O_ASYNC標(biāo)志表示對(duì)于返回的fd所指向的文件上有IO操作時(shí)剂癌,系統(tǒng)會(huì)產(chǎn)生一個(gè)信號(hào)通知應(yīng)用程序淤翔,這僅對(duì)一些特殊的文件有效果,比如FIFO文件佩谷,socket文件以及終端設(shè)備文件旁壮。另外在linux中,需要調(diào)用fcntl的F_SETFL來(lái)設(shè)置此標(biāo)志才起作用
  • O_NONBLOCK以非阻塞模式打開文件
  • O_TRUNC表示當(dāng)文件存在且為普通文件谐檀,將文件長(zhǎng)度截?cái)酁?
  • O_CLOEXEC為文件描述符啟用close_on_exec抡谐,即調(diào)用exec函數(shù)族時(shí),子進(jìn)程不繼承父進(jìn)程打開的文件描述符桐猬;并且調(diào)用open并設(shè)置此標(biāo)志為原子操作麦撵,另外可以調(diào)用fcntl的F_SETFD設(shè)置,但是此方法非線程安全
  • 當(dāng)需要判斷一個(gè)文件是否存在時(shí)溃肪,應(yīng)當(dāng)使用O_CREAT和O_EXCL標(biāo)志來(lái)一次性調(diào)用open來(lái)判斷(作為原子操作)
  • 當(dāng)有多個(gè)進(jìn)程向同一個(gè)文件尾部寫入數(shù)據(jù)時(shí)免胃,在沒(méi)有同步的情況下如果使用lseek加write的方式,會(huì)出現(xiàn)競(jìng)爭(zhēng)狀態(tài)進(jìn)而導(dǎo)致寫入錯(cuò)誤惫撰;正確的做法是使用O_APPEND標(biāo)志的open羔沙,然后再write
read
  • read函數(shù)原型為ssize_t read(int fd,void* buffer,size_t count),返回值為一個(gè)有符號(hào)整形數(shù)厨钻,正常讀返回讀到的字節(jié)數(shù)扼雏,如果讀到文件末尾返回0(EOF),如果調(diào)用出錯(cuò)返回-1莉撇;count表示最大讀取的字節(jié)數(shù)
  • 如果讀寫位置位于文件尾部或者讀取的文件為終端設(shè)備呢蛤,管道,socket和FIFO棍郎,會(huì)出現(xiàn)讀取的字節(jié)數(shù)小于請(qǐng)求字節(jié)數(shù)的情況其障;由于表示字符串終止的空字符的影響,緩沖區(qū)的大小至少要比最大讀取字節(jié)數(shù)多一個(gè)字節(jié)
write
  • write函數(shù)原型為ssize_t write(int fd,void* buffer,size_t count)write調(diào)用可能會(huì)出現(xiàn)實(shí)際寫入的字節(jié)小于指定的從buf寫入文件的字節(jié)數(shù)涂佃,對(duì)于磁盤文件來(lái)說(shuō)励翼,可能是因?yàn)榇疟P已經(jīng)滿了
  • write函數(shù)的參數(shù)與read基本一致,count表示要寫入的字節(jié)數(shù)辜荠,成功調(diào)用返回寫入的字節(jié)數(shù)汽抚,調(diào)用失敗返回-1
lseek
  • lseek函數(shù)原型為off_t lseek(int fd,off_t offset,int whence)
  • offset表示偏移量,是一個(gè)有符號(hào)整數(shù)伯病;分別表示正/負(fù)偏移
  • whence表示偏移地址造烁,當(dāng)設(shè)置為SEEK_SET時(shí),偏移量必須為正數(shù)
  • lseek只是修改了fd對(duì)應(yīng)文件表項(xiàng)中的文件偏移量的值,并沒(méi)有去訪問(wèn)物理設(shè)備
  • 不允許將lseek應(yīng)用于管道惭蟋,socket苗桂,終端以及FIFO,如果這樣調(diào)用了告组,調(diào)用將失敗并將errno置為ESPIPE
  • 如果文件的文件偏移量已經(jīng)超過(guò)了文件末尾煤伟,那么內(nèi)核將以空字節(jié)去填充,不占用實(shí)際磁盤空間木缝,這稱為文件空洞便锨。直到向文件空洞中去寫入數(shù)據(jù),內(nèi)核才會(huì)真正去方位磁盤設(shè)備我碟,申請(qǐng)磁盤空間
fcntl
  • 函數(shù)原型為int fcntl(int fd,int cmd,......)
  • 通過(guò)將cmd設(shè)置為F_GETFL可以獲得fd所指向文件的文件狀態(tài)標(biāo)志放案,進(jìn)而可以通過(guò)將其和各種狀態(tài)標(biāo)志做&操作,判斷是否設(shè)置了該狀態(tài)標(biāo)志
  • 對(duì)于文件的訪問(wèn)權(quán)限來(lái)說(shuō)怎囚,在判斷的時(shí)候比較特殊卿叽;需要先將返回值與O_ACCMODE做&操作,然后才能進(jìn)行判斷恳守,如下所示
int flags = fcntl(fd,F_GETFL);
accessmode = flags & O_ACCMODE;
switch(accessmode)
{
    case O_WRONLY:{...}
    case O_RDONLY:{...}
    case O_RDWR:{...}
}
  • 相反考婴,可以使用F_SETFL來(lái)設(shè)置相應(yīng)fd的文件狀態(tài)標(biāo)志;一般催烘,都是先調(diào)用F_GETFL獲取舊 的標(biāo)志位沥阱,然后在舊的標(biāo)志位的基礎(chǔ)上對(duì)其進(jìn)行修改;可以修改的狀態(tài)標(biāo)志為O_APPEND\O_NONBLOCK\O_NOATIME\O_ASYNC\O_DIRECT伊群。不能通過(guò)F_SETFL修改文件的訪問(wèn)模式(O_RDONLY\O_WRONLY\O_RDWR)
  • 可以通過(guò)F_DUPFD復(fù)制文件描述符考杉,格式為newfd = fcntl(oldfd,F_DUPFD,startfd),其中新創(chuàng)建的文件描述符將使用大于startfd的最小的且未用的文件描述符舰始;如果想要為新fd設(shè)置close-on-exec標(biāo)志崇棠,就附加F_DUPFD_CLOEXEC命令
dup
  • dup函數(shù)族中一共有三個(gè)函數(shù),分別為dup,dup2,dup3丸卷。三個(gè)函數(shù)的原型如下
#include <unistd.h>

int dup(int oldfd);
int dup2(int oldfd,int newfd)
int dup3(int oldfd,int newfd,int flags)

return new fd on success,or -1 on error
  • dup僅僅復(fù)制一個(gè)已經(jīng)打開的文件描述符枕稀,并返回新的文件描述符;dup2創(chuàng)建的新的fd是由newfd參數(shù)所指定的谜嫉,如果newfd所指向的fd已經(jīng)打開萎坷,那么dup2首先會(huì)將其關(guān)閉然后再完成復(fù)制;
  • dup和dup2所創(chuàng)建的文件描述符的fd標(biāo)志(close-on-exec)都是處于關(guān)閉狀態(tài)沐兰,而dup3在dup2的基礎(chǔ)上可以控制該標(biāo)志的開關(guān)
pread和pwrite
#include <unistd.h>

ssize_t pread(int fd,void* buf,size_t count,off_t offset);
                                          return nums of bytes read,0 on EOF,or -1 on error
ssize_t pwrite(int fd,const void* buf,size_t count,off_t offset);
                                          return nums of bytes written,or -1 on error
  • 為了避免競(jìng)爭(zhēng)狀態(tài)的產(chǎn)生哆档,保證多進(jìn)程或者多線程之間能夠正確的在指定偏移處進(jìn)行IO操作,可以使用這一組系統(tǒng)調(diào)用住闯;在單線程環(huán)境下瓜浸,pread調(diào)用等同于下面的調(diào)用過(guò)程(pwrite同理)澳淑。先保存了當(dāng)前的文件偏移,然后對(duì)文件偏移進(jìn)行修改并在修改后的偏移處進(jìn)行read斟叼,最后讀取完以后將偏移恢復(fù)
off_t curoffset = lseek(fd,0,SEEK_CUR);
lseek(fd,offset,SEEK_SET);
read(fd,buf,count);
lseek(fd,curoffset,SEEK_SET);
readv和writev
  • readv和writev都具有原子性偶惠,不被其他執(zhí)行路徑干擾(進(jìn)程或線程)
#include <sys/uio.h>

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

  • 以上的系統(tǒng)調(diào)用可以一次投遞多塊緩沖區(qū)以供讀寫,iov是指向緩沖區(qū)結(jié)構(gòu)數(shù)組的指針朗涩,iovcnt是數(shù)組中的緩沖區(qū)個(gè)數(shù);struct iovec結(jié)構(gòu)是一個(gè)用來(lái)描述緩沖區(qū)的結(jié)構(gòu)體绑改,定義如下
struct iovec{
      void* iov_base;
      size_t iov_len;
};
  • readv實(shí)現(xiàn)了分散輸入的功能谢床,從fd指定的文件中連續(xù)讀取指定的字節(jié)數(shù),然后按照順序分別放入緩沖區(qū)數(shù)組中(從iov[0]開始)readv函數(shù)應(yīng)用示例如下:
  1 #include "../sysHeader.h"
  2 
  3 int main(void)
  4 {
  5     struct iovec iov[3];
  6 
  7     int fd = open("temp",O_RDWR);
  8 
  9     if(fd == -1)
 10     {
 11         perror("open");
 12         exit(1);
 13     }
 14 
 15     char v1[64];
 16     char v2[64];
 17     char v3[128];
 18 
 19     iov[0].iov_base = v1;
 20     iov[0].iov_len = 64;
 21 
 22     iov[1].iov_base = v2;
 23     iov[1].iov_len = 64;
 24 
 25     iov[2].iov_base = v3;
 26     iov[2].iov_len = 128;
 27 
 28     int totalreq = 256;
 29 
 30     int res = readv(fd,iov,3);
 31 
 32     if(res < totalreq)
 33         printf("read fewer bytes than requered\n");
 34     else
 35         printf("v1 = %s\nv2 = %s\nv3 = %s\n",v1,v2,v3);
 36 
 37     return 0;
 38 }
truncate和ftruncate
       #include <unistd.h>
       #include <sys/types.h>

       int truncate(const char *path, off_t length);
       int ftruncate(int fd, off_t length);
                          return 0 on success,or -1 on error
  • 若文件當(dāng)前長(zhǎng)度小于length厘线,則在文件尾部添加空字節(jié)以增長(zhǎng)到指定length识腿;若文件當(dāng)前長(zhǎng)度大于length,則將文件長(zhǎng)度減小至length

文件IO緩沖

  • 由于磁盤存取的速度比較緩慢造壮,所以read\write調(diào)用并不等待實(shí)際的磁盤存取操作渡讼。內(nèi)核為read/write準(zhǔn)備了緩存,每當(dāng)調(diào)用write寫數(shù)據(jù)時(shí)耳璧,實(shí)際上是往緩存中寫入成箫,直到緩存滿或者某個(gè)時(shí)機(jī),系統(tǒng)會(huì)自動(dòng)將緩存中的數(shù)據(jù)刷新到磁盤旨枯,這也減少了對(duì)磁盤的存取提高了效率蹬昌。如果還沒(méi)有將寫入的數(shù)據(jù)刷新到磁盤上,且這時(shí)候有另外一個(gè)進(jìn)程來(lái)讀取該數(shù)據(jù)攀隔,內(nèi)核會(huì)直接返回緩存中的數(shù)據(jù)皂贩,大大提高了效率;對(duì)于read調(diào)用也是類似的
  • 對(duì)于標(biāo)準(zhǔn)IO庫(kù)stdio來(lái)說(shuō)昆汹,里面的IO函數(shù)都是帶緩沖的明刷,可以使用setvbuf函數(shù)來(lái)控制stdio庫(kù)使用緩沖的方式,參數(shù)stream表示要修改的某個(gè)文件流满粗,mode指示了緩沖的類型辈末,分別有三種控制類型:_IONBF/_IOLBF/_IOFBF。_IONBF表示不緩沖败潦,相當(dāng)于直接調(diào)用write和read本冲;_IOLBF表示行緩沖,在遇到一個(gè)換行符之前緩沖數(shù)據(jù)劫扒,默認(rèn)終端設(shè)備采用行緩沖檬洞;_IOFBF表示全緩沖,以buf和size指定的緩沖區(qū)為緩沖對(duì)象沟饥;參數(shù)buf表示緩沖區(qū)首地址添怔,不能使用棧上的內(nèi)存湾戳,因?yàn)殡S著函數(shù)返回,內(nèi)存不可訪問(wèn)广料,size表示緩沖區(qū)大小砾脑。函數(shù)原型如下
       #include <stdio.h>

       int setvbuf(FILE *stream, char *buf, int mode, size_t size);
  • 無(wú)論使用何種緩沖方式,都可以使用fflush函數(shù)立即刷新stream流的緩沖區(qū)艾杏,如果stream為NULL則刷新所有的緩沖區(qū)韧衣,函數(shù)原型如下
       #include <stdio.h>

       int fflush(FILE *stream);
  • 對(duì)于內(nèi)核緩沖區(qū),我們也可以調(diào)用刷新函數(shù)來(lái)將緩沖區(qū)的內(nèi)容刷新到磁盤购桑,可以調(diào)用sync畅铭,fsync以及fdatasync三種刷新函數(shù);linux實(shí)現(xiàn)的sync會(huì)將所有內(nèi)核緩沖區(qū)中的數(shù)據(jù)排入寫隊(duì)列勃蜘,且等待實(shí)際的寫入完成才返回硕噩,而某些實(shí)現(xiàn)不等待IO完成就返回;fsync將與fd文件描述符相關(guān)的文件的所有信息都刷新到磁盤缭贡,并等待IO完成再返回炉擅;fdatasync只是將與文件描述符fd相關(guān)的文件的數(shù)據(jù)部分刷新到磁盤并等待完成后再返回;相應(yīng)的函數(shù)原型如下
       #include <unistd.h>

       int fsync(int fd)阳惹;
       int fdatasync(int fd);
       void sync(void);
  • 另外谍失,在調(diào)用open時(shí)如果指定O_SYNC標(biāo)志,會(huì)使得所有后續(xù)的輸出同步穆端,即等待數(shù)據(jù)(包括元數(shù)據(jù))寫入到磁盤上才返回
  • 文件描述符和標(biāo)準(zhǔn)庫(kù)的流對(duì)象之間可以互相轉(zhuǎn)換袱贮,函數(shù)原型如下
       #include <stdio.h>

       int fileno(FILE *stream);
       FILE *fdopen(int fd, const char *mode);

文件系統(tǒng)詳述

  • 在linux中,一切都是文件体啰,設(shè)備也是以文件的形式存在的攒巍;設(shè)備可分為字符設(shè)備文件和塊設(shè)備文件,兩者的區(qū)別就在于字符型設(shè)備基于每個(gè)字符來(lái)處理數(shù)據(jù)而塊設(shè)備按照一塊數(shù)據(jù)來(lái)處理數(shù)據(jù)荒勇。典型的柒莉,像鼠標(biāo)就是字符設(shè)備而磁盤就屬于塊設(shè)備。應(yīng)用程序?qū)τ诟黝愒O(shè)備的read/write調(diào)用實(shí)際上底層都是去調(diào)用了對(duì)應(yīng)設(shè)備的驅(qū)動(dòng)程序沽翔,最終完成實(shí)際的輸入輸出兢孝。
  • 以ext2文件系統(tǒng)為例,首先第一個(gè)部分是引導(dǎo)塊仅偎,包含了引導(dǎo)操作系統(tǒng)的信息跨蟹;然后剩余的空間被劃分為大小相等的塊組,每一個(gè)塊組中包含了超級(jí)塊橘沥,inode位圖窗轩,數(shù)據(jù)塊位圖,inode表以及數(shù)據(jù)塊表座咆;超級(jí)塊中記錄了當(dāng)前這個(gè)塊組中的inode剩余量以及數(shù)據(jù)塊的大小等信息痢艺;inode位圖用來(lái)記錄當(dāng)前塊組中inode節(jié)點(diǎn)的使用情況仓洼;數(shù)據(jù)塊位圖用來(lái)記錄數(shù)據(jù)塊的使用情況;而inode表和數(shù)據(jù)塊表就是實(shí)際存放文件數(shù)據(jù)的地方堤舒,inode用來(lái)存放文件的元數(shù)據(jù)(包括文件大小色建,訪問(wèn)時(shí)間等),數(shù)據(jù)塊用來(lái)存放文件的真正數(shù)據(jù)
  • 上面說(shuō)過(guò)inode節(jié)點(diǎn)中存放了文件的元數(shù)據(jù)舌缤,維護(hù)了以下一些信息
  • 文件類型
  • 文件大小
  • 文件所屬用戶及用戶組
  • 文件對(duì)應(yīng)屬組的權(quán)限位
  • 指向當(dāng)前文件的硬鏈接數(shù)
  • 數(shù)據(jù)塊指針箕戳,指向文件存放真正數(shù)據(jù)的數(shù)據(jù)塊
  • 三個(gè)時(shí)間信息,對(duì)文件的最后訪問(wèn)時(shí)間友驮,對(duì)文件的最后修改時(shí)間(對(duì)文件內(nèi)容的修改)以及對(duì)文件的元數(shù)據(jù)的最后修改時(shí)間
  • 該文件所占有的數(shù)據(jù)塊的數(shù)目
  • 對(duì)于inode節(jié)點(diǎn)中的數(shù)據(jù)塊指針又分為四種類型漂羊,直接指針,一級(jí)指針卸留,二級(jí)指針以及三級(jí)指針。在ext2中每個(gè)inode中包含15個(gè)與數(shù)據(jù)塊相關(guān)的指針椭豫,其中前12個(gè)都是直接指針耻瑟;而后面三個(gè)指針?lè)謩e是一級(jí),二級(jí)和三級(jí)指針赏酥。假設(shè)一個(gè)數(shù)據(jù)塊為4096字節(jié)喳整,那么對(duì)于文件大小小于12×4096字節(jié)的文件來(lái)說(shuō),只需要用到前12個(gè)指針裸扶,小文件的數(shù)據(jù)都直接放置于前12個(gè)指針指向的數(shù)據(jù)塊中框都;如果超過(guò)這個(gè)大小,就需要用到一級(jí)指針了呵晨,一級(jí)指針指向的數(shù)據(jù)塊中存放的不是數(shù)據(jù)魏保,而是指向其他數(shù)據(jù)塊的指針,而每個(gè)指針長(zhǎng)度為4字節(jié)摸屠,則一共可以存放1024個(gè)指針谓罗,則在只使用13個(gè)指針的前提下,文件的大小最大可以使(1024+12)×4096字節(jié)季二;二級(jí)指針和三級(jí)指針類似檩咱。
  • 為了達(dá)到為所有文件系統(tǒng)提供同樣的API的目的,linux在應(yīng)用程序和具體的文件系統(tǒng)之間提供了一層抽象層胯舷,也就是VFS(虛擬文件系統(tǒng))刻蚯;調(diào)用過(guò)程可能是這樣,應(yīng)用程序調(diào)用write桑嘶,內(nèi)核通過(guò)inode節(jié)點(diǎn)中的對(duì)應(yīng)的函數(shù)指針找到對(duì)應(yīng)文件系統(tǒng)的write調(diào)用炊汹,然后再去調(diào)用驅(qū)動(dòng)程序完成寫入

文件屬性

  • 利用以下三種系統(tǒng)調(diào)用可以獲取一個(gè)文件的元數(shù)據(jù),一般都是從文件的inode節(jié)點(diǎn)中獲取不翩。文件屬性保存于結(jié)構(gòu)體stat中兵扬,stat會(huì)返回指定路徑名的文件的文件屬性信息麻裳;fstat返回指定文件描述符fd的文件屬性;lstat與stat類似器钟,區(qū)別在于如果指定路徑是一個(gè)符號(hào)鏈接津坑,那么返回的文件屬性是與該符號(hào)鏈接相關(guān),也就是不追蹤傲霸,而stat會(huì)返回符號(hào)鏈接所指向的文件的信息疆瑰。調(diào)用lstat和stat的進(jìn)程不需要對(duì)指定的文件有任何權(quán)限,但是必須保證對(duì)pathname的父目錄有執(zhí)行權(quán)限昙啄;而fstat只需要提供一個(gè)有效的fd就可以穆役。相關(guān)函數(shù)原型和結(jié)構(gòu)體如下
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>

       struct stat {
               dev_t     st_dev;         /* ID of device containing file */
               ino_t     st_ino;         /* Inode number */
               mode_t    st_mode;        /* File type and mode */
               nlink_t   st_nlink;       /* Number of hard links */
               uid_t     st_uid;         /* User ID of owner */
               gid_t     st_gid;         /* Group ID of owner */
               dev_t     st_rdev;        /* Device ID (if special file) */
               off_t     st_size;        /* Total size, in bytes */
               blksize_t st_blksize;     /* Block size for filesystem I/O */
               blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

               /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* Time of last access */
               struct timespec st_mtim;  /* Time of last modification */
               struct timespec st_ctim;  /* Time of last status change */

           #define st_atime st_atim.tv_sec      /* Backward compatibility */
           #define st_mtime st_mtim.tv_sec
           #define st_ctime st_ctim.tv_sec
           };

       int stat(const char *pathname, struct stat *statbuf);
       int fstat(int fd, struct stat *statbuf);
       int lstat(const char *pathname, struct stat *statbuf);
  • 對(duì)于返回的stat結(jié)構(gòu)體的部分成員進(jìn)行說(shuō)明。st_ino表示該文件對(duì)應(yīng)的inode編號(hào)梳凛;st_mode是一個(gè)16位長(zhǎng)的字段耿币,高4位表示文件類型,低12位表示文件權(quán)限韧拒⊙徒樱可以利用系統(tǒng)提供的宏來(lái)判斷文件類型,例如S_ISREG()測(cè)試該文件是否是常規(guī)文件叛溢,具體參考man手冊(cè)塑悼;st_nlink表示硬鏈接數(shù);st_size表示文件大锌簟厢蒜;st_blocks表示分配給該文件的數(shù)據(jù)塊數(shù)目;st_blksize表示在該文件上進(jìn)行IO操作時(shí)所采用的最優(yōu)緩沖區(qū)大小
  • 每一個(gè)文件都有一個(gè)相關(guān)聯(lián)的用戶和組ID烹植,分別用st_uid和st_gid表示斑鸦。在文件創(chuàng)建時(shí),用戶和組ID分別取自進(jìn)程的有效用戶和組ID
  • 對(duì)于文件的權(quán)限位來(lái)說(shuō)刊橘,可以用st_mode和各種權(quán)限位相&的方式來(lái)判斷鄙才,具體見man手冊(cè);另外促绵,可以通過(guò)filePermStr調(diào)用將st_mode轉(zhuǎn)換為一個(gè)形如"rwxr-xr--"的字符串攒庵,flags參數(shù)指定為FP_SPECIAL,原型為char* filePermStr(mode_t perm,int flags)败晴,位于sys/types.h頭文件中
  • 對(duì)于目錄來(lái)說(shuō)浓冒,讀寫執(zhí)行權(quán)限有不同的意義。目錄具有讀權(quán)限表示只能查看目錄下的文件名尖坤;具有寫權(quán)限可以對(duì)目錄下的文件進(jìn)行修改或者增刪文件稳懒;具有執(zhí)行權(quán)限可以進(jìn)入該目錄
  • 當(dāng)進(jìn)程訪問(wèn)一個(gè)文件時(shí),會(huì)依次根據(jù)進(jìn)程的有效用戶ID慢味,有效組ID以及附屬組ID來(lái)分別檢查對(duì)于該文件的權(quán)限场梆;
  • 調(diào)用chown可以改變一個(gè)文件的屬主和屬組墅冷。只有對(duì)于特權(quán)級(jí)進(jìn)程來(lái)說(shuō),才能更改文件的屬主或油;非特權(quán)級(jí)進(jìn)程只有在進(jìn)程的有效用戶ID和文件當(dāng)前屬主一致的情況下才能改變文件的屬主寞忿。函數(shù)原型為int chown(const char* pathname,uid_t owner,gid_t group)
  • 若想要更改文件的權(quán)限位,一般先調(diào)用stat獲取文件的原權(quán)限顶岸,然后在此基礎(chǔ)上進(jìn)行修改腔彰,最后調(diào)用chmod。同樣的辖佣,對(duì)于chmod來(lái)說(shuō)有兩種情況霹抛。當(dāng)調(diào)用進(jìn)程是特權(quán)級(jí)進(jìn)程,可以修改文件的權(quán)限位卷谈;當(dāng)調(diào)用進(jìn)程是非特權(quán)進(jìn)程杯拐,其有效用戶ID必須和文件屬主一致。函數(shù)原型為int chmod(const char* pathname,mode_t mode)
  • 注意到文件名并沒(méi)有包括在inode節(jié)點(diǎn)的文件屬性中世蔗。文件名是存放在目錄下的藕施,對(duì)于一個(gè)目錄來(lái)說(shuō),它的inode的文件類型表示一個(gè)目錄凸郑,并且inode中數(shù)據(jù)塊指針?biāo)赶虻臄?shù)據(jù)塊中存放的是關(guān)于文件名和inode的映射。有可能存在多個(gè)不同的文件名指向相同的一個(gè)inode矛市,這就是一個(gè)硬鏈接芙沥。如果一個(gè)inode節(jié)點(diǎn)的硬鏈接計(jì)數(shù)為0,那么該文件就會(huì)被刪除浊吏,即釋放相關(guān)inode和數(shù)據(jù)塊
  • 硬鏈接存在兩個(gè)問(wèn)題而昨,其一是只能應(yīng)用于同一個(gè)文件系統(tǒng)下,因?yàn)閕node編號(hào)的唯一性不能跨文件系統(tǒng)找田;其二是不能為目錄創(chuàng)建硬鏈接歌憨,避免出現(xiàn)鏈接環(huán)路
  • 為解決上述問(wèn)題,就要用到符號(hào)鏈接墩衙。建立一個(gè)符號(hào)鏈接相當(dāng)于建立一個(gè)新的文件务嫡,也就新建了一個(gè)inode節(jié)點(diǎn),符號(hào)鏈接的文件數(shù)據(jù)只包括所引用文件的絕對(duì)路徑漆改,當(dāng)系統(tǒng)發(fā)現(xiàn)這是一個(gè)符號(hào)鏈接時(shí)心铃,會(huì)自動(dòng)解引用找到真正的文件
  • 有關(guān)硬鏈接的函數(shù)原型如下,link和unlink分別是創(chuàng)建和刪除一個(gè)硬鏈接挫剑,需要注意的是link和unlink不會(huì)對(duì)pathname解引用去扣,即如果oldpath或者pathname是符號(hào)鏈接,那么只會(huì)針對(duì)該符號(hào)鏈接去新建或者刪除硬鏈接樊破,而不涉及引用的實(shí)際文件
       #include <unistd.h>

       int link(const char *oldpath, const char *newpath);
       int unlink(const char *pathname);
  • rename調(diào)用同shell命令mv愉棱,可以移動(dòng)或者重命名一個(gè)文件唆铐,原型為int rename(const char* oldpath,const char* newpath)
  • symlink調(diào)用用于創(chuàng)建一個(gè)符號(hào)鏈接,如果target所代表的文件不存在奔滑,那么linkpath成為懸空鏈接艾岂,原型為int symlink(const char *target, const char *linkpath);另外,解除一個(gè)符號(hào)鏈接也是調(diào)用unlink函數(shù)档押。如果用open去打開一個(gè)符號(hào)鏈接膜赃,系統(tǒng)會(huì)為其解引用募舟,即打開symbol link所引用的文件。所以如果想要查看符號(hào)鏈接本身的內(nèi)容,那么就使用readlink函數(shù)言询,buffer是保存內(nèi)容的緩沖區(qū),bufsiz表示緩沖區(qū)大小揩抡,pathname表示符號(hào)鏈接路徑藻懒,原型為ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);需要注意的是,這個(gè)函數(shù)的返回值為放入buf中的字節(jié)數(shù)癞松,并且buf中不會(huì)包括空字符爽撒,所以一般會(huì)分配一個(gè)長(zhǎng)度為路徑最大長(zhǎng)度的緩沖區(qū),示例如下(解析一個(gè)符號(hào)鏈接的內(nèi)容)
  1 #include <unistd.h>
  2 #include <stdio.h>
  3 #include <sys/stat.h>
  4 #include <limits.h>
  5 #include <stdlib.h>
  6 
  7 #define BUFFSIZE PATH_MAX
  8 
  9 int main(void)
 10 {
 11     char buf[BUFFSIZE];
 12     char* path = "lntag";
 13     struct stat st;
 14 
 15     int res = lstat(path,&st);
 16     if(res == -1)
 17     {
 18         perror("stat");
 19         exit(1);
 20     }
 21 
 22     if(!S_ISLNK(st.st_mode))
 23     {
 24         printf("lntag is not a slink\n");
 25         exit(1);
 26     }
 27 
 28     int nums = readlink(path,buf,BUFFSIZE - 1);
 29     buf[nums] = '\0';
 30     printf("lntag:%s\n",buf);
 31     
 32     return 0;
 33 }
  • 對(duì)于目錄的訪問(wèn)不能使用open函數(shù)响蓉,調(diào)用open會(huì)出錯(cuò)硕勿;對(duì)于目錄的訪問(wèn)需要另外一組API函數(shù),首先需要使用opendir或者fdopendir獲得對(duì)目錄的一個(gè)指針枫甲,該指針是DIR類型的源武。然后循環(huán)調(diào)用readdir,傳入目錄指針想幻,該函數(shù)的返回值是一個(gè)指向dirent結(jié)構(gòu)體的指針粱栖,這個(gè)結(jié)構(gòu)體相當(dāng)于一條目錄項(xiàng),存放文件的inode和文件的名字以及其他信息脏毯;并且每調(diào)用一次readdir闹究,就會(huì)自動(dòng)從指定的目錄指針中獲取下一條目錄項(xiàng),直到返回NULL表示遍歷目錄結(jié)束食店。還可以調(diào)用rewinddir函數(shù)將目錄指針重新指向目錄初始處渣淤,另外遍歷結(jié)束后應(yīng)調(diào)用closedir釋放資源。相關(guān)函數(shù)原型及結(jié)構(gòu)體如下
       #include <dirent.h>
       #include <sys/types.h>

       struct dirent {
               ino_t          d_ino;       /* Inode number */
               off_t          d_off;       /* Not an offset; see below */
               unsigned short d_reclen;    /* Length of this record */
               unsigned char  d_type;      /* Type of file; not supported
                                              by all filesystem types */
               char           d_name[256]; /* Null-terminated filename */
           };

       DIR *opendir(const char *name);
       DIR *fdopendir(int fd);
       struct dirent *readdir(DIR *dirp);
       void rewinddir(DIR* dirp);
       int closedir(DIR* dirp);
  • 可以通過(guò)dirfd函數(shù)獲得對(duì)應(yīng)于目錄流的文件描述符叛买,原型為int dirfd(DIR* dirp)
  • 關(guān)于進(jìn)程的當(dāng)前工作目錄砂代,可以調(diào)用getcwd函數(shù)來(lái)獲取,該函數(shù)將當(dāng)前工作目錄字符串存放于cwdbuf數(shù)組中率挣,size為函數(shù)寫入cwdbuf數(shù)組的最大長(zhǎng)度刻伊,如果調(diào)用成功返回一個(gè)指向cwdbuf的指針;另外可以調(diào)用chdir和fchdir來(lái)改變當(dāng)前工作目錄,相關(guān)函數(shù)原型如下
       #include <unistd.h>

       int chdir(const char *path);
       int fchdir(int fd);
       char *getcwd(char *buf, size_t size);
  • dirname和basename函數(shù)分別獲取對(duì)于pathname的目錄和文件名捶箱,例如對(duì)于路徑"/home/pty/file.c"智什,dirname返回"/home/pty",basename返回"pty"丁屎,函數(shù)原型如下
#include <libgen.h>

char* dirname(char* pathname);
char* basename(char* pathname);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末荠锭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子晨川,更是在濱河造成了極大的恐慌证九,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件共虑,死亡現(xiàn)場(chǎng)離奇詭異愧怜,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)妈拌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門拥坛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人尘分,你說(shuō)我怎么就攤上這事猜惋。” “怎么了培愁?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵著摔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我定续,道長(zhǎng)梨撞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任香罐,我火速辦了婚禮,結(jié)果婚禮上时肿,老公的妹妹穿的比我還像新娘庇茫。我一直安慰自己,他們只是感情好螃成,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布旦签。 她就那樣靜靜地躺著,像睡著了一般寸宏。 火紅的嫁衣襯著肌膚如雪宁炫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天氮凝,我揣著相機(jī)與錄音羔巢,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛竿秆,可吹牛的內(nèi)容都是我干的启摄。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼幽钢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼歉备!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起匪燕,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蕾羊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后帽驯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體龟再,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年界拦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吸申。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡享甸,死狀恐怖截碴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛉威,我是刑警寧澤日丹,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站蚯嫌,受9級(jí)特大地震影響哲虾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜择示,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一束凑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧栅盲,春花似錦汪诉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至拟烫,卻和暖如春该编,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背硕淑。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工课竣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘉赎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓稠氮,卻偏偏與公主長(zhǎng)得像曹阔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子隔披,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 1.1 C標(biāo)準(zhǔn)函數(shù)與系統(tǒng)函數(shù) C標(biāo)準(zhǔn)是工作在操作系統(tǒng)之上的赃份。比如要執(zhí)行C標(biāo)準(zhǔn)函數(shù)printf函數(shù),printf會(huì)調(diào)...
    FlyingReganMian閱讀 985評(píng)論 0 0
  • 實(shí)驗(yàn)環(huán)境介紹 gcc:4.8.5 glibc:glibc-2.17-222.el7.x86_64 os:Cento...
    alex_man閱讀 423評(píng)論 0 2
  • I/O函數(shù)就是打開文件奢米,讀文件抓韩,寫文件,在絕大數(shù)unix系統(tǒng)中只需用到5個(gè)函數(shù)open鬓长、read谒拴、write、ls...
    laidian閱讀 337評(píng)論 0 0
  • 在該章節(jié)中討論的文件描述符的概念涉波。其中包括:打開文件英上,關(guān)閉文件,從文件中讀取數(shù)據(jù)和向文件中寫數(shù)據(jù)啤覆。 概述所有執(zhí)行I...
    Capr1corn閱讀 755評(píng)論 0 0
  • 最近在讀unix環(huán)境高級(jí)編程苍日,每天都會(huì)做做筆記! UNIX標(biāo)準(zhǔn)及實(shí)現(xiàn) UNIX編程環(huán)境的標(biāo)準(zhǔn)化已經(jīng)取得了很大進(jìn)展窗声。...
    MrTrans閱讀 431評(píng)論 0 1