文件操作
(Linux文件操作)) [文件|目錄]
Linux文件操作:為了對文件和目錄進程處理,你需要用到系統(tǒng)調(diào)用(這是 Unix和Linux中與WindowsAPI對應(yīng)的概念),但系統(tǒng)中同時還存在一套庫函數(shù)--標(biāo)準(zhǔn)IO(stdio),可以更有效地進行文件處理。
- 文件和設(shè)備
- 系統(tǒng)調(diào)用
- 庫函數(shù)
- 底層文件訪問
- 管理文件
- 標(biāo)準(zhǔn)I/O庫
- 格式化輸入和輸出
- 文件和目錄的維護
- 掃描目錄
- 錯誤及其處理
- /proc 文件系統(tǒng)
- 高級主題:fcntl和mmap
3.1:Linux 文件結(jié)構(gòu)
在Linux中,一切(或者幾乎一切)都是文件,這就意味著,通常程序完全可以像使用文件那樣使用磁盤文件、串行口、打印機、和其他設(shè)備。
3.1.1 :目錄
文件,除了本身包含內(nèi)容以外,他還會有一個名字和一些屬性,即“管理信息”,它包括文件的創(chuàng)建/修改日期和它的訪問權(quán)限。這些屬性被保存在文件的inode(節(jié)點)中,他是文件系統(tǒng)中的一個特殊的數(shù)據(jù)塊,他同時還包含文件的長度和文件在磁盤上的存放位置。系統(tǒng)使用的是文件的inode編號,目錄結(jié)構(gòu)為文件命名僅僅是為了便于人們使用。
目錄是用于保存其他文件的節(jié)點號和名字的文件。目錄文件中的每個數(shù)據(jù)項都指向某個文件節(jié)點鏈接,刪除文件名就等于刪除與之對應(yīng)的連接(文件的節(jié)點號可以通過ln-i命令查看)。你可以通過使用ln命令在不同的目錄中創(chuàng)建指向同一個文件的鏈接。
刪除一個文件時,實質(zhì)上是刪除了該文件對應(yīng)的目錄項,同時指向該文件的鏈接數(shù)減1.
3.1.2 :文件和設(shè)備
UNIX和Linux中比較重要的設(shè)備文件有3個:/dev/console 、/dev/tty和/dev/null
- 1./dev/console :這個設(shè)備代表的是系統(tǒng)控制臺[錯誤信息和診斷信息通常會發(fā)送到這個設(shè)備]
- 2./dev/tty :如果一個進程有控制終端的話,那么特殊文件/dev/tty就是這個控制端(鍵盤和顯示屏,或鍵盤和窗口)的別名(邏輯設(shè)備)。
- 3./dev/null :/dev/null文件是空(null)設(shè)備。
創(chuàng)建空文件的另外一個方法是使用touch <fileName>命令,該命令的作用是改變文件的修改時間,如果指定的文件不存在,就創(chuàng)建它,但該命令并不會把所有的內(nèi)容的文件變成空文件
/dev目錄中的其他設(shè)備包括:硬盤和軟盤、通信端口、磁帶驅(qū)動器、CD-ROM、聲卡以及一些代表系統(tǒng)內(nèi)部工作狀態(tài)的設(shè)備。
3.2 系統(tǒng)調(diào)用和設(shè)備驅(qū)動程序
操作系統(tǒng)的核心部分,即內(nèi)核,是一組設(shè)備驅(qū)動程序。它們是一組對系統(tǒng)硬件進行控制的底層接口。
下面是用于訪問設(shè)備驅(qū)動程序的底層函數(shù)(系統(tǒng)調(diào)用)。
- open:打開文件或設(shè)備
- read:從打開的文件或設(shè)備里讀取數(shù)據(jù)
- write:向文件或設(shè)備寫數(shù)據(jù)
- close:關(guān)閉文件或設(shè)備。
- ioctl:把控制信息傳遞給設(shè)備驅(qū)動程序。
此外,每個驅(qū)動程序都定義了它自己的一組ioctl命令
3.3 庫函數(shù)
使用系統(tǒng)調(diào)用會影響系統(tǒng)的性能。
硬件會限制對底層系統(tǒng)調(diào)用一次所讀寫的數(shù)據(jù)塊大小。
3.4 底層文件訪問
每個運行中的程序稱為進程(process),它有一些與之關(guān)聯(lián)的文件描述符。
- 0 :標(biāo)準(zhǔn)輸入
- 1 :標(biāo)準(zhǔn)輸出
- 2 :標(biāo)準(zhǔn)錯誤
3.4.1 write系統(tǒng)調(diào)用
系統(tǒng)調(diào)用write的作用是吧緩沖區(qū)的buf的前nbytes個字節(jié)寫入與文件描述符fileds關(guān)聯(lián)的文件中。它返回實際寫入的字節(jié)數(shù)。
write系統(tǒng)調(diào)用原型:
#include <unistd.h>
size_t write(int filds,const void *buf,size_t nbytes);
3.4.2 read系統(tǒng)調(diào)用
系統(tǒng)調(diào)用read的作用是:從與文件描述符fildes相關(guān)的文件里讀入nbytes個字節(jié)的數(shù)據(jù),并把它們放到數(shù)據(jù)區(qū)buf中。它返回實際讀入的字節(jié)數(shù),這可能會小于請求的字節(jié)數(shù)。
#include <unistd.h>
size_t read(int fildes,void *buf,size_t nbytes);
3.4.3 open系統(tǒng)調(diào)用
為了創(chuàng)建一個新的描述符,你需要使用系統(tǒng)調(diào)用open
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int open(const char *path,int oflags);
int open(const char *path,int oflags,mode_t mode);
嚴(yán)格來說,在遵循POSIX規(guī)范系統(tǒng)上,使用open系統(tǒng)調(diào)用并不需要包括頭文件sys/types.h和sys/stat.h,但在某些unix系統(tǒng)上,它們可能是必不可少的。
open 建立一條文件和設(shè)備的訪問路徑。成功則返回一個唯一描述符,供read 和 write調(diào)用,文件描述符石不能進行進程共享。
oflags : 模式
模式 | 說明 |
---|---|
O_RDONLY | 以只讀方式打開 |
O_WRONLY | 以只寫的方式打開 |
O_RDWR | 以讀寫的方式打開 |
open調(diào)用還可以在oflags參數(shù)中包括下列可選模式的組合(按位或)
- O_APPEND :把寫入數(shù)據(jù)追加在文件的末尾
- O_TRUNC: 把文件長度設(shè)置為零,丟棄已有的內(nèi)容
- O_CREAT:如果需要,就按參數(shù)mode中給出的訪問模式創(chuàng)建文件
- O_EXCL:與O_CREAT一起使用,確保調(diào)用者創(chuàng)建出文件。open調(diào)用是一個原子操作,也就是說,它只執(zhí)行一個函數(shù)調(diào)用。使用這個可選模式可以防止兩個程序同時創(chuàng)建同一個文件。如果文件已經(jīng)存在,open 調(diào)用將失敗。
POSIX 規(guī)范還標(biāo)準(zhǔn)化了一個creat調(diào)用,但它并不常用。
3.4.4 訪問權(quán)限的初始值
當(dāng)你使用帶有O_CREAT 標(biāo)志的open 調(diào)用來創(chuàng)建文件時,你必須使用有3個參數(shù)格式的open調(diào)用。第三個參數(shù)mode 時幾個標(biāo)志按位或后得到的,這些標(biāo)志在頭文件sys/stat.h中定義。
- S_IRUSR :讀權(quán)限,文件屬主。
- S_IWUSR :寫權(quán)限,文件屬主。
- S_IXUSR :執(zhí)行權(quán)限,文件屬主
- S_IRGRP :讀權(quán)限,文件所屬組
- S_IWGRP :寫權(quán)限,文件所屬組
- S_IXGRP :執(zhí)行權(quán)限,文件所屬組
- S_IROTH :讀權(quán)限,其他用戶
- S_IWOTH :寫權(quán)限,其他用戶
- S_IXOTH :執(zhí)行權(quán)限,其他用戶
請看下面的例子:
open ("myfile",OCREAT,S_IRUSR|S_IXOTH);
1.umask
umask 是一個系統(tǒng)變量,他的作用是,當(dāng)文件被創(chuàng)建時,為文件的訪問權(quán)限設(shè)定一個掩碼。執(zhí)行umask命令可以修改這個變量的值。
2.close 系統(tǒng)調(diào)用
你可以使用close調(diào)用終止文件描述符fildes與其對應(yīng)文件之間的關(guān)聯(lián)。文件描述符被釋放并能夠重新使用。close 調(diào)用成功時返回0,出錯時返回-1
注意,檢查close 調(diào)用的返回結(jié)果非常重要。有的文件系統(tǒng),特別是網(wǎng)絡(luò)文件系統(tǒng)可能不會再關(guān)閉文件之前報告文件寫操作中的錯誤,這是因為在執(zhí)行寫操作時,數(shù)據(jù)可能未被確認(rèn)寫入。
3.ioctl系統(tǒng)調(diào)用
ioctl 調(diào)用有點像是個大雜燴。它提供了一個用于控制設(shè)備及其描述符行為和配置底層服務(wù)接口。
#include unistd.h
int ioctl(int fildes,int cmd,...)
--》程序塊一
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
define IN_FILE "in_file.txt"
define OUT_FILE "out_file.txt"
int main(void) {
char buf[1024];
char c;
int in ,out;
int nread;
in = open(IN_FILE,O_RDONLY);
out = open(OUT_FILE,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
while(nread = read(in,buf,sizeof(buf)) > 0)
write(out,buf,1);
return EXIT_SUCCESS;
}
3.4.5 其他與文件管理有關(guān)的系統(tǒng)調(diào)用
1:lseek 系統(tǒng)調(diào)用
lseek 系統(tǒng)調(diào)用對文件描述符fildes的讀寫指針進行設(shè)置。也就是說,你可以用它來設(shè)置文件的下一個讀寫位置。讀寫指針既可被設(shè)置為文件中的某個絕對位置,也可以把它設(shè)置為相對于當(dāng)前位置或文件尾的某個相對位置
#include <unistd.h>
#include <sys/types.h>
off_t lseek(int fildes,off_t offset, int whence);
off_set參數(shù)用來指定位置,而whence參數(shù)定義該偏移植的用法。whence可以取值之一:
- SEEK_SET:offset 是一個絕對位置。
- SEEK_CUR:offset 是相對于當(dāng)前位置的一個相對位置
- SEEK_END:offset 是相對于文件尾的一個相對位置
lseek 返回從文件頭到文件指針被設(shè)置處的字節(jié)偏移值,失敗時,返回1
2:fstat、stat和lstat系統(tǒng)調(diào)用
fstat 系統(tǒng)調(diào)用返回與打開文件描述符的相關(guān)的文件狀態(tài)信息,該信息將會寫到一個buf結(jié)構(gòu)中,buf的地址一參數(shù)形式傳遞給fstat。
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int fstat(int fildes,struct stat *buf)
int stat(const char *path,struct stat *buf)
int lstat(const char *path,struct stat *buf)
0)
3:dup和dup2系統(tǒng)調(diào)用[可用于斷點續(xù)傳]
dup系統(tǒng)調(diào)用提供了一種復(fù)制文件描述符的方法,使我們能夠通過兩個或者更多個不同的描述符訪問同一個文件。這可以用于在文件的不同位置對數(shù)據(jù)進行讀寫。dup系統(tǒng)調(diào)用復(fù)制文件描述符fileds,返回一個新的描述符。dup2系統(tǒng)調(diào)用則是通過明確指向目標(biāo)描述符來吧一個文件描述符復(fù)制為另外一個。
#include <unistd.h>
int dup(int fildes);
int dup2(int fildes,int fildes2);
當(dāng)你通過管道多個進程間進行通信時,這些調(diào)用也很有用。
3.5 標(biāo)準(zhǔn)I/O 庫
標(biāo)準(zhǔn)I/O庫(stdio)即其頭文件stdio.h 為底層I/O系統(tǒng)調(diào)用提供了一個通用的接口。
在標(biāo)準(zhǔn)I/O庫中,與底層文件描述符對應(yīng)的是流(stream),它被實現(xiàn)為指向結(jié)構(gòu)FILE的指針。
標(biāo)準(zhǔn)I/O庫中的下列庫函數(shù):
- fopen 、fclose
- fread 、fwrite
- fflush-
- fseek-
- fgetc、getc、getchar
- fputc、putc、putchar
- fgets、gets
- printf、fprintf和sprintf
- scanf、fscanf和sscanf
3.5.1 fopen 函數(shù)
fopen 庫函數(shù)類似于底層的open系統(tǒng)調(diào)用。它主要用于文件和終端的輸入輸出。
該函數(shù)原型如下:
#include <stdio.h>
FILE *fopen(const char *filename,const char *mode)
fopen 打開由filename參數(shù)指定的文件,并把它與一個文件流關(guān)聯(lián)起來,mode參數(shù)指定文件的打開方式,它取下列字符串中的值。
- "r" 和 “rb”:以只讀方式打開
- "w" 和 “wb”:以寫方式打開,并把文件長度截短為0
- "a" 和 “ab”:以寫方式打開,新內(nèi)容追加在文件尾
- "r+" 和 “rb+”或"r+b":以更新方式打開(讀和寫)
- "w+" 和 “wb+”或"w+b":以更新方式打開,并把文件長度截短為零
- "a+" 和 “ab+”或"a+b":以更新方式打開,新內(nèi)容追加在文件尾
字母b表示文件是一個二機制文件而不是文本文件。
fopen 在成功時返回一個非空的FILE *指針,失敗時返回NULL值,NULL值在頭文件stdio.h里定義。
3.5.2 fread 函數(shù)
fread 庫函數(shù)用于從一個文件流里讀取數(shù)據(jù)。數(shù)據(jù)從文件流stream讀到ptr指向的數(shù)據(jù)緩沖區(qū)里。
#include <stdio.h>
size_t fread(
void *ptr//
,size_t size//指定每個數(shù)據(jù)記錄的長度
,size_t nitems// 給出要傳輸?shù)挠涗泜€數(shù)
,FILE * stream
);
對所有向緩沖區(qū)里寫數(shù)據(jù)的標(biāo)準(zhǔn)I/O 函數(shù)來說,為數(shù)據(jù)分配空間和檢查錯誤是程序員的責(zé)任。
3.5.3 fwrite 函數(shù)
fwrite庫函數(shù)與fread相似接口。從指定的數(shù)據(jù)緩沖區(qū)里取出數(shù)據(jù)記錄,并把它們寫出輸出流中。它的返回值是成功寫入的記錄個數(shù)。
#include <stdio.h>
size_t fwrite(const void *ptr,size_t size,size_t nitems,FILE *stream);
請注意,我們不推薦把fread 和fwrite用于結(jié)構(gòu)化數(shù)據(jù)。部分原因在于用fwrite寫的文件在不同的計算機體現(xiàn)結(jié)構(gòu)之間可能不具備可移植性
3.5.4 fclose函數(shù)
fclose 庫函數(shù)關(guān)閉指定的文件stream ,使所有尚未寫出數(shù)據(jù)都寫出。
因為stdio 庫會對數(shù)據(jù)進行緩沖,所以使用fcolse是很重要的。
#include stdio.h
int fclose(FILE *stream);
3.5.5 fflush 函數(shù)
fflush 庫函數(shù)的作用是把文件流離的所有未寫出數(shù)據(jù)立刻寫出。
注意,調(diào)用fclose函數(shù)隱含執(zhí)行了i 次flush操作,所以你不必再調(diào)用fclose之前調(diào)用fflush
#include stdio.h
int fflush(FILE *stream);
3.5.6 fseek函數(shù)
fseek 函數(shù)是與lseek 系統(tǒng)調(diào)用對應(yīng)的文件流函數(shù)。它在文件流里為下一次讀寫操作指定位置。
fseek函數(shù) 返回一個整數(shù):0表示成功,-1表示失敗并設(shè)置errno 指出錯誤。
#include stdio.h
int fseek(FILE *stream,long int offset,int whence)
3.5.7 fgetc 、getc 和 getchar函數(shù)
fgetc 函數(shù)從文件流里取出下個字節(jié)并把他作為一個字符返回,當(dāng)他到達文件尾或出現(xiàn)錯誤時,它返回EOF。你必須通過ferror或 feof 來區(qū)分者兩種情況。
#include stdio.h
int fgetc(FILE *stream)
int getc(FILE *stream)
int getchar();
3.5.8 fputc、putc 和 putchar函數(shù)
fputc 函數(shù)把一個字符寫到一個輸出文件流中。它返回寫入的值,如果失敗,則返回EOF。
#include stdio.h
int fputc(int c,FILE *stream)
int fputc(int c,FILE *stream)
int putchar(int c);
3.5.9 fgets 和 gets 函數(shù)
fgets函數(shù)從輸入文件流stream里讀取一個字符串。
#include stdio.h
char *fgets(char *s ,int n ,FILE *stream)
char *gets(char *s);
fget把讀到字符寫到s執(zhí)行的字符串里,知道出現(xiàn)下面某種情況:遇到換行符,已經(jīng)傳輸了n-1個字符或者是達到文件尾。
當(dāng)成功完成時,fgets返回一個指向字符串s的指針。如果文件流已經(jīng)到到達文件尾,fgets會設(shè)置這個文件EOF標(biāo)識并返回一個空指針。如果出現(xiàn)讀錯誤,fgets返回一個空指針并設(shè)置errno以指出錯誤類型。
3.6 格式化輸入輸出
3.6.1:printf、fprintf和sprintf函數(shù)
printf 系列函數(shù)能夠?qū)Ω鞣N不同類型的參數(shù)進程格式編排和輸出
#include <stdio.h>
int printf(const char *format,...);
int sprintf(char *s ,const char *format,...);
int fprintf(FILE *stream,const char *format,...);
說明:
- printf()函數(shù)把自己的輸出送到標(biāo)準(zhǔn)輸出。
- fprintf()函數(shù)把自己的輸出送到一個指定的文件流;
- sprintf()函數(shù)把自己的輸出和一個結(jié)尾空字符寫到座位參數(shù)傳遞過來的字符串s里。
下面一些常用的轉(zhuǎn)換控制符:
- %d , %i :以十進制格式輸出一個整數(shù)。
- %o , %x :以八進制或十六進制格式輸出一個整數(shù)。
- %c : 輸出一個字符
- %s : 輸出一個字符串
- %f : 輸出一個(單精度)浮點數(shù)。
- %e : 以科學(xué)計數(shù)法格式輸出一個雙精度浮點數(shù)。
- %g : 以通用格式輸出一個雙精度浮點數(shù)。
3.6.2:scanf 、fscanf 和 sscanf 函數(shù)
#include stdio.h
int scanf(const char *format,...);
int fscanf(FILE *stream,const char *format);
int sscanf(const char *s,const char *format);
說明:
- scanf 函數(shù)讀入的值將保持到對飲的變量里去,這個變量類型必須正確,并且它們必須精確匹配格式字符串。
一般來說,對scanf 系列函數(shù)的評價并不高,這主要有下面3方面的原因。
- 從歷史來看,它們的具體實現(xiàn)都有漏洞
- 它們使用不夠靈活
- 使用它們編寫的代碼不容易看出究竟正在解析什么。
3.6.3:其他流函數(shù)
- fgetpos:獲取文件流的當(dāng)前(讀寫)位置
- fsetpos:設(shè)置文件流的當(dāng)前(讀寫)位置
- ftell:返回文件流當(dāng)前(讀寫)位置的偏移植。
- rewind:重置文件流離的讀寫位置
- freopen:重新使用一個文件流
- setvbuf:設(shè)置文件流的緩沖機制
- remove:相當(dāng)于unlink函數(shù),但如果他的path參數(shù)是一個目錄的話,其作用就相當(dāng)于rmdir函數(shù)
3.6.4:文件流錯誤
為了表明錯誤,許多stdio苦函數(shù)會返回一個超出范圍的值,比如空指針或EOF常熟。此時,錯誤由外部變量errno指出:
#include <errno.h>
extern int errno;
注意許多函數(shù)都可能改變errno的值,他的值只有在函數(shù)調(diào)用失敗時才有意義,你必須在函數(shù)表明失敗之后立即對其進行檢查,你應(yīng)該總是在使用它之前將他先復(fù)制到另一個變量中,因為像fprintf這樣的輸出函數(shù)本身就可能改變errno的值。
你也可以通過檢查文件流的狀態(tài)來確定是否發(fā)生了錯誤,或者是否達到了文件尾。
#include stdio.h
int ferror(FILE *stream)
int feof(FILE *stream)
void clearerr(FILE *stream)
ferror函數(shù)測試一個文件流的錯誤標(biāo)識,如果改標(biāo)識被設(shè)置就返回一個非零值,否則返回零
feof 函數(shù)測試一個文件流的文件尾標(biāo)識被設(shè)置就返回非零值,否則返回零
clearerr 函數(shù)的作用時清除由stream指向的文件尾標(biāo)識和錯誤標(biāo)識。
3.6.5 文件流和文件描述符
每個文件流都和一個底層文件描述符相關(guān)聯(lián)。
#include stdio.h
int fileno(FILE *stream)
FILE *fdopen(int fildes,const char *mode);
說明:
fileno 函數(shù)來確定文件流使用的是那個底層文件描述符
fdopen函數(shù)操作方式與fopen函數(shù)是一樣的,只是前者參數(shù)不是一個文件名,而是一個底層文件描述符。
3.7文件和目錄的維護
3.7.1 chmod 系統(tǒng)調(diào)用
你可以通過chmod 系統(tǒng)調(diào)用來改變文件或目錄的訪問權(quán)限。
該函數(shù)原型如下:
#include <sys/stat.h>
int chmod(const char *path,mode_t mode);
path參數(shù)指定的文件被修改為具有mode參數(shù)給出的訪問權(quán)限。
參數(shù)mode的定義與open系統(tǒng)調(diào)用中的一樣
3.7.2 chown 系統(tǒng)調(diào)用
“超級用戶“可以使用chown 系統(tǒng)調(diào)用來改變一個文件的屬主。
#include <sys/types.h>
#include <unistd.h>
int chown (const char *path,uid_t owner,gid_t group);
這個調(diào)用使用的是用戶ID和組ID的數(shù)字值(通過getuid和getgid調(diào)用獲得)和一個限定誰可以修改文件屬主de系統(tǒng)值。
3.7.3 unlink、 link 、 symlink系統(tǒng)調(diào)用
#include unistd.h
int unlink(const char *path);
int link(const char *path1,const char *path2);
int symlink(const char *path1,const char *path);
unlink 系統(tǒng)調(diào)用刪除一個文件的目錄項并且減少它的連接數(shù)。它在成功時返回0,失敗時返回-1
link 系統(tǒng)調(diào)用將創(chuàng)建一個指向已有文件path1的新鏈接。新目錄項由path2給出。
symlink 系統(tǒng)調(diào)用類似的方式創(chuàng)建符號鏈接。
3.7.2 mkdir 和rmdir系統(tǒng)調(diào)用
使用mkdir 和 rmdir 系統(tǒng)函數(shù)來建立和刪除目錄。
#include sys/types.h
#include sys/stat.h
/**
* mkdir系統(tǒng)調(diào)用用于創(chuàng)建目錄,它相當(dāng)于mkdir程序。mkdir調(diào)用將參數(shù)path 作為新建目錄的名字
* 目錄的權(quán)限由參數(shù)mode設(shè)定,
*/
int mkdir (const char *path,mode_t mode);
/**
* rmdir 系統(tǒng)調(diào)用用于刪除目錄,但只有在目錄為空時才行。
*/
int rmdir (const char *path)
3.7.5 chdir 系統(tǒng)調(diào)用和getcwd函數(shù)
程序可以通過chdir系統(tǒng)調(diào)用來切換目錄
程序可以通過調(diào)用getcwd函數(shù)來確定自己的當(dāng)前工作目錄
#include unistd.h
int chdir(const char *path)
int getcwd (char *buf,size_t size)
getcwd 函數(shù)把當(dāng)前目錄的名字寫到給定的緩沖區(qū)buf里。如果目錄名的長度超出了參數(shù)size給出的緩沖區(qū)長度(一個ERANGE錯誤),他就返回NULL,如果成功,它返回指針buf
如果再程序運行過程中,目錄被刪除(EINVAL錯誤)或者有關(guān)權(quán)限發(fā)生變化(EACCESS)錯誤,gecwt也可能返回NULL
3.8 掃描目錄
Linux 系統(tǒng)上一個常見的問題就是掃描目錄,也就是確定一個特定目錄下存放的文件。
與目錄相關(guān)的函數(shù)dirent.h頭文件聲明。
函數(shù)列表:
- opendir、closedir
- readdir
- telldir
- seekdir
- closedir
3.8.1 opendir 函數(shù)
opendir函數(shù)作用是打開一個目錄并建立一個目錄流。如果成功,它返回一個指向dir結(jié)構(gòu)的指針,該指針用于讀取目錄數(shù)據(jù)項
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
opendir在失敗時返回一個空指針。注意,目錄流使用一個底層文件描述符來訪問目錄本身,所以如果打開的文件過多,opendir可能會失敗
3.8.2 readdir 函數(shù)
readdir 函數(shù)返回一個指針,該指針指向的結(jié)構(gòu)里保存著目錄流dirp中下一個目錄項的有關(guān)資料。
#include <sys/types.h>
#include <dirent>
struct dirent *readdir(DIR *dirp);
注意:如果在readdir函數(shù)掃描目錄的同時還有其他進程在該目錄里創(chuàng)建或刪除文件,readdir將不保證能夠列出該墓里的所有文件(和子目錄)
dirent結(jié)構(gòu)中包含的目錄項內(nèi)容包括以下部分。
ino_t d_ino:文件的inode節(jié)點號
char d_name[]文件名字。
3.8.3 telldir函數(shù)
telldir函數(shù)的返回值紀(jì)錄著一個目錄流里的當(dāng)前位置。
#include <sys/types.h>
#include <dirent.h>
long int telldir(DIR *dirp)
3.8.4: seekdir 函數(shù)
seekdir函數(shù)作用就是設(shè)置目錄流dirp的目錄項指針。loc的值用來設(shè)置指針位置,他應(yīng)該通過前個telldir調(diào)用獲得。
#include <sys/types.h>
#include <dirent.h>
void seekdir(DIR *dirp,long int loc);
3.8.5: closedir 函數(shù)
closedir 函數(shù)關(guān)閉一個目錄流并釋放與之關(guān)聯(lián)的資源,他的執(zhí)行成功返回0,發(fā)生錯誤時返回-1
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp)
總結(jié)案例
/*
============================================================================
Name : printdir.c
Author : james
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
void printdir(char *dir ,int depth){
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL){
fprintf(stderr,"cannot open directory:%s\n",dir);
return ;
}
chdir(dir);
while((entry == readdir(dp)) != NULL){
lstat (entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)){
/* Found a directory ,but ignore. and..*/
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
printf("%*%s\n",depth,"",entry->d_name);
printdir(entry->d_name,depth+4);
}
else printf("%*%s%s\n",depth,"",entry->d_name);
}
chdir("..");
closedir(dp);
}
int main(void) {
printf("Directory scan of /home/james/workspace:\n");
printdir("/home/james/workspace",0);
printf("done.\n");
return EXIT_SUCCESS;
}
3.9 錯誤處理
錯誤代碼的取值喝含義都列在頭文件errno.h里:
- ERERM 操作不允許
- ENOENT 文件或目錄不存在
- EINTR 系統(tǒng)調(diào)度被中斷
- EIO I/O 錯誤
- EBUSY 設(shè)備或資源忙
- EEXIST 文件存在
- EINVAL 無效參數(shù)
- EMFILE 打開文件過多
- ENODEV 設(shè)備不存在
- EISDIR 是一個目錄
- ENOTDIR 不是一個目錄
有兩個非常有用的函數(shù)可以用來報告出現(xiàn)的錯誤,它們是strerror 和perror
3.9.1 strerror 函數(shù)
strerror 函數(shù)把錯誤代碼映射為一個字符串,該字符串對發(fā)生的錯誤類型進程說明。這在記錄錯誤條件時十分有用
函數(shù)如下
#include <string.h>
char *strerror(int errnum)
3.9.2 perror 函數(shù)
perror 函數(shù)也罷errno 變量中報告的當(dāng)前錯誤映射到了一個字符串,并把它輸出到標(biāo)準(zhǔn)錯誤輸出流。該字符串的前面加上字符串s(如果不為null)中給出的消息,再加上一個冒號和空格
該函數(shù)原型如下:
#include <stdio.h>
void perror(const char *s);
請看下面的栗子:
perror(“program”);
他可能在標(biāo)準(zhǔn)錯誤輸出中給出如下輸出結(jié)果:
program:Too many open files
3.10 /proc 文件系統(tǒng)
Linux 提供了一個特殊的文件系統(tǒng)procfs ,它通常以/proc 目錄的形式呈現(xiàn)。該目錄中包含了許多的特殊文件用來對缺懂程序和內(nèi)河星系進程更高層的訪問。只要應(yīng)用程序有正確的訪問權(quán)限,他們就可以通過讀寫這些文件來獲得信息或設(shè)置參數(shù)。
3.11 高級主題:fcntl 和 mmap
3.11.1: fcntl 系統(tǒng)調(diào)用
fcntl 系統(tǒng)調(diào)用對底層文件描述符提供了更多的操縱方法。
#include <fcntl.h>
int fcntl(int fildes,int cmd)
int fcntl(int fildes,int cmd ,long arg)
利用fcntl系統(tǒng)調(diào)用,你可以對伐開的文件描述符進行各種操作,報考對它們進程復(fù)制、獲取和設(shè)置文件描述符的標(biāo)志,以及管理建設(shè)性文件性文件鎖等。
3.11.2 mmap 函數(shù)
Unix 提供了一個有用的功能以允許程序共享內(nèi)存,Linux內(nèi)核從2.0版本開始已經(jīng)把這一功能包括進程。mmap(內(nèi)存映射)函數(shù)作用是建立一段可被兩個或多個程序讀寫內(nèi)存。一個程序?qū)λ鞒龅男薷目梢员黄渌绦蚩匆姟?/p>
這一組功能還可以用到文件處理上。你可以是某個磁盤文件的全部看起來就像在內(nèi)存中的一個數(shù)組,如果文件由紀(jì)錄組成,而這些記錄又能夠用C語言中的結(jié)構(gòu)來描述的話,就可以通過訪問結(jié)構(gòu)數(shù)組來更新文件的內(nèi)容了。
mmap函數(shù)創(chuàng)建了一個指向一段內(nèi)存區(qū)域的指針,該內(nèi)存區(qū)域可以通過一個打開的文件描述符訪問文件的內(nèi)容相關(guān)聯(lián)。
#include <sys/mman.h>
void *mmap(void *addr ,size_t len, int port,int flags,int fildes,off_t off);
可以通過傳遞off參數(shù)來改變共享內(nèi)存段訪問文件中暑的啟示偏移值。打開文件描述符由filds參數(shù)給出。可以訪問的數(shù)據(jù)量(即內(nèi)存的長度)由len參數(shù)設(shè)置。
port 參數(shù)用于設(shè)置內(nèi)存段的訪問權(quán)限。
- PROT_READ:允許讀該內(nèi)存段
- PROT_WRITE:允許寫該內(nèi)存段
- PROT_EXEC:允許執(zhí)行該內(nèi)存段
- PROT_NONE:該內(nèi)存段不能被訪問
flags參數(shù)控制程序?qū)υ搩?nèi)存段的改變所造成的影響。
- MAP_PRIVATE 內(nèi)存段時私有的,對它的修改只對本進程有效
- MAP_SHARED 把該內(nèi)存段的修改保存到磁盤文件中
- MAP_FIXED 該內(nèi)存段必須位于addr指定的地址處
msync 函數(shù)的作用是:把該內(nèi)存段的某個部分或爭端中的修改回到被映射的文件中(或者從被映射文件里讀出)。
mumap 函數(shù)的作用是:釋放內(nèi)存段