1.什麼是無緩衝I/O
我們首先要知道Linux作業系統提供給我們的使用者程式調用的介面,除了使用庫函數,還可以直接使用系統調用。而今天我要介紹的相關函數就是直接調用了核心的系統調用,所以說它是無緩衝的,它跟標準的I/O函數相對應。
2.開啟或者建立檔案的函數:open()
要操作相關檔案,就必須獲得該檔案的控制代碼,專業點叫檔案描述符。它是進程在開啟一個檔案或者建立一個檔案時,核心返回給該進程的一個唯一的非負的整數,且一定是當前可以的描述符中最小的一個數。進程獲得這個檔案描述符後,就可以根據它來告訴read()或者write相關函數具體來操作哪個檔案了。
Tips:通常系統對一個進程可開啟的檔案描述符數量有一個預設值,可以使用命令ulimit -f查看,也可以使用ulimit命令重新設定該值。
通常,系統有3個已經預設的檔案描述符,即0代表標準輸入,1表示標準輸出,2表示標準錯誤,在unistd.h標頭檔中,把他們定義為常量,分別是STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。
open()函數為我們提供了該檔案描述符:
#include<fcntl.h>
int open( const char * pathname,int flags, mode_t mode);
pathname:即是我們要開啟或者建立檔案的名字,可以是相對路徑,也可以是絕對路徑。
flags:重點介紹一下,它的取值比較多,在<fcntl.h>標頭檔中定義了很多常量:
O_RDONLY:即以唯讀方式開啟該檔案。
O_WRONLY:即以唯寫的方式開啟該檔案。
O_RDWR:即以讀、寫的方式開啟該檔案。
以上這三個常量,flags必選選擇其一,且只能選擇其一。還有幾個可選常量可以跟這三個之一組合使用,以達到不同的效果:
O_CREAT:若欲開啟的檔案不存在則自動建立該檔案.
O_TRUNC :若檔案存在並且以可寫的方式開啟時,此旗標會令檔案長度清為0,而原來存於該檔案的資料也會消失。
O_APPEND :當讀寫檔案時會從檔案尾開始移動,也就是所寫入的資料會以附加的方式加入到檔案後面。
O_EXCL 如果O_CREAT 也被設定,此指令會去檢查檔案是否存在。檔案若不存在則建立該檔案,否則將導致開啟檔案錯誤。
比如,我們可以使用open("./filename" ,O_WRONLY|O_CREAT,mode),以寫方式開啟filename,如果filenam不存在則建立它。
再如,open("./filename" ,O_WRONLY|TRUNC),就會將原來filename中的內容清除掉,從新寫入新的值。
還如,open("./filename" ,O_WRONLY|O_CREAT|O_EXCL),可以測試filename是否存在,存在則報錯,不存在則建立filename.
當然flags還有幾個其他的常量取值,就不一一介紹了,可以自己去查手冊。
mode:僅當建立一個新檔案需要指定該參數,該參數的意思是說檔案以一個什麼樣的許可權建立。
open()調用成功後將返迴文件描述符,出錯則返回-1。
系統還提供一個專門用來建立檔案的函數create():
#include<fcntl.h>
int creat(const char * pathname, mode_tmode);
其相當於open("pathname",O_WRONLY|O_CREAT|O_TRUNC,mode)。該函數有個不好之處是,只能以寫的方式開啟檔案。有了open(),我們可以這樣定義:
open("pathname",O_RDWR|O_CREAT|O_TRUNC,mode)
3.讀函數read(),寫函數write()
#include<unistd.h>
ssize_t read(int fd,void * buf ,size_t count);
read()函數從fd中將指定大小count的位元組讀入buf中(注意,也可能讀少於count的位元組數,如到達了檔案尾部,或者是特殊的檔案)。成功返回讀到的位元組數,遇到結尾則返回0,失敗返回-1。
#include <unistd.h>
ssize_t write (int fd,const void * buf,size_t count);
write函數向fd中當前位移量後面寫入count大小位元組的buf內容。如果成功返回寫入的位元組數大小,如果失敗則返回-1.
4.設定檔案位移量的函數lseek()
每個開啟的檔案都有一個“當前檔案位移量”,在read(),write()的時候就可以根據該位置開始讀寫。使用lseek()則改變該位置。
#include<unistd.h>
off_t lseek(int fildes,off_t offset ,int whence);
offset即指要相對whence便宜多少位元組。whence不同的值使offset的意思不一樣:
SEEK_CUR:將位移量設定為當前位移量加offset個位元組。
SEEK_SET:將位移量設定為檔案起始處位移offset個位元組。
SEEK_END:將位移量設定為檔案末尾加offset個位元組。
有個小技巧,如果要獲得該檔案當前檔案位移量,怎麼辦呢?可以這樣:currPos = lseek(fd,0,SEEK_CUR);
注意:當前檔案位移量不一定是非負的值,也可能是負值。所以判斷lseek是否失敗一定要測試其結果是否為-1,而不是小於0。
如果設定位移量大於檔案長度時,檔案中將形成“空洞”,這種“空洞”並不佔有磁碟的空間,它讀取出來表現是0。
5.一個copy程式
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFFER 4096
int main(int argc,char *argv[])
{
int n;
char buf[BUFFER];
int fd1 = open(argv[1],O_RDONLY);
int fd2 = open(argv[2],O_WRONLY);
while( (n=read(fd1,buf,BUFFER))>0 )
{
if( (write(fd2,buf,n))!=n )
{
printf("error\n");
exit(1);
}
}
close(fd1);
close(fd2);
exit(0);
}