天天看點

linux檔案操作(一)

在這一部分當中,我們将會讨論Linux的檔案以及 目錄以及如何來管理他們.我們将會學習建立檔案,打開檔案,讀取檔案,寫入檔案以及關閉檔案.我們也将會學習程式如何來管理目錄(例如建立,掃描,删 除).在上一部分當中我們使用Shell進行程式設計,而現在我們要開始使用C程式設計.

在讨論Linux處理檔案I/O之前,我們将會看一些與檔案,目 錄以及裝置相關的概念.要處理檔案與目錄,我們需要使用系統調用(與Windows API相類似的Unix/Linux調用),但是也存在着一系列的庫函數,标準I/O庫(stdio),來使得我們的檔案處理更為有效.

在這裡我們要将讨論處理檔案和目錄的各種調用,是以我們将會談到下面的一些内容:

1 檔案與裝置

2 系統調用

3 庫函數

4 低層檔案通路

5 檔案管理

6 标準I/O庫

7 格式化輸入與輸出

8 檔案與目錄維護

9 目錄掃描

10 錯誤

11 /proc檔案系統

12 進階主題:fcntl與mmap

Linux檔案

也許有人會問:為什麼?我們要來讨論檔案結構嗎?我們已經知道這些了.不錯,與Unix中相類似,檔案在Linux環境中是相當重要的,因為他們提供了簡單并一緻的接口來處理系統服務與裝置.在Linux中,一切都是檔案.

這就意味着,在Linux中,程式可以像處理普通檔案一樣來使用磁盤檔案,序列槽,列印機以及其他的裝置.

而目錄,也是一類特殊的檔案.在現代的Unix版本中,包括Linux,甚至是超級使用者也不可以對他們直接進行寫入操作.所有的普通使用者使用高層的opendir/readdir接口來讀取目錄,而不需要知道目錄實作的系統細節.我們稍後将會讨論特殊的目錄函數.

确實,在Linux下,所有的内容都被看成檔案,或者是通過特殊的檔案可以通路.即便這樣,也存在着一些主要的原則,而這與我們平常所了解和喜愛的檔案是不同的.現在我們來看一下我們談到的特殊情況.

目錄

與 他的内容一樣,一個檔案有檔案名和一些屬性,或者是管理資訊;也就是說是這些檔案的建立/修改日期與通路權限.這些屬性存放在檔案的I節點(inode) 中,所謂的I節點是檔案系統中一個特殊資料塊,包含着檔案的長度以及在磁盤上的存放位置等資訊.系統使用檔案的I節點數目,目錄結構隻是為我們的友善進行 檔案的命名.

一個目錄就是一個包含I節點數目以及其他檔案名的檔案.每一個目錄實體都是指向一個檔案I節點的連結,删除了這個檔案名,我們也就删 除了這個連結(我們可以使用ls -i指令來檢視一個檔案的I節點數).使用ln指令,我們可以建立在不同的目錄中指向同一個檔案的連結.如果指向一個檔案的連結數(也就是ls -l指令輸出中權限後的數字)為零,那麼他所指向的I節點和資料就不再使用并标記為空閑.

檔案排列在目錄,而其中一些也許還會有子目錄.這樣就形 成了我們所熟悉的檔案系統結構.一個名為neil使用者,通常将他的檔案存放在home目錄中,也許是/home/neil,而在其中也許會存在一些e- mail等一些子目錄.在這裡我們要注意就是在Unix和Linux Shell中都有一個直接到達我們的使用者主目錄的指令,也就是~.如果要進入其他使用者的目錄,我們可以輸入~user.正如我們所知道的,每一個使用者的 home目錄都是因為這個特殊的原因而建立的高層目錄的子目錄,在這裡是/home.

在這裡我們要注意的就是在标準的庫函數中并不支援檔案名參數的~符号.

/home 目錄隻是根目錄/的一個子目錄,根目錄是檔案系統層次的最高層并且在其子目錄中包含所有的系統檔案.root目錄通常包含有系統程式的/bin目錄,系統 配置檔案的/etc目錄,系統庫的/lib目錄.代表實體裝置并提供處理這些裝置的接口的檔案位于/dev目錄中.我們可以在Linux File System Stander上查找到更為詳細的内容,或者是我們可以使用man hier指令來得到更為詳細的描述.

檔案與裝置

甚至是硬體裝置也會表示成為檔案.例如,作為超級使用者,我們可以将CD-ROM挂載作為一個檔案:

# mount -t iso9660 /dev/hdc /mnt/cdrom

# cd /mnt/cdrom

這樣就會啟動CD-ROM裝置,并将其當将的内容作為/mnt/cdrom檔案結構分枝.我們可以像平常的檔案一樣進入CD-ROM目錄,當然,此時這些内容都是隻讀的.

在Unix和Linux中常見了的三個最重要的裝置為/dev/console,/dev/tty和/dev/null.

/dev/console

這 個裝置代表系統控制台.錯誤以及論斷資訊常會發到這個裝置.每一個Unix系統都會有一個指定的終端或是螢幕來接收控制台資訊.曾經,這是一個複雜的列印 終端.在現代的工作站或是Linux中,他通常是一個活動的虛拟控制台,而在X下,則會是在螢幕上的一個特殊的控制視窗.

/dev/tty

/dev/tty是一個程序控制終端的别名,如果有這樣的一個程序.例如,由cron運作的程序并不會有一個控制終端,是以他不會打開/dev/tty.

在他可以使用的地方,/dev/tty允許程式将資訊直接寫給使用者,而不需要考慮使用者正在使用何種終端.他在标準輸出重定向時相當有用.一個例子就是ls -R | more指令,在這裡more可以用輸出的每一個新頁來提示給使用者.

在這裡我們要注意的是,雖然隻有一個/dev/console裝置,但是卻可以通過/dev/tty來通路多個不同的實體裝置.

/dev/null

這是一個空裝置.所有寫入這個裝置的資訊都會被丢棄.當讀取這個檔案時将會立刻到達檔案的結尾處,這樣他就可以作為使用cp指令的一個空的檔案源.所有不希望看到的輸出都可以重定向到這個裝置.

另一個建立新檔案的方法就是使用touch <filename>指令,他将修改一個檔案的修改日期,而如果這個檔案不存在則會建立這個檔案.然而他并不會清空他的内容.

$ echo do not want to see this >/dev/null

$ cp /dev/null empty_file

其 他可以在/dev目錄中找到的裝置有硬碟,軟碟,通信口,錄音帶,CD-ROM,聲霸卡以及一些代表系統内部狀态的裝置.還有一個裝置/dev/zero,他 可以作為一個空位元組的源來建立一個檔案大小為零的檔案.我們需要超級使用者的權限來通路其中的一些設定.普通的使用者不可以使用程式來直接通路類似于硬碟這樣 的低層裝置.裝置名也許會由系統的不同而不同.在Linux發行版本中通常會有一個需要超級使用者來運作的程式來管理這些裝置檔案,例如mount指令.

裝置可以分為字元裝置與塊裝置.差別就在于一些裝置需要一次通路一個塊.塊裝置通常是指那些支援随機存取的檔案系統,如硬碟.

系統調用與裝置驅動

我們可以使用一些函數來通路和控制裝置檔案.這些函數就是所謂的系統調用,是由Unix/Linux直接提供的到作業系統的接口.

在 作業系統的核心,核心,是大量的裝置驅動.這是控制系統硬體的低層接口的集合.例如會有一個錄音帶驅動,他可以知道如何來啟動錄音帶,向前或是向後,讀或是 寫.他同時知道錄音帶每次要寫入一定大小的資料塊.因為錄音帶是順序存取的,驅動器不可以直接通路錄音帶塊,而是必須要轉到的指定的位置.

與此相類似,低層硬碟裝置也會每一次寫入一個指定的磁盤塊,但是可以直接通路所需的磁盤塊,因為磁盤是随機存取的裝置.

為了提供一個相似的接口,硬碟驅動器封裝了所有依賴于硬體的特征.硬體的材質特征我們可以通過ioctl得到.

/dev中的裝置檔案可以用同樣的方法來使用,他們可以打開,讀取,寫入和關閉.例如用來打開一個正常檔案的open調用可以用來通路一個使用者終端,一個列印機或是錄音帶.

用來通路裝置驅動器的代層系統調用包括:

1 open:打開一個檔案或是裝置

2 read:從一個打開的檔案或是裝置讀

3 write:寫入一個檔案或是裝置

4 close:關閉檔案或是裝置

5 ioctl:向裝置驅動器傳遞控制資訊

ioctl系統調用用來提供必須的硬體控制,是以他會因裝置的不同而不同.例如,ioctl調用可以用來重定位錄音帶或是設定序列槽的字元流.正是由于這個原因,ioctl從一個機器到另一個機器并不是必須移植的.另外每一個驅動器定義了自己的ioctl指令集.

庫函數

直接使用低層的系統來進行輸入與輸出所存在的問題就是這樣的方式并不是十分的有效.為什麼呢?

1 使用系統調用會有一個不好的結果.與函數調用比較起來系統調用要浪費大量的資源,因為這時Linux要在執行我們的程式代碼與執行他的核心代碼之間進行切 換.一個好的方法是盡量使得在一個程式中所用到的系統最少,同時要使得每一個系統調用做盡可能多的工作,例如,每一次要讀出或是寫入大量的資料,而不是第 一次隻讀寫一個字元.

2 由于硬體的限制會在每一次使用低層系統調用來讀寫的資料尺寸上有許多的限制.例如,對于錄音帶驅動器來說,他們每一次可以寫入的資料塊的大小為10k.是以 如果我們每一次要寫入的資料塊大小并不是10k的整倍數,那麼錄音帶就會向前到下一個10k處,這時就會在錄音帶上留下一段空白.

為了提供一個到裝置 或是硬碟檔案的高層接口,與Unix相類似,Linux發行版本提供了大量的标準庫.這是一些我們可以包含在我們的程式中用來處理這些問題的函數的集合. 一個很好的例子就是提供了緩沖區輸出的标準I/O庫.我們可以高效的書寫變尺寸的資料塊,進而可以使用那些為了提供完整的資料塊而安排的低層系統調用.這 樣就會大大的減少了系統調用的開銷.

庫函數通常位于手冊頁的第三部分,而且通常會有一個标準的頭檔案與之相對,如對于标準輸入輸出的stdio.h

低層的檔案通路

每一個正在運作的程式,被稱之為一個程序,他們都有一系列的檔案描述符與之相對.這是一些我們可以用來通路打開的檔案或是裝置的小整數.我們可以使用這些迫切描述符中的多少内容是依賴于我們的系統配置的.當啟動一個程式時,他通常會有三個已打開的描述符.他們是:

0:标準輸入

1:标準輸出

2:标準錯誤輸出

我們可以使用open系統調用來使用其他檔案或是裝置的描述符,這正是我們稍後将要讨論的問題.然而那些自動打開的檔案描述符可以允許我們使用write來建立一些簡單的程式.

write

write 系統會将buf中的第一個nbytes位元組的内容寫入與fildes檔案描述符相關的檔案.他的傳回值為實際寫入的位元組數.如果在檔案描述符中發生了錯誤 或是底層的裝置驅動器要求塊的尺寸時,傳回值也許會小于nbytes.如果函數傳回0,則說明沒有資料寫入.如果函數傳回-1,則說明在write調用中 發生了錯誤,而這個錯誤将會存放在errno全局變量中.

write的文法如下:

#include <unistd.h>

size_t write(int fildes, const void *buf, size_t nbytes);

有了這些知識,我們可以寫出我們的第一個程式,simple_write.c:

#include <stdlib.h>

int main()

{

    if ((write(1, “Here is some data\n”, 18)) != 18)

        write(2, “A write error has occurred on file descriptor 1\n”,46);

    exit(0);

}

這個程式隻是簡單的在标準輸出上列印一條資訊.當程式結束時,所有打開的描述符都會自動關閉,是以我們并不需要顯示的關閉他們.然而當我們處理緩沖區的輸出時并不是這樣的情況.

運作這個程式,我們會得到下面的輸出結果:

$ simple_write

Here is some data

$

在這裡有一點值得我們注意的就是也許會報告他所寫入的位元組比我們所要求的要少.這并不是一個必須的錯誤.在我們的程式中,我們需要松果檢查error進而檢測錯誤并且調用write寫入其餘的資料.

read

read 系統調用會從fildes檔案描述符所指的檔案中讀取nbytes位元組的資料并将所讀取的資料放在資料區域buf中.他的傳回值為實際讀取的位元組數,這也 許會比所要求的數值要小.如果一個read函數傳回0,則并沒有讀取任何内容,而是到達了檔案的結尾.同樣,如果發生錯誤則傳回-1.

其文法如下:

size_t read(int fildes, void *buf, size_t nbytes);

下面的這個簡單的程式simple_read.c,将會從标準輸入讀取128個字元到标準輸出.如果實際的資料個數小于128,則會讀取全部的内容.

    char buffer[128];

    int nread;

    nread = read(0, buffer, 128);

    if (nread == -1)

        write(2, “A read error has occurred\n”, 26);

    if ((write(1,buffer,nread)) != nread)

        write(2, “A write error has occurred\n”,27);

如果我們運作這個程式,我們會得到下面的輸出結果:

$ echo hello there | simple_read

hello there

$ simple_read < draft1.txt

Files

In this chapter we will be looking at files and directories and how to manipulate

them. We will learn how to create files, o$

在第一次運作時,我們為我們的程式使用echo指令建立一些輸入,這些輸入将導入我們的程式.在第二次運作時,我們從一個檔案重定向輸入.在這種情況下,我們發現檔案draft1.txt的第一部分出現在标準輸出中.

open

要建立一個新的檔案描述符,我們需要使用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);

更為嚴格的說,我們并不需要包含sys/types.h和sys/stat.h來打開一個POSIX系統,但是在一些Linux系統這卻是必須的.

從 簡單的角度來說,open建立一個到檔案或是裝置的通路路徑.如果調用成功,則會傳回一個檔案描述符,而這個檔案描述符可以用于read,write或是 其他的系統調用.這個檔案描述符是唯一,而不會與其他正在運作的程序所共享.如果兩個程式在同一時間打開了同一個,他們就會分别維護不同的檔案描述符.如 果他們同時寫入檔案,他們将會在他們所讀入的地方寫入檔案.這些資料并不會插入,而是一個會覆寫另一個.每一個程式都會記錄一個他們所讀入或是寫入檔案的 偏移量的資訊.我們可以通過使用檔案加鎖的方法來避免這樣的情況發生.

要打開的檔案或是裝置的名字是以path參數的形式傳入的.而oflags參數則是指定了要在打開的檔案上進行的動作.

oflags是以指令(mandatory)檔案通路或是其他一些可選模式組合的方式來指定的.open調用必須指定下列檔案通路模式中的一種:

O_RDONLY    以隻讀方式打開

O_WRONLY    以隻寫方式打開

O_RDWR        以讀寫方式打開

這個調用還可以在oflags參數中包含下列可選模式的組合(使用OR):

O_APPEND    在檔案末尾寫入資料

O_TRUNC        将檔案的大小設為零,而不管存在的内容

O_CREAT        如果需要,以指定的模式建立檔案

O_EXCL        與O_CREAT配合使用,保證調用建立檔案.

open是原子型的,也就是他隻作為一個函數調用.工程禁止兩個程式同時建立檔案.如果檔案已經存在,則open失敗.

其他一些oflags的可能的參數可以在open手冊頁中找到,這個手冊頁可在手冊頁的第二部分找到(使用man 2 open).

open 如果調用成功則會傳回一個新的檔案描述符(通常是一個非負整數),如果失敗則會傳回-1,同時open會設定errno全局變量來指明失敗的原因.我們将 會在以後的部分中詳細的讨論errno.新的檔案描述符總是最小的未被使用的檔案描述符,這在一些環境下是相當有用的.例如,如果一個程式關閉了他的标準 輸出,然後調用open函數,檔案描述符1可以重新使用,而标準輸出也可以高效的重定向到另一個不同的檔案或是裝置.還有一個被POSIX标準化的 creat調用,但是這個函數并不常用.creat并不如我們所希望那樣的僅是建立檔案,而是會同時打開這個檔案,這與使用open函數同時使用 O_CREAT|O_WRONLY|O_TRUNC oflags參數的效果是一樣的.

初始權限

當我們使用open函數的O_CREAT來建立一個檔案時,我們必須使用第三個參數的形式.mode是第三個參數,他是由檔案sys/stat中所定義的.這些權限如下:

S_IRUSR:擁有者讀權限

S_IWUSR:擁有者寫權限

S_IXUSR:擁有者執行權限

S_IRGRP:組讀權限

S_IWGRP:組寫權限

S_IXGRP:組執行權限

S_IROTH:其他使用者讀權限

S_IWOTH:其他使用者寫權限

S_IXOTH:其他使用者執行權限

例如下面的例子:

open (“myfile”, O_CREAT, S_IRUSR|S_IXOTH);

這個例子将會生成一個名為myfile的檔案,其權限為擁有者的讀權限和其他使用者的執行權限,而且隻有這些權限.

$ ls -ls myfile

0 -r------x   1 neil software 0 Sep 22 08:11 myfile*

會 有許多的因素影響檔案的權限.首先,隻有檔案在建立時使用的權限.第二,使用者的屏蔽位(由umask指令所指定)影響已建立的檔案權限.open調用時所 指定的模式值以及運作時所保留的使用者屏蔽位.例如,如果使用者的屏蔽位設定為001并且在建立檔案時指定了S_IXOTH權限,則所建立的檔案并不會有其他 使用者的執行權限,因為使用者的屏蔽位并沒有提供其他使用者的執行權限.事實上,open以及creat調用中的标志需要請求設定權限.所請求設定的權限有沒有 設定則依賴于運作時的umask值.

umask

umask 是一個系統變量,當建立一個檔案時,可以被用來為一個檔案的權限設定屏蔽位.我們可以通過執行umask指令并提供一個新的值進而可以改變這個變量的 值.umask的值是一個三位的十六進制數.每一個數字是1,2或4中的數相加的結果值.我們可以從下表中清楚地明白這個意思.每一個不同的數字位可以對 應user,group,other的權限.

第一位:

0    沒有使用者的權限被禁止

4    使用者讀權限被禁止

2    使用者寫權限被禁止

1    使用者執行權限被禁止

第二位:

0    沒有組權限被禁止

4    組讀權限被禁止

2    組寫權限被禁止

1    組執行權限被禁止

第三位:

0    沒有其他使用者的權限被禁止

4    其他使用者讀權限被禁止

2    其他使用者寫權限被禁止

1    其他使用者執行權限被禁止

例如,要禁止group的寫與執行權限以及other的寫權限,我們可以使用下面的umask值:

Digit Value

1     0

2     2

      1

3     2

每一位的值是相加的結果,是以第二位将是2&1,也就是3.是以umask值将為032.

當 我們使用open或是creat來建立一個檔案時,mode參數将會與umask值相比較.同時在mode參數和umask值中進行了設定的位将會被移 除.最終的結果将會是使用者可以設定他們的環境來說:不要建立帶有其他使用者寫權限的檔案,僅管建立檔案的程式要求這樣的權限設定.這樣并不會阻止一個程式或 是使用者在以後使用chmod指令(或是在一個程式中使用chmod系統調用)來增加其他使用者的寫權限,但是這确實可以避免使用者在所有的新檔案上檢查和設定 權限,進而可以起到保護使用者的作用.

close

我們使用close系統調用來關閉一個檔案描述符,files與其相對應的檔案之間的關聯.這樣這個檔案描述符就可以重新被使用.如果調用成功則會傳回0,錯誤傳回-1.

其文法格式如下:

int close(int fildes);

在這裡我們要注意就是檢查close的傳回值是相當重要的.一些檔案系統尤其是網絡檔案系統,當寫入檔案時并不會報告寫入錯誤,直到檔案關閉,進而會造成當執行寫入動作并沒有真正将資料寫入檔案.

一個運作的程式同時打開的檔案數目是有限制的.這個限制是由limits.h中定義的OPEN_MAX值決定的,會因系統的不同而不同,但是POSIX要求最少為16.這個限制也許會受到本地系統限制的影響.

ioctl

ioctl 是一個事物的集合.他提供了一個接口進而可以控制裝置的形為以及他們的描述符和服務的配置.終端,檔案描述符,套接字,甚至錄音帶都有為他們所定義的 ioctl調用,我們可以檢視相關的手冊頁得到更為詳細的内容.POSIX隻為流定義了ioctl.其文法格式如下:

int ioctl(int fildes, int cmd, ...);

ioctl會執行由檔案描述符fildes所對應目标上的cmd所訓示的函數.他也許會帶有第三個可選的參數,這要依賴于具體的裝置所提供的函數.

現在我們已經了解了足夠多的關于open,read和write系統調用的知識,我們現在可以寫一個低層程式,copy_system.c,将一個檔案的内容一個字元一個字元地拷貝到另一個檔案.

(在整個這個讨論中,我們将使用不同的方法來寫這個程式,進而可以比較不同方法之間的效率問題.為簡單起見,我們假設輸入檔案存在,而輸出檔案不存在,并且所有的讀與寫操作都是成功的.當然,在真正的程式設計中,我們将會檢測這些假設是否真實存在).

#include  <unistd.h>

#include  <sys/stat.h>

#include  <fcntl.h>

#include  <stdlib.h>

    char c;

    int in, out;

    in = open(“file.in”, O_RDONLY);

    out = open(“file.out”, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);

    while(read(in,&c,1) == 1)

         write(out,&c,1);

在這裡我們要注意的是,#include <unistd.h>必須放在第一行,因為他定義POSIX編譯相關的标記,進而會影響其他包含進來的檔案.

首先,我們需要建立一個測試輸入檔案,大小為1Mb,命名為file.in.

如果我們運作這個程式,我們會得到下面的輸出:

$ TIMEFORMAT=”” time copy_system

4.67user 146.90system 2:32.57elapsed 99%CPU

...

$ ls -ls file.in file.out

1029 -rw-r--r-   1 neil     users     1048576 Sep 17 10:46 file.in

1029 -rw-----   1 neil     users     1048576 Sep 17 10:51 file.out

在 這裡我們使用time程式來測量運作這個程式所需要的時間.TIMEFORMAT變量是在Linux系統用來覆寫POSIX預設的時間輸出格式的,而在默 認的情況下并不會包含CPU的使用情況.從這個輸出結果我們可以看出在這個較老的系統上,1Mb的檔案file.in成功地複制到了檔案 file.out,而這個輸出輸出檔案隻對于擁者本身有讀和寫的權限.然而這個程式的運作卻使用了2分半,并且幾乎占用全部的CPU運作時間.他這樣慢的 原因是因為他使用了2百萬次的系統調用.

在近些年,Linux在系統調用與檔案系統方面取得了較大的進步.比較而言,在2.4的核心上進行測試則隻需要不到5秒的時間.

1.07user 3.50system 0:04.77elapsed 95%CPU

我們可以通過一次複制較大的塊來進行改進.在下面的這個改進的程式copy_block.c中,一次拷貝1k位元組,并且也使用系統調用:

    char block[1024];

    while((nread = read(in,block,sizeof(block))) > 0)

         write(out,block,nread);

現在我們來運作這個程式,首先要删除舊的輸出檔案:

$ rm file.out

$ TIMEFORMAT=”” time copy_block

0.00user 0.01system 0:00.01elapsed 62%CPU

現在這個程式隻需要百分之一秒,而且他隻需要大概2000次系統調用.當然,這個時間會因系統的不同而異,但是他們卻顯示出系統調用的大量開支,是以他們的使用是值得優化的.

管理檔案的其他系統調用

還有許多其他的系統調用可以用來處理這樣的低層檔案描述符.這些允許一個程式來控制如何來使用一個并且傳回狀态資訊.

lseek

lseek系統調用可以用來設定檔案描述符fildes的讀或是寫的指針.也就是說我們可以用來設定将會發生讀寫的位置.我們可以在檔案中使用絕對位址來設定檔案指針或是與目前位置或是檔案結尾的相對位置來設定檔案指針.

off_t lseek(int fildes, off_t offset, int whence);

offset參數用來指定位置,而whence參數用來指定如何使用偏移量.whence的取值可以是下列中的一個:

SEEK_SET:offset是一個絕對位址

SEEK_CUR:offset是一個對目前位置的相對位址

SEEK_END:offset是對檔案結尾的相對位置

lseek的傳回值為從設定了檔案指針的檔案開始以位元組大小度量的offset值,如果失敗則會傳回-1.在lseek操作中使用的off_t類型是在sys/types.h中定義的獨立實作.

fstat,stat,lstat

fstat系統調用會傳回與打開檔案描述符相對應的檔案的狀态資訊.這些資訊将會寫入一個結構buf中,而buf的位址是作為參數進行傳遞的.

他們的文法格式如下:

int fstat(int fildes, struct stat *buf);

int stat(const char *path, struct stat *buf);

int lstat(const char *path, struct stat *buf);

在這裡我們所包含的sys/types.h檔案是可選的,,但是我們建議在使用系統調用時這樣做,因為他們的一些定義采用了标準類型的别名,而這些是有可能發生變化的.

相關的函數stat與lstat将傳回一個命名檔案的狀态資訊.他們會産生同樣的結果,但是當檔案是一個符号連結時就會有一些不同.lstat會傳回這個連結的資訊,而stat卻會傳回連結所指向的檔案的資訊.

stat結構的成員會因系統的不同的而有所不同的,但是一般情況下都會有下面的一些資料:

st_mode        檔案權限以及檔案類型資訊

st_ino        檔案I節點資訊

st_dev        檔案所在的裝置資訊

st_uid        檔案所有者的使用者辨別

st_gid        檔案所有者的組辨別

st_atime    上一次通路的時間

st_ctime    上一次對權限,所有者,組,或是内容的修改時間

st_mtime    上一次對内容的修改時間

st_nlink    到這個檔案的硬連結數目

由stat結構傳回的st_mode同時也會包含一些在頭檔案sys/stat.h中定義的許多相關的宏.這些宏包括權限名字,标記檔案類型,以用用不幫助測試一些特殊的類型和權限的内容.

權限标記與我們在前面所說到的open系統調用的權限相同.檔案類型标記包括下面的一些:

S_IFBLK:實體是一個特殊的塊裝置

S_IFDIR:實體是一個目錄

S_IFCHR:實體是一個特殊的字元裝置

S_IFIFO:實體是一個IFIFO(指令管道)

S_IFREG:實體是一個正常檔案

S_IFLNK:實體是一個符号連結

其他的一些模式标記如下:

S_ISUID:實體在執行權限上設定了UID

S_ISGID:實體在執行權限上設定了GID

用來解釋st_mode的屏蔽(mask)如下:

S_IFMT:檔案類型

S_IRWXU:使用者讀/寫/執行權限

S_IRWXG:組讀/寫/執行權限

S_IRWXO:其他使用者讀/寫/執行權限

還有一些定義的宏用來幫助我們确定檔案類型.這些隻是将合适的屏蔽模式标記與合适的類型标記進行對比.他們包含:

S_ISBLK:為特殊的塊檔案測試

S_ISCHR:為特殊的字元檔案測試

S_ISDIR:為目錄測試

S_ISFIFO:為FIFO測試

S_ISREG:為正常檔案測試

S_ISLNK:為符号連結測試

例如,要測試一個不是一個目錄并且為所有者設定了執行權限,但是并沒有其他使用者的權限,我們可以用下面的測試:

struct stat statbuf;

mode_t modes;

stat(“filename”,&statbuf);

modes = statbuf.st_mode;

if(!S_ISDIR(modes) && (modes & S_IRWXU) == S_IXUSR)

    ...

dup與dup2

dup 系統調用提供了一個檔案描述符副本的方式,可以提供給我們兩個或是更多個通路同一個檔案的描述符.這可以用于在一個檔案讀和寫不同的位置.dup系統産生 了檔案描述fildes的一個副本,傳回一個新的描述符.dup2系統可以通過指定拷貝用的描述符可以高效的将一個檔案描述符拷貝到另一個.

int dup(int fildes);

int dup2(int fildes, int fildes2);

這些調用在我們用管道進行多個程序的通信時就會非常有用.我們将會在以後的部分更為詳細的讨論dup系統.

本文轉自nxlhero 51CTO部落格,原文連結:http://blog.51cto.com/nxlhero/213803,如需轉載請自行聯系原作者