檔案是linux中的一個重要概念。在Linux中,一切(幾乎一切)都是檔案。簡單的說,C中基本的的printf()函數,scanf()函數,其實都屬於檔案操作。
對於檔案操作,雖然都是通過函數調用的方式實現,卻還是能分為兩類:系統調用和庫函數。
這篇文章將先介紹linux中檔案的概念,系統調用和庫函數的概念 ,然後具體的討論兩種方式下的檔案操作。
博文的主要內容如下:
- Linux 中的檔案
- 檔案訪問-庫函數
- 檔案訪問-系統調用
- 庫函數
- 標準 I/O 庫
- /proc檔案系統
1 Linux中的檔案
1.1概念
按照普通的定義,檔案不過是一堆資料,在往下說,就是儲存空間中的0101。。。而我們這裡討論的檔案有了更廣的定義。對於Linux中的檔案,我的理解是:
Linux中的檔案具有的特點是:可通過作業系統或者程式對外提供資訊,也能對內輸入資訊,可以被建立,刪除。
Linux中,檔案有特別重要的意義,他們為作業系統和裝置提供了一個簡單而統一的介面。在Linux中,幾乎一切都可以看做是檔案 。
這就意味著,普通程式完全可以像使用檔案(普通定義)那樣使用磁碟檔案、串列口、印表機和其他裝置。
硬體裝置在linux作業系統中也被表示為檔案。例如,可以通過如下命令把cd-rom磁碟機掛載為一個檔案,
#mount -t iso9660 /dev/hdc /mnt/cdrom
#cd /mnt/rom
然後,就能像訪問普通檔案那樣在cd-rom目錄中漫遊。
1.2操作
和操作一般意義上的檔案一樣,linux中對檔案的操作只需要五個基本的函數:
open、close、read、write和ioctl
通過調用這幾個函數就能對linux中的檔案進行讀、寫等操作。不過,這種操作又分為系統調用和庫函數調用。簡單的說,系統調用是最直接的方式,
庫函數調用最終也是通過系統調用實現的。可認為庫函數調用是對系統調出於效率考慮而做出的最佳化。
庫函數調用和系統調用的區別和聯絡請參看:linux系統調用和庫函數調用的區別
我們用很少的函數就可以對檔案和裝置進行訪問和控制。這些函數就是所謂的系統調用,由作業系統直接提供,他們是通向作業系統本身的介面。
作業系統的核心部分,既核心,其實就是一組裝置驅動程式。這是一些對硬體進行控制的介面。
2 檔案訪問-系統調用
通過系統調用來訪問檔案是最直接的方式。系統調用函數直接作用於作業系統核心的裝置驅動程式從而實現檔案訪問。
2.1 檔案描述符
在系統中需要處理的檔案(讀、寫操作)需要一個標識,以便在其它地方能識別出這個檔案,於是就產生了檔案描述符。檔案描述符是一些小值整數,簡單的說就是
一個檔案ID用於在系統中唯一的標識檔案。檔案描述符的總數也就是系統可以開啟檔案的最多個數,這取決於系統的配置情況。
當開始運行程式時,也就是系統開始運行時,它一般會有三個已經開啟的檔案描述符。他們是:
其它檔案的檔案描述符,在調用檔案開啟函數open時返回。這就是說,每個裝置對應著一個檔案描述符。檔案描述符由作業系統分配,每次分配最小的。
2.2 write系統調用
write,就是把緩衝區的資料寫入檔案中。注意,這裡的檔案時廣泛意義的檔案,比如寫入磁碟、寫入印表機等等。
Linux 中write()的函數原型:
size_t write(int fildes, const void *buf, size_t nbytes);
參數說明:
fildes:檔案描述符,標識了要寫入的目標檔案。例如:fildes的值為1,就像標準輸出寫資料,也就是在顯示屏上顯示資料;如果為 2 ,則想標註錯誤寫資料。
*buf:待寫入的檔案,是一個字串指標。
nbytes:要寫入的字元數。
函數傳回值:size_t 返回成功寫入檔案的字元數。需要指出的是,write可能會報告說他寫入的位元組比你所要求的少。這並不一定是個錯誤。在程式中,你需要檢查
error已發現錯誤,然後再次調用write寫入剩餘的資料。
請看下面的例子:
運行結果:
這個程式只在標準輸出上顯示一條訊息。
read系統調用
系統調用read是從檔案中讀出資料。要讀取的檔案用檔案描述符標識,資料讀入一個事先定義好的緩衝區。他返回實際讀入的位元組數。
Linux中read的函數原型:
size_t read(int fildes, void *buf, size_t nbytes);
參數說明:
fildes:檔案描述符,標識要讀取的檔案。如果為0,則從標準輸入讀資料。類似於scanf()的功能。
*buf:緩衝區,用來儲存讀入的資料。
nbytes:要讀取的字元數。
傳回值:size_t返回成功讀取的字元數,它可能會小於請求的位元組數。
運行結果:
open系統調用
系統調用open的作用是開啟一個檔案,並返回這個檔案的描述符。
簡單地說,open建立了一條到檔案或裝置的訪問路徑。如果操作成功,它將返回一個檔案描述符,read和write等系統調用使用該檔案描述符對檔案或
裝置進行操作。這個檔案描述符是唯一的,他不會和任何其他運行中的進程共用。如果兩個程式同時開啟一個檔案,會得到兩個不同的問價描述符。如果
同時對兩個檔案進行操作,他們各自操作,互補影響,彼此相互覆蓋(後寫入的覆蓋先寫入的)為了防止檔案按讀寫衝突,可以使用檔案鎖的功能。這不是
本次重點,以後介紹。
Linux中open的函數原型有兩個:
int open(const char *path, int oflags);
int open(const char *path, int oflags, mode_t mode );
參數說明。
path:準備開啟的檔案或裝置名稱字。
oflags:指出要開啟檔案的訪問模式。open調用必須指定如下所示的檔案訪問模式之一:
open調用哈可以在oflags參數中包括下列可選模式的組合(用”按位或“操作):
- O_APPEDN: 把寫入資料追加在檔案的末尾。
- O_TRUNC: 把檔案長度設為零,丟棄以後的內容。
- O_CREAT: 如果需要,就按參數mode中給出的訪問模式建立檔案。
- O_EXCL: 與O_CREAT一起調用,確保調用者建立出檔案。使用這個模式可防止兩個程式同時建立一個檔案,如果檔案已經存在,open調用將失敗。
關於其他可能出現的oflags值,請看考open的調用手冊。
mode:
當使用哦、O_CREAT標誌的open來建立檔案時,我們必須使用三個參數格式的open調用。第三個參數mode 是幾個標誌按位OR後得到的。他們是:
- S_IRUSR: 讀許可權,檔案屬主。
- S_IWUSR:寫入權限,檔案屬主。
- S_ IXUSR:執行許可權,檔案屬主。
- S_IRGRP:讀許可權,檔案所屬組。
- S_IWGRP:寫入權限,檔案所屬組。
。。。。
請看下面例子:
open("myfile", O_CREAT, S_IRUSR|S_IXOTH ;
他的作用是建立一個名為myfile 的檔案,檔案屬主擁有讀許可權,其他使用者擁有執行許可權,且只有這些許可權。
運行結果:
程式建立了一個名為myfile的檔案,檔案屬主有讀許可權,其他使用者有執行許可權,且只有這些許可權。
close系統調用
close系統調用用於“關閉”一個檔案,close調用終止一個檔案描述符fildes以其檔案之間的關聯。檔案描述符被釋放,並能夠重新使用。
close成功返回1,出錯返回-1.
#Include<unistd.h>
int close(int fildes);
ioctl系統調用
ioctl提供了一個用於控制裝置及其描述符行為和配置底層服務的介面。終端、檔案描述符、甚至磁帶機都可以又為他們定義的ioctl,具體
細節可以參考特定裝置的使用手冊。
下面是ioctl 的函數原型
#include<unistd.h>
int ioctl(int fildes, int cmd,,,,,,);
ioctl對描述符fildes指定的對象執行cmd 參數中所給出的操作。
其他和檔案管理有關的系統調用
還有許多其他的系統調用能對檔案進行操作。
幾個常用的如:lseek()對檔案描述符fildes指定檔案的讀寫指標進行設定,也就是說,它可以設定檔案的下一個讀寫位置。
fstat,stat,lstat 是和檔案描述符相關的函數操作,這裡就不做介紹。
dup,dup2系統調用。dup提供了複製檔案描述符的方法,使我們能夠通過兩個或者更多個不同的檔案描述符來訪問同一個檔案。這可以用於
在檔案的不同位置對資料進行讀寫。
4 庫函數
在輸入、輸出操作中,直接使用系統調用效率會非常底。具體原因有二:
- 系統調用會影響系統效能。與函數調用相比,系統調用的開銷大。因為在執行系統調用的時候,要切換到核心代碼區執行,然後再返回使用者代碼。這必然就需要大量的時間開支。一種解決辦法是:盡量減少系統調用的次數,讓每次系統調用完成儘可能多的 任務。例如每次系統調用寫入大量的字元而不是單個字元。
- 硬體會對系統調用一次能讀寫的資料區塊做一定的限制。例如,磁帶機通常的寫操作資料塊長度是10k,如果縮寫資料不是10k的整數倍,磁帶機還是會以10k為單位繞磁帶,這就在磁帶上留下空隙。
為了提高檔案訪問操作的效率,並且使得檔案操作變得更方便,Linux發行版提供了一系列的標準函數庫。他們是一些由函數構成的集合,你可以在自己的程式方便的中使用它們,
去操作檔案。提供輸出緩衝功能的標準I/O庫就是這樣的例子。你可以高效的寫任意長度的資料區塊,庫函數則在需要的時候安排底層函數調用(系統調用)
也就是說,庫函數在使用者和系統之間,增加了一個中介層。如所示:
庫函數是根據實際需要而封裝好的系統調用,使用者可在程式中方便的使用庫函數,如標準I O庫(稍後會講)
5 標準I/O庫
標準I/O庫及其標頭檔<stdio.h>為底層I/O系統調用提供了一個通用的介面。這個庫現在已經成為ANSI標準C的一部分,而前面所講的系統調用卻不是。
標準I/O庫提供了許多複雜功能的函數,用于格式化輸出和掃描輸入,它還負責滿足裝置的緩衝需求。
在許多方面,使用標準I/O庫和使用底層檔案描述符類似。需要先開啟一個檔案,已建立一個檔案訪問路徑(也就是系統調用中的檔案描述符)
在標準I/O庫中,與檔案描述符對應的叫 流(stream),它被實現為指向結構FILE的指標。
在啟動程式時,有三個檔案流是自動開啟的。他們是:
- stdin: 標準輸入
- stdout: 標準輸出
- stderr: 標準錯誤輸出
下面會介紹一些常用的I/O庫函數:
5.1 fopen函數
fopen函數類似於系統調用中的open函數。和open一樣,它返迴文件的標識符,只是這裡叫做流(stream),在庫函數裡實現為一個指向檔案的指標。
如果需要對裝置的行為進行明確的控制,最好使用底層系統調用,因為這可以避免使用庫函數帶來的一些非預期的副作用,如輸入/輸出緩衝。
函數原型:
#include<stdio.h>
FILE *fopen(const char *filename, const char *mode);
參數說明:
*filename:開啟檔案的檔案名稱
*mode:開啟的方式
r 以唯讀方式開啟檔案,該檔案必須存在。
r+ 以可讀寫方式開啟檔案,該檔案必須存在。
rb+ 讀寫開啟一個二進位檔案,允許讀資料。
rw+ 讀寫開啟一個文字檔,允許讀和寫。
w 開啟唯寫檔案,若檔案存在則檔案長度清為0,即該檔案內容會消失。若檔案不存在則建立該檔案
w+ 開啟可讀寫檔案,若檔案存在則檔案長度清為零,即該檔案內容會消失。若檔案不存在則建立該檔案。
fopen在成功是返回一個非空的FILE *指標。失敗返回NULL
5.2 fread/fwrite函數
fread函數從檔案流中讀取資料,對應於系統調用中的read;fwrite函數從檔案流中寫資料,對應於系統調用中的write
函數原型:
#include<stdio.h>
size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);
參數說明:
*ptr 要讀取資料的緩衝區,也就是要存放讀取資料的地方。
size:指定每個資料記錄的長度。
nitems: 計數,給出要傳輸的記錄個數。
傳回值:成功讀取到資料緩衝區的記錄個數,當到達檔案尾時,他的傳回值可能會消耗與nitems,甚至可以是0
size_t fwrite(const coid *ptr, size_t size , size_t nitimes, FILE *stream);
他從指定的資料緩衝區ptr中把資料寫入檔案流,返回成功寫入的記錄個數。
5.3 fclose函數
fclose函數關閉指定的檔案流stream,這個操作會使所有未寫出的資料都寫出。因為stdio庫函數會對資料進行緩衝,所有調用fclose函數是很重要的。
如果程式需要確保資料已經全部寫出,就應該調用fclose函數。雖然程式正常結束時,也會自動的調用fclose函數,但這樣就不能檢測出調用fclose所產生的錯誤了。
函數原型如下:
#include<stdio,h>
int fclose(FILE *stream);
5.4 fflush函數
fflush函數的作用是把檔案流中所有未寫出的資料全部寫出。 處於效率考慮,在使用庫函數的時候會使用資料緩衝區,當緩衝區滿的時候才進行寫操作。使用fflush函數
可以將緩衝區的資料全部寫出,而不關心緩衝區是否滿。fclose的執行隱含調用了fflush函數,所以不必再fclose執行之前調用fflush。
函數原型:
#include<stdio.h>
int fflush(FILE *stream);
6 /proc檔案系統
Linux將一切看做檔案,硬體裝置在檔案系統中也有相應的條目。/dev目錄中的檔案使用底層系統調用這樣一種特殊方式來訪問硬體。
/proc檔案系統,可以看做是一個特殊的檔案系統,在這個系統中,每個檔案都對應一個獨立的硬體,所以使用者可以通過proc檔案系統像訪問檔案一樣來訪問硬體裝置。
該檔案系統通常表現為/proc 目錄。該目錄中包含了許多特殊檔案以允許對驅動和核心資訊進行高層訪問。
如果你想知道CPU的資訊,核心版本資訊等,就可以通過proc檔案系統。
/proc目錄中的檔案會隨系統的不同而不同。我的電腦上的/proc 中的檔案如下所示:
在多數情況下,直接讀取這些檔案就可以獲得狀態資訊。
6.1 訪問裝置資訊
例如,擷取CPU的資訊:
記憶體使用量資訊(只顯示裡局部~):
每次讀這些檔案的內容時,他們所提供的資訊都會及時更新。所以再讀一次meminfo檔案會得到不同的結果。
由特定核心功能給出的更多資訊可以在proc目錄的子目錄中查到。
6.2 查看核心功能給出的資訊
例如:查看網路通訊端的使用統計:
6.3通過proc查看進程資訊
用ps 命令可得到當前正在啟動並執行進程,每個進程在proc中都有相應的資訊檔,通過查看這個檔案,可以得知進程相關的資訊:
進程2754的當前工作目錄是:/hme/yyl
程式 /bin/su正在運行,還有其他資訊此處不再說明;
修改proc檔案系統內容
例如,系統中所有啟動並執行程式同時開啟的檔案總數是Linux核心的一個參數。
如果我們想要增大這個歌值,則可通過寫同一個檔案來實現。
注意:對proc的寫操作要注意許可權問題,在修改時要小心,不適當的值可能會影響到系統的一運行。
參看資料:Linux 程式設計
如有轉載請註明出處:http://www.cnblogs.com/yanlingyin/
geek_ling、尹雁鈴@ 部落格園 2012-8-04
E-mail:yanlingyin@yeah.net