大家都知道,在Linux下,一切皆檔案,因此熟悉Linux中的檔案操作,是linux下編程的基礎。廢話少說,進入主題。
在linux下用檔案描述符來表示裝置檔案盒普通檔案,檔案描述符是一個整型的資料,所有對檔案的操作都是通過檔案描述符來實現的。
檔案描述符是檔案系統中串連使用者空間和核心空間的樞紐,當我們開啟一個或者建立一個檔案時,核心空間會建立相應的結構,並且生
成一個整型的變數傳遞給使用者空間的對應進程,而進程則用這個檔案描述符來對檔案進行操作。要注意的是,檔案描述符是一個有限的
資源,因此,在使用完畢後要及時釋放,一般是調用close()函數來關閉的。在linux系統中有3個已經分配好的檔案描述符,那就是標準
輸入,標準輸出和標準錯誤,他們的檔案描述符分別為0,1,2.
1.open() ,create()函數
在linux下,open()函數用於開啟一個已經存在的檔案或者建立一個新檔案,而create()函數用於建立一個新的檔案:
int open(const char * pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
其中,flags為使用者佈建 的標準,其可能值為:O_RDONLY(唯讀),O_WRONLY(唯寫),O_RDWR(讀寫),O_APPEND(追加)
O_CREAT(不在則建立)等等,這裡壓迫特別注意的是,當我們選擇了O_CREAT時,則要在mode參數中設定新檔案的許可權。
pathname為檔案路徑
mode參數用於表示開啟檔案的許可權,它必須與flags的O_CREAT結合使用。
int create(const char *parhname,mode_t mode);
而create函數相當於open的縮寫版本:open(parhname,O_WRONLY|O_CREAT|O_TRUNC,mode);
2.讀取檔案read函數
ssize_t read(int fd,void *buf,size_t count)
read函數是負責從fd中讀取count位元組內容並且放在buf開始的緩衝區.當讀成功時,檔案對應的讀取位置指標向後移動位置,移動的大小為
成功讀取的位元組數,read返回實際所讀的位元組數,如果返回的值是0 表示已經讀到檔案的結束了,小於0表示出現了錯誤。
3.寫檔案write()函數
ssize_t write(int fd, const void*buf,size_t count);
write函數將buf中的count位元組內容寫入檔案描述符fd.成功時返回寫的位元組數.失敗時返回-1. 並設定errno變數.
4檔案位移函數lseek()
每個開啟的檔案都有一個與其相關聯的“當前檔案位移量”。通常,讀、寫操作都從當前檔案位移量處開始,並使位移量增加所讀寫的位元組數。
按系統預設的情況,當開啟一個檔案時,除非指定O_APPEND選項,否則該位移量被設定為0。可以調用lseek顯式地為一個開啟的檔案設
置起位移量。 #include <unistd.h> off_t lseek(int filedes, off_t offset, int whence);
傳回值:成功返回新的檔案位移量,出錯返回-1.對參數offset的解釋與參數whence的值有關。 若是SEEK_SET,則將該檔案的位移量設定為距檔案開始處offset個位元組。 若是SEEK_CUR,則將該檔案的位移量設定為當前值加offset,offset可為正或負。 若是SEEK_END,則將該檔案的位移量設定為檔案長度加offset,offset可為正或負。
可以用下列方式確定開啟檔案的當前位移量: off_t currpos; currpos = lseek(fd, 0, SEEK_CUR);
這種方法也可用來確定所涉及的檔案是否可以設定位移量。如果檔案描述符引用的是一個管道、FIFO或網路通訊端,則lseek返回-1,並將
errno設定為ESPIPE。通常,檔案的當前位移量應當是一個非負整數,但是,某些裝置也可能允許負的位移量。但對於普通檔案,則其位移
量必須是非負值。因為位移量可能是負值,所有在比較lseek的傳回值時應當謹慎,不要測試它是否小於0,而要測試它是否等於-1。
lseek僅將當前的檔案位移量記錄在核心中,它並不引起任何I/O操作。
檔案位移量可以大於檔案的當前長度,在這種情況下,對該檔案的下一次寫將加長該檔案,並在檔案中構成了一個空洞。位於檔案中但沒
有寫過的位元組都被讀為0。檔案中的空洞並不要求在磁碟上佔用儲存區。
5.擷取檔案狀態fstat()函數
#include <sys/stat.h>
int stat(const char*pathname,struct stat*buf);
int fstat(int filedes,struct stat*buf);
int lstat(const char *pathname,struct stat*buf);
這三個函數的返回:若成功為0,出錯為-1。 給予一個pathname,stat函數返回一個與此命名檔案有關的資訊結構,fstat函數獲
得已在描述符filedes上開啟的檔案的有關資訊。 lstat函數類似於stat,但是當命名的檔案是一個符號串連時,lstat返回該符號串連的有關
資訊,而不是由該符號串連引用的檔案的資訊。
第二個參數是個指標,它指向一個我們應提供的結構。這些函數填寫由buf指向的結構。該結構的實際定義可能所實施而有所不同,但其基本形式是:
struct stat{
mode st_mode; /*檔案類型和方式(許可數)*/
ino st_ino;/* i-節點號(序號)*/
dev st_dev;/*裝置號(檔案系統)*/
dev st_rdev;/*特殊檔案的裝置號*/
nlink st_nlink;/*串連數*/
uid st_uid;/*屬主的使用者ID*/
gid st_gid;/*屬主的組ID*/
off st_size;/*普通檔案的位元組長度*/
time st_atime;/*最後存取時間*/
time st_mtime;/*最後修改存取時間*/
time st_ctime;/*最後檔案狀態更改時間*/
long st_blksize;/*最佳I/O塊長*/
long st_blocks;/*分配的512位元組塊塊數
};
6.檔案空間映射mmap()函數
void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize);
該函數主要用途有三個:
1、將一個普通檔案對應到記憶體中,通常在需要對檔案進行頻繁讀寫時使用,這樣用記憶體讀寫取代I/O讀寫,以獲得較高的效能;
2、將特殊檔案進行匿名記憶體映射,可以為關聯進程提供共用記憶體空間;
3、為無關聯的進程提供共用記憶體空間,一般也是將一個普通檔案對應到記憶體中。
mmap系統調用使得進程之間通過映射同一個普通檔案實現共用記憶體。普通檔案被映射到進程地址空間後,進程可以像訪問普通記憶體一樣對檔案
進行訪問,不必再調用read(),write()等操作。
參數start:指向欲映射的記憶體起始地址,通常設為 NULL,代表讓系統自動選定地址,映射成功後返回該地址。
參數length:代表將檔案中多大的部分映射到記憶體。
參數prot:映射地區的保護方式。可以為以下幾種方式的組合:
PROT_EXEC 映射地區可被執行
PROT_READ 映射地區可被讀取
PROT_WRITE 映射地區可被寫入
PROT_NONE 映射地區不能存取
參數flags:影響映射地區的各種特性。在調用mmap()時必須要指定MAP_SHARED 或MAP_PRIVATE。
MAP_FIXED 如果參數start所指的地址無法成功建立映射時,則放棄映射,不對地址做修正。通常不鼓勵用此旗標。
MAP_SHARED對映射地區的寫入資料會複製迴文件內,而且允許其他映射該檔案的進程共用。
MAP_PRIVATE 對映射地區的寫入操作會產生一個對應檔的複製,即私人的“寫入時複製”(copy on write)對此地區作的任何修改都不會寫
回原來的檔案內容。
MAP_ANONYMOUS建立匿名映射。此時會忽略參數fd,不涉及檔案,而且映射地區無法和其他進程共用。
MAP_DENYWRITE只允許對映射地區的寫入操作,其他對檔案直接寫入的操作將會被拒絕。
MAP_LOCKED 將映射地區鎖定住,這表示該地區不會被置換(swap)。
參數fd:要映射到記憶體中的檔案描述符。如果使用匿名記憶體映射時,即flags中設定了MAP_ANONYMOUS,fd設為-1。有些系統不支援匿名記憶體
映射,則可以使用fopen開啟/dev/zero檔案,然後對該檔案進行映射,可以同樣達到匿名記憶體映射的效果。
參數offset:檔案對應的位移量,通常設定為0,代表從檔案最前方開始對應,offset必須是分頁大小的整數倍。
傳回值:
若映射成功則返回映射區的記憶體起始地址,否則返回MAP_FAILED(-1),錯誤原因存於errno 中。
系統調用mmap()用於共用記憶體的兩種方式:
(1)使用普通檔案提供的記憶體映射:
適用於任何進程之間。此時,需要開啟或建立一個檔案,然後再調用mmap()
典型調用代碼如下:
fd=open(name, flag, mode);
if(fd<0)
.........
ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);
..........
munmap(ptr,len);
close(fd);
當對檔案的操作完畢後,需要使用munmap()函數將mmap()映射的地址取消並關閉開啟的檔案.
(2)使用特殊檔案提供匿名記憶體映射:
適用於具有親緣關係的進程之間。由於父子進程特殊的親緣關係,在父進程中先調用mmap(),然後調用 fork()。那麼在調用fork()之後,
子進程繼承父進程匿名映射後的地址空間,同樣也繼承mmap()返回的地址,這樣,父子進程就可以通過映射區 域進行通訊了。注意,這裡
不是一般的繼承關係。一般來說,子進程單獨維護從父進程繼承下來的一些變數。而mmap()返回的地址,卻由父子進程共同維護。 對於具
有親緣關係的進程實現共用記憶體最好的方式應該是採用匿名記憶體映射的方式。此時,不必指定具體的檔案,只要設定相應的標誌即可
7.檔案屬性fcntl()函數
fcntl()函數向開啟的檔案fd發送命令,更改其屬性。函數原型如下:
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd,int cmd, struct flock *lock);
操作成功,其傳回值依賴於cmd,出錯返回-1。以下命令有特殊傳回值:
F_DUPFD 傳回值為新的檔案描述符
F_GETFD 傳回值為獲得的相應標誌
F_GETFL 傳回值為檔案描述符的狀態標誌
F_GETOWN 傳回值如果為正數則是進程ID號,如果為負數則是進程組ID號
函數fcntl()的功能分為以下6類:
(1) 複製檔案描述符(cmd=FD_DUPFD)
(2) 獲得/設定檔案描述符(cmd=F_GETFD或者F_SETFD)
(3) 獲得/設定檔案狀態值(cmd=F_GETFL或者F_SETFL)
(4) 獲得/設定訊號發送對象(cmd=F_GETLEASE、F_SETOWN、F_GETSIG或者F_SETSIG)
(5) 獲得/設定檔案記錄鎖(cmd=F_GETLK或者F_SETLK或者F_SETLKW)
(6) 獲得/設定檔案租約(cmd=F_GETLEASE或者F_SETLEASE)
8. 檔案輸入輸出控制ioctl()函數
Ioctl是input output control的簡寫,表示輸入輸出控制,ioctl()函數通過對檔案描述符的發送命令來控制裝置。
函數原型如下:
#include <sys/ioctl.h>
int ioctl(int d, int request,…);