国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

文件和設備編程

2019-11-17 05:15:14
字體:
來源:轉載
供稿:網友

  文件訪問原語

POSIX API 最重要的一個抽象概念就是文件。盡管幾乎所有的操作系統都將文件用于永久性存儲器,但所有 Unix 版本通過文件抽象概念提供對大多數系統資源的訪問。
更具體地說,這意味著 linux 使用相同的一組系統調用來提供對設備(例如軟盤和磁帶設備)、網絡資源(最常見的是 TCP/ip 連接)、系統終端,甚至內核狀態信息的訪問。感謝無所不在的系統調用,嫻熟地使用與文件相關的調用對于每個 Linux 程序員來說都很重要。讓我們仔細查看一下文件 API 背后的一些基本概念,并描述最重要的文件相關系統調用。

linux 提供許多不同種類的文件。最常見的類型就簡稱為常規文件,它存儲大量用于以后訪問的信息。您所使用的絕大部分文件 -- 例如可執行文件(如 /bin/vi)、數據文件(如 /etc/passwd)和系統二進制文件(如 /lib/libc.so.6)-- 都是常規文件。它們通常駐留在磁盤上的某處,但我們稍后會發現,并不一定都是這種情況。

另一種文件類型是目錄,它包含了一個其它文件及其位置的列表。使用 ls 命令列出目錄中的文件時,它打開該目錄的文件,并打印出它所包含的所有文件的信息。

其它文件類型包括塊設備(表示文件系統高速緩存的設備,例如硬盤驅動器)、字符設備(表示非高速緩存的設備,例如磁帶驅動器、鼠標和系統終端)、管道和套接字(答應進程相互之間對話),以及符號鏈接(答應文件在目錄層次結構中有多個名稱)。

大多數文件都有一個或多個引用它們的符號名。這些符號名是一組由 / 字符定界的字符串,并向內核標識文件。它們是 linux 用戶所熟悉的路徑名;例如,路徑名 /home/ewt/article 引用的是我手提電腦中包含這篇文章文本的文件。沒有兩個文件可以共享相同的名稱(但單一文件可以有多個名稱),因此路徑名唯一地標識單一文件。

進程可以訪問的每個文件都由一個小的非負整數標識,稱為“文件描述符”。文件描述符由打開文件的系統調用創建,并由從當前進程創建的新子進程繼續。就是說,當進程啟動了一個新程序時,原始進程的打開文件通常是由新程序繼續的。

按照約定,大多數程序保留前三個文件描述符(0、1 和 2)用于非凡目的 -- 訪問所謂的標準輸出、標準輸出和標準錯誤流。文件描述符 0 是標準輸入,這里許多程序都將從外部世界接收輸入。文件描述符 1 是標準輸出。大多數程序在這里顯示正常的輸出。對于與錯誤情況相關的輸出,使用文件描述符 2(標準錯誤)。

任何習慣使用 linux shell 的人都曾看到過標準輸入、輸出和錯誤文件描述符的使用。通常,shell 運行命令時帶文件描述符 0、1 和 2,都是指 shell 的終端。當使用 > 字符指示 shell 將一個程序的輸出發送給另一個程序時,shell 在調用新程序之前打開該文件作為文件描述符 1。這將導致程序將它的輸出發送給指定的文件而不是用戶終端;其妙處是,對于程序本身,這是透明的!

與之類似,"<" 字符指示 shell 使用特定的文件作為文件描述符 0。這樣就強迫程序從該文件中讀取它的輸入;這兩種情況下,任何來自程序的錯誤仍將出現在終端上,如同它們在文件描述符 2 的情況下發送給標準錯誤一樣。(在 "bash" shell 中,可以使用 2> 而不是 > 將標準錯誤重定向)。這種類型的文件重定向是 linux 命令行最強大的特性之一。

使用任何與文件相關的系統調用之前,程序應該包括 <fcntl.h> 和 <unistd.h>;它們為最普遍的文件例程提供了函數原型和常數。在下面的示例代碼中,我們假設每個程序開始處都有

#include <fcntl.h>
#include <unistd.h>

首先,讓我們了解如何讀寫文件。憑直覺就可以知道,read() 和 write() 系統調用是執行這些操作的最常用方法。這兩種系統調用將有三個自變量:要訪問的文件描述符、指向要讀寫的信息的指針以及應該讀寫的字符數。返回成功讀寫的字符數。清單 1 說明了一個簡單的程序,它從標準輸入(文件描述符 0)中讀取一行,并將它寫入標準輸出(文件描述符 1):

清單 1:

void main(void) {
char buf[100];
int num;

num = read(0, buf, sizeof(buf));
write(1, "I got: ", 7); /* Length of "I got: " is 7! */
write(1, buf, num);
}

關于這個處理有兩個值得注重的問題。首先,我們要求 read() 返回 100 個字符,但假如我們運行這個程序,只有在用戶按下了 "enter" 鍵以后才能獲得輸入。許多文件操作都根據最佳效果工作:它們嘗試返回程序要求的所有信息,但只有部分能夠成功。缺省情況下,終端配置成一旦存在 "
" 或新行符(通過按 "enter" 鍵產生)時,就從 read() 調用返回。這實際上非常方便,因為大多數用戶都希望程序無論如何都是面向行的。但常規數據文件并非如此,假如依靠它就可能產生不可預料的結果。


另一個要注重的問題是我們不必在顯示輸出后寫一個
。read() 調用給了我們來自用戶的
,只將那個
通過 write() 寫回標準輸出。假如您希望在沒有新行符的情況下看到發生的事件,嘗試將最后一行改為

write(1, buf, num - 1);

有關這個簡單示例的最后一點:buf 絕對不包含實際的 C 字符串。C 字符串由標記字符串結束的單一 字符終止。因為 read() 不將 添加到緩沖區的結尾,在 read() 上使用 strlen()(或任何其它 C 字符串函數)將可能鑄成大錯!這種行為可以讓 read() 和 write() 對包括 字符的數據處理,而這對于一般字符串函數來說是不可能的。

read() 和 write() 系統調用可以對絕大多數文件起作用。但它們不對目錄起作用,目錄應該通過非凡函數(例如 readdir())來訪問。另外,read() 和 write() 對于某些類型的套接字也不起作用。

某些文件,例如常規文件和塊設備文件,使用文件指針的概念。它指定在文件中,下一個 read() 調用從哪里讀取,下一個 write() 調用從哪里寫入。read() 或 write() 后,文件指針隨著已處理的字符數(在內部,通過內核)增加。這樣,使用單一循環就可以方便地讀取文件中的所有數據。清單 2 就是示例:

清單 2:

char buffer[1024];
while ((num = read(0, buffer, 1024))) {
");
}



這個循環將讀取標準輸入上的所有數據,自動在每次讀取后增加內核的內部文件指針。當文件指針處于文件結尾時,read() 將返回 0 并退出循環。某些文件(例如字符設備 -- 終端就是很好的一例)本身沒有文件指針,所以對于這一點,該程序將繼續運行,直到用戶提供文件結束標記(通過按 "Ctrl-D")為止。

到現在為止,我們已經知道如何讀寫文件了,下一步要學習如何打開一個新文件。打開不同類型的文件有不同方法;我們將在這里討論的方法是通過路徑名打開在文件系統中表示的文件;包括常規文件、目錄、設備文件和指定的管道。某些套接字文件有路徑名,那些必須通過替代方法打開。

撇開放棄權利的,open() 系統調用可以讓程序訪問大多數系統文件。open() 是個不平常的系統調用,因為它獲取兩個或者三個自變量:

int open(const char *
pathname,
int flags);

或者,
int open(const char *
pathname,
int flags,
int perm);

第一種形式更普遍一些;它打開一個已存在的文件。第二種格式應該在需要創建文件時使用。第三個自變量指定應該給予新文件的訪問權限。

open() 的第一個參數是以正常 C 字符串表示的全路徑名(即以 終止)。第二個參數指定文件應該如何打開,并包括邏輯“與”操作的一個或多個以下標志:

O_RDONLY:文件可以只讀
O_RDWR:文件可以讀寫
O_APPEND:文件可以讀或附加
O_CREAT:假如文件還不存在則應該創建
O_EXCL:假如文件已存在,失敗而不是創建它(只應該使用 O_CREAT)
O_TRUNC:假如文件已存在,從中除去所有數據(與創建新文件類似)

open() 的第三個參數只在使用 O_CREAT 時需要;它指定了以數字表示的文件許可權(格式與 chown 命令的數值許可權自變量的格式相同。為 open() 指定的許可權受用戶的 umask 影響,后者答應用戶指定一系列新文件應該獲得的缺省許可權。大多數創建文件的程序都使用第三個自變量 0666 調用 open(),可以讓用戶通過 umask 來控制程序的缺省許可權。(大多數 shell 的 umask 命令都可以更改它。)

例如,清單 3 顯示了如何為進行讀寫打開文件、假如它不存在則創建,以及廢棄其中的數據:

清單 3:

int fd;
fd = open("myfile", O_RDWR O_CREAT O_TRUNC, 0666)
if (fd < 0) {
/* Some error occurred */
/* ... */
}

open() 返回引用文件的文件描述符。回憶一下,文件描述符總是 >= 0。假如 open() 返回了一個負值,就表示發生了錯誤,全局變量錯誤號包含了描述問題的 Unix 錯誤代碼。open() 總盡量返回最小數,假如沒有使用文件描述符 0,open() 將總返回 0。

進程帶文件結束時,它應該通過 close() 系統調用關閉它,該系統調用的格式為:

int close(int fd);

close 的文件描述符是傳遞給 close() 的唯一自變量,在成功情況下返回 0。盡管 close() 失敗的情況比較少見,但假如文件描述符引用的是遠程服務器上的文件,系統無法正確清空它的高速緩存,close() 就可能真的失敗。進程終止時,內核自動關閉所有還在打開的文件。

最后的一個常見文件操作是移動文件指針。這(自然)只對帶文件指針的文件有意義,假如嘗試在不恰當的文件上嘗試該操作就會返回錯誤。lseek() 系統調用用于以下目的:

off_t lseek(int fd, off_t pos, int whence);

off_t 類型是表達 longint (long 就是 lseek 中 "l" 的來歷)的一種別致方法。lseek() 返回相對于文件開始處文件指針的最終位置,假如有錯誤,則返回 -1。這個系統調用希望被移動的文件指針所屬的文件描述符作為第一個自變量,將它移動到文件中的位置作為第二個自變量。最后一個自變量描述文件指針的移動方式。


SEEK_SET 將它移動到從文件開始算起的 pos 字節。
SEEK_END 將它移動到從文件結尾算起的 pos 字節。
SEEK_CUR 從它當前位置開始向文件結尾移動 pos 字節。

open()、close()、write()、read() 和 lseek() 的組合為 linux 提供了基本的文件訪問 API。雖然還有許多其它操縱文件的函數,但這里描述的是最常用的。

大多數程序員都使用熟悉的 ANSI C 庫文件函數,例如 fopen() 和 fread(),而不是在此描述的低級系統調用。可以預見到,fopen() 和 fread() 是在用戶級別庫中這些系統調用的基礎上實現的。仍然會經常看到低級系統調用的使用,非凡是在更復雜的程序中。通過熟悉這些例程和接口,您就可以成為一個真正的 Unix 黑客了。

關于作者
Erik Troan 是 Red Hat Software 的開發者,linux
application Development 一書的作者之一。可以通過 ewt@redhat.com 與他聯系。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 安多县| 安龙县| 勃利县| 龙海市| 双辽市| 灵璧县| 临城县| 苗栗市| 乌拉特前旗| 广南县| 凯里市| 曲靖市| 白银市| 靖西县| 九江市| 察雅县| 梅河口市| 阿拉善左旗| 安溪县| 贵德县| 淮安市| 新和县| 永新县| 依兰县| 灵璧县| 琼海市| 瑞金市| 老河口市| 鄄城县| 石景山区| 胶州市| 新巴尔虎右旗| 灵璧县| 沧源| 贵州省| 五峰| 新巴尔虎右旗| 宣化县| 图片| 元氏县| 遂川县|