一、什麼是檔案
在講述檔案操作之前,我們首先要知道什麼是檔案。看到這個問題你可能會感覺到可笑,因為對於用過計算機的人來說,檔案是最簡單不過的概念了,例如乙個文字是乙個檔案,乙個work文件是乙個檔案等。但是在linux中,檔案的概念還遠不止於這些,在linux中,一切(或幾乎一切)都是檔案。檔案包括很多的內容,例如:大家知道的普通檔案是檔案,目錄也是乙個檔案,裝置也是乙個檔案,管道也是乙個檔案等等。對於目錄、裝置這些的操作也可以完全等同於對純文字檔案的操作,這也是linux非常成功的特性之一吧。
二、系統呼叫
1、檔案描述符
檔案描述符是一些小數值,你可以通過它們訪問的開啟的檔案裝置,而有多少檔案描述符可用取決於系統的配置情況。但是當乙個程式開始執行時,它一般會有3個已經開啟的檔案描述符,就是
0:標準輸入
1:標準輸出
2:標準錯誤
那些數學(即0、1、2)就是檔案描述符,因為在linux上一切都是檔案,所以標準輸入(stdin),標準輸出(stdout)和標準錯誤(stderr)也可看作檔案來對待。
2、系統呼叫常用函式
a、open系統呼叫
open函式的原型為:
int open(const char *path, int oflags);
int open(const char *path, int oflags, mode_t mode);
path,是包括路徑的完整檔名,oflags是檔案訪問模式(即是什麼方式開啟檔案,唯讀、只寫還是可讀並可寫等),mode用於設定檔案的訪問許可權。具體的可選引數,可以自己檢視手冊頁,這裡不一一詳述。
open建立了一條到檔案或裝置的訪問路徑,如果呼叫成功,返回乙個可以被read、write等其他系統呼叫的函式使用的檔案描述符,而且這個檔案描述是唯一的,不與任何其他執行中的程序共享,在失敗時返回-1,並設定全域性變數errno來指明失明的原因。
b、write系統呼叫
write函式的原型為:
size_t write(int fildes, const void *buf, size_t nbytes);
write的作用是把緩衝區buf的前nbytes個位元組寫入到檔案描述符fildes關聯的檔案中,返回實際寫入的位元組數。返回0表示沒有寫入任何資料,返回-1表示呼叫中出現了錯誤,錯誤**儲存在errno中。
注:fildes一定要是在open呼叫中返回的建立的檔案描述符,或者是0、1、2等標準輸入、輸出或標準錯誤。
c、read系統呼叫
read函式的原型為:
size_t read(int fildes, void *buf, size_t nbytes);
read系統呼叫的作用是從與檔案描述符相關的檔案裡讀入nbytes個位元組的資料,並把它們放到資料區buf中,返回讀入的位元組數,失敗時返回-1。
d、close系統呼叫
close呼叫的函式原型為:
int close(int fildes);
close函式的作用是終於檔案描述符fildes一其對應的檔案之間的關聯。
e、例子
說了這麼多,我就給出乙個完整的例子吧,就是從乙個資料檔案(裡面有1m個『0』字元)逐個複製到別乙個檔案。檔名為copy_system.c,**如下:
[cpp]view plain
copy
print?
#include
#include
#include
#include
int main()
#include #include #include #include int main()
三、標準i/o庫
有過c程式設計經歷的人都會知道stdio標頭檔案,它就是c語言的標準io庫,在標準io庫中,與底層檔案描述符相對應的是流,它被實現為指向結構file的指標。io庫的函式有很多,為了與前面的內容對應,這裡還是只講與前面四個函式相對應的函式,其他的函式,你可以查一查手冊頁。
a、fopen庫函式
fopen庫函式的原型為:
file* fopen(const char *filename, const char *mode);
它與底層系統呼叫open類似,成功時返回乙個非空指標。失敗時返回null。
b、fread庫函式
fread庫函式的原型為:
size_t fread(void *ptr, size_t size, size_t nitems, file *stream);
它與底層呼叫read相似,其作用是從stream讀取nitems個長度為size的資料到ptr所指向的緩衝區中。返回值是成功讀到緩衝區中的記錄個數。
注:stream為用fopen函式返回的檔案結構指標。
c、fwrite庫函式
fwrite庫函式的原型:
size_t fwrite(const void *ptr, size_t size, size_t nitems, file *stream);
它與底層呼叫write相似,其作用是從ptr指向的緩衝區中讀取nitems個長度為size到資料,並把它們寫到stream所對應的檔案中。
d、fclose庫函式
fclose庫函式的原型為:
int fclose(file *stream);
它與系統呼叫close相似,其作用是關閉指定的檔案流stream。例子
同樣地,下面是前乙個例子的另乙個實現版本,它實現的功能與先前的例子一樣,不過使用的是標準i/o庫,而不是系統呼叫,檔名為copy_stdio.c**如下:
[cpp]view plain
copy
print?
#include
#include
int main()
#include #include int main()
當然這裡你也可以用其他的庫函式來完成工作,如:用fgetc代替fread,用fputc代替fwrite等。
四、檔案描述符和檔案流的關係
每個檔案流都對應乙個底層檔案描述符,你可以把底層輸入輸出操作與高層檔案流操作混合使用,但是一般不要這樣做,因為資料緩衝的後果難以預料。我們可以通過呼叫fileno函式(原型為:int fileno(file *stream))來確定檔案流使用的底層檔案描述符,它返回指向檔案流的檔案描述符。相反地,你可以通過呼叫函式fdopen(原型為file* fdopen(int fildes, const char* mode))來在乙個已經開啟的檔案描述符上建立乙個新的檔案流,mode引數與fopen函式的完全一樣,同時它必須符合該檔案在最初開啟時所設定的訪問模式。
但是在linux下的程式設計,系統呼叫用得比較多一些,因為很多時候系統呼叫能提供更多的靈活性和更加強大的功能,有些操作是一定要使用系統呼叫,例如,建立檔案讀寫鎖時就一定要使用系統呼叫。
五、系統呼叫與標準i/o的效能比較
就拿本例子中的**來比較,兩個例子編譯後生成的可執行檔案的檔名分別為:copy_system.exe和copy_stdio.exe,在linux下用time命令來測試其執行時間如下:
從測試結果可以看出,系統呼叫的效率比庫函式要低很多。為什麼呢?
因為使用系統呼叫會影響系統的效能。與函式呼叫相比,系統呼叫時,linux必須從執行使用者**切換到執行核心**,然後再返回使用者**,所以系統呼叫的開銷要比普通函式呼叫大一些。然而也是有辦法減少這種開銷的,就是在程式中儘量減少系統呼叫的的次數,並且讓每次系統呼叫完成盡量多的工作。
而庫函式為什麼做同樣的事情效率卻會高這麼多呢?這是因為庫函式在資料滿足資料塊長度(或buffer長度)要求時才安排執行底層系統呼叫,從而減少了系統呼叫的次數,也讓每次的系統呼叫做了盡量多的事情,所以效率就比較高。
六、提高系統呼叫的簡單方法舉例
用回每乙個例子(coy_system.c)的**,略加修改就能提高我們的效率,例如一次讀1024個位元組,修改後儲存檔名為copy_system2.c,**如下:
[cpp]view plain
copy
print?
#include
#include
#include
#include
int main()
#include #include #include #include int main()
生成的可執行檔案為copy_system2.exe,使用time命令檢視其執行時間,如下:
比較下可以看出,其效能改善了乙個數量級,其效率甚至比用庫函式乙個乙個字元複製來來得高效,至少在我的機子上是這樣。
Linux 檔案操作 系統呼叫和標準I O庫
分類 linux 2013 08 19 00 18 705人閱讀收藏 舉報linux c程式設計檔案操作 系統呼叫複製 一 什麼是檔案 在講述檔案操作之前,我們首先要知道什麼是檔案。看到這個問題你可能會感覺到可笑,因為對於用過計算機的人來說,檔案是最簡單不過的概念了,例如乙個文字是乙個檔案,乙個wo...
Linux 檔案操作 系統呼叫和標準I O庫
一 什麼是檔案 在講述檔案操作之前,我們首先要知道什麼是檔案。看到這個問題你可能會感覺到可笑,因為對於用過計算機的人來說,檔案是最簡單不過的概念了,例如乙個文字是乙個檔案,乙個work文件是乙個檔案等。但是在linux中,檔案的概念還遠不止於這些,在linux中,一切 或幾乎一切 都是檔案。檔案包括...
Linux 檔案操作 系統呼叫和標準I O庫
一 什麼是檔案 在講述檔案操作之前,我們首先要知道什麼是檔案。看到這個問題你可能會感覺到可笑,因為對於用過計算機的人來說,檔案是最簡單不過的概念了,例如乙個文字是乙個檔案,乙個work文件是乙個檔案等。但是在linux中,檔案的概念還遠不止於這些,在linux中,一切 或幾乎一切 都是檔案。檔案包括...