Linux文件操作

文件操作

(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ù)塊大小。

用戶空間與內(nèi)核空間交互圖

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)

stat 結(jié)構(gòu)成員圖

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ù)。

proc目錄下面文件一覽

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)存段

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 底層文件訪問 open系統(tǒng)調(diào)用 在遵循POSIX規(guī)范的系統(tǒng)上,使用open系統(tǒng)調(diào)用并不需要包含頭文件sys/typ...
    Select_Ep閱讀 1,503評論 1 3
  • linux資料總章2.1 1.0寫的不好抱歉 但是2.0已經(jīng)改了很多 但是錯誤還是無法避免 以后資料會慢慢更新 大...
    數(shù)據(jù)革命閱讀 12,228評論 2 33
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,933評論 18 139
  • 一、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運行的地址不確定 關(guān)于...
    SeanCST閱讀 7,880評論 0 27
  • 老人救起一只狼 奄奄一息入膏肓 老人舍其心愛驢 讓狼打尖救救急 雖知此狼通人氣 與驢和諧敬老人 惡獸也有報恩心 何...
    旖旎i閱讀 540評論 11 19