文件IO編程
一、系統(tǒng)調(diào)用及API
1、 系統(tǒng)調(diào)用
所謂系統(tǒng)調(diào)用就是指操作系統(tǒng)提供給用戶程序調(diào)用的一組“特殊”接口,用戶程序可以通過(guò)這組“特殊”接口來(lái)獲得操作系統(tǒng)內(nèi)核提供的服務(wù),例如,用戶可以通過(guò)進(jìn)程控制相關(guān)的系統(tǒng)調(diào)用來(lái)創(chuàng)建進(jìn)程、實(shí)現(xiàn)進(jìn)程調(diào)度、進(jìn)程管理等
為了更好地保護(hù)內(nèi)核空間(就是為什么用戶程序不能直接訪問(wèn)系統(tǒng)內(nèi)核提供的服務(wù)),將程序的運(yùn)行分為內(nèi)核空間和用戶空間,它們運(yùn)行在不同的級(jí)別上,在邏輯上是相互隔離的。
2、 用戶編程接口(API)
系統(tǒng)調(diào)用不是直接與程序員進(jìn)行交互的,它僅僅是一個(gè)通過(guò)軟中斷機(jī)制向內(nèi)核提交請(qǐng)求,以獲取內(nèi)核服務(wù)的接口,實(shí)際使用中,程序員調(diào)用的通常是API函數(shù),API遵循了在UNIX中最流行的應(yīng)用編程界面標(biāo)準(zhǔn)——POSIX標(biāo)準(zhǔn)。該標(biāo)準(zhǔn)描述了操作系統(tǒng)的系統(tǒng)調(diào)用編程接口(實(shí)際上就是API),用于保證應(yīng)用程序可以在源代碼一級(jí)上在多種操作系統(tǒng)上移植運(yùn)行,這些系統(tǒng)調(diào)用接口主要是通過(guò)C庫(kù)來(lái)實(shí)現(xiàn)的。
3、 系統(tǒng)命令
系統(tǒng)命令相對(duì)API更高了一層,實(shí)際上是一個(gè)可執(zhí)行程序(如shell系統(tǒng)命令),它的內(nèi)部引用了用戶編程接口(API)來(lái)實(shí)現(xiàn)相應(yīng)的功能。
二、文件及文件描述符
在linux中對(duì)目錄和設(shè)備的操作都等同于文件的操作,linux中文件分為普通文件、目錄文件、鏈接文件、設(shè)備文件四種。
內(nèi)
核通過(guò)文件描述符區(qū)分和引用特定的文件,所有設(shè)備和文件的操作都使用文件描述符來(lái)進(jìn)行的,它是一個(gè)非負(fù)的整數(shù),是一個(gè)索引值,并指向內(nèi)核中每個(gè)進(jìn)程打開文
件的記錄表,當(dāng)打開一個(gè)現(xiàn)存文件或創(chuàng)建一個(gè)新文件時(shí),內(nèi)核就向進(jìn)程返回一個(gè)文件描述符,當(dāng)需要讀文件時(shí),也需要把文件描述符作為參數(shù)傳遞給相應(yīng)的函數(shù)。
通常,一個(gè)進(jìn)程打開時(shí),都會(huì)打開3個(gè)文件:標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、和標(biāo)準(zhǔn)出錯(cuò)處理,這3個(gè)文件分別對(duì)應(yīng)文件描述符為0,1,2
三、不帶緩存的文件IO操作
不帶緩存是指每一個(gè)函數(shù)都只是調(diào)用系統(tǒng)中的一個(gè)函數(shù),這些函數(shù)雖然不是ANSI C的組成部分,但都是POSIX的組成部分。
主要用到5個(gè)函數(shù):open,read,write,close,lseek。
Open函數(shù)定義:
定義函數(shù) int open( const char * pathname, int flags);
int open( const char * pathname,int flags, int mode);
參數(shù)pathname 被打開的文件名(可包含路徑名)。
參數(shù)flags 所能使用的旗標(biāo):
O_RDONLY 以只讀方式打開文件
O_WRONLY 以只寫方式打開文件
O_RDWR 以可讀寫方式打開文件。上述三種旗標(biāo)是互斥的,也就是不可同時(shí)使用,但可與下列的旗標(biāo)利用OR(|)運(yùn)算符組合。
O_CREAT 若欲打開的文件不存在則自動(dòng)建立該文件。
O_EXCL 如果O_CREAT 也被設(shè)置,此指令會(huì)去檢查文件是否存在。文件若不存在則建立該文件,否則將導(dǎo)致打開文件錯(cuò)誤。此外,若O_CREAT與O_EXCL同時(shí)設(shè)置,并且欲打開的文件為符號(hào)連接,則會(huì)打開文件失敗。
O_NOCTTY 如果欲打開的文件為終端機(jī)設(shè)備時(shí),則不會(huì)將該終端機(jī)當(dāng)成進(jìn)程控制終端機(jī)。
O_TRUNC 若文件存在并且以可寫的方式打開時(shí),此旗標(biāo)會(huì)令文件長(zhǎng)度清為0,而原來(lái)存于該文件的資料也會(huì)消失。
O_APPEND 當(dāng)讀寫文件時(shí)會(huì)從文件尾開始移動(dòng),也就是所寫入的數(shù)據(jù)會(huì)以附加的方式加入到文件后面。
O_NONBLOCK 以不可阻斷的方式打開文件,也就是無(wú)論有無(wú)數(shù)據(jù)讀取或等待,都會(huì)立即返回進(jìn)程之中。
O_NDELAY 同O_NONBLOCK。
O_SYNC 以同步的方式打開文件。
O_NOFOLLOW 如果參數(shù)pathname 所指的文件為一符號(hào)連接,則會(huì)令打開文件失敗。
O_DIRECTORY 如果參數(shù)pathname 所指的文件并非為一目錄,則會(huì)令打開文件失敗。
此為Linux2.2以后特有的旗標(biāo),以避免一些系統(tǒng)安全問(wèn)題。參數(shù)mode 則有下列數(shù)種組合,只有在建立新文件時(shí)才會(huì)生效,此外真正建文件時(shí)的權(quán)限會(huì)受到umask值所影響,因此該文件權(quán)限應(yīng)該為(mode-umaks)。
S_IRWXU00700 權(quán)限,代表該文件所有者具有可讀、可寫及可執(zhí)行的權(quán)限。
S_IRUSR 或S_IREAD,00400權(quán)限,代表該文件所有者具有可讀取的權(quán)限。
S_IWUSR 或S_IWRITE,00200 權(quán)限,代表該文件所有者具有可寫入的權(quán)限。
S_IXUSR 或S_IEXEC,00100 權(quán)限,代表該文件所有者具有可執(zhí)行的權(quán)限。
S_IRWXG 00070權(quán)限,代表該文件用戶組具有可讀、可寫及可執(zhí)行的權(quán)限。
S_IRGRP 00040 權(quán)限,代表該文件用戶組具有可讀的權(quán)限。
S_IWGRP 00020權(quán)限,代表該文件用戶組具有可寫入的權(quán)限。
S_IXGRP 00010 權(quán)限,代表該文件用戶組具有可執(zhí)行的權(quán)限。
S_IRWXO 00007權(quán)限,代表其他用戶具有可讀、可寫及可執(zhí)行的權(quán)限。
S_IROTH 00004 權(quán)限,代表其他用戶具有可讀的權(quán)限
S_IWOTH 00002權(quán)限,代表其他用戶具有可寫入的權(quán)限。
S_IXOTH 00001 權(quán)限,代表其他用戶具有可執(zhí)行的權(quán)限。
返回值 若所有欲核查的權(quán)限都通過(guò)了檢查則返回0 值,表示成功,只要有一個(gè)權(quán)限被禁止則返回-1。
錯(cuò)誤代碼 EEXIST 參數(shù)pathname 所指的文件已存在,卻使用了O_CREAT和O_EXCL旗標(biāo)。
EACCESS 參數(shù)pathname所指的文件不符合所要求測(cè)試的權(quán)限。
EROFS 欲測(cè)試寫入權(quán)限的文件存在于只讀文件系統(tǒng)內(nèi)。
EFAULT 參數(shù)pathname指針超出可存取內(nèi)存空間。
EINVAL 參數(shù)mode 不正確。
ENAMETOOLONG 參數(shù)pathname太長(zhǎng)。
ENOTDIR 參數(shù)pathname不是目錄。
ENOMEM 核心內(nèi)存不足。
ELOOP 參數(shù)pathname有過(guò)多符號(hào)連接問(wèn)題。
EIO I/O 存取錯(cuò)誤
如:
int dev_fd;//文件描述符,失敗返回為-1。
dev_fd = open("/dev/simple",O_RDWR | O_NONBLOCK,0600);
//權(quán)限值可以不要
如果使用了O_CREATE標(biāo)志,則使用的函數(shù)為int open( const char * pathname,int flags, int mode);此時(shí)就要指定mode,用來(lái)表示文件的訪問(wèn)的權(quán)限,mode的組合表示如上所述。
除了可以用以上宏來(lái)進(jìn)行“或”邏輯產(chǎn)生標(biāo)志以外,我們還可以使用自己的數(shù)字來(lái)表示,linux總共用5個(gè)數(shù)字來(lái)表示文件的各種權(quán)限,第一位表示設(shè)置用戶的ID,第二位表示設(shè)置組ID,第三位表示用戶自己的權(quán)限位,第四位表示組的權(quán)限,第五位表示其他人的權(quán)限,每個(gè)數(shù)字可以取1(執(zhí)行權(quán)限),2(寫權(quán)限),4(讀權(quán)限),0(無(wú)),或者這些值的組合。
例如,如果要?jiǎng)?chuàng)建一個(gè)用戶可讀,可寫,可執(zhí)行,但是組沒(méi)有權(quán)限,其他人可以讀,可以執(zhí)行的文件,并設(shè)置用戶ID,那么使用的模式是1(設(shè)置用戶ID)、0(不設(shè)置用戶ID)、7(1+2+4,讀,寫,執(zhí)行)、0(沒(méi)有權(quán)限)、5(1+4,讀,執(zhí)行),即10705,如:
Open(“test”, O_CREATE,10705)
以O_CREATE為標(biāo)志的open函數(shù)實(shí)際上實(shí)現(xiàn)了文件創(chuàng)建的功能,因此下面的函數(shù)等同create()函數(shù)。
Int open(pathname, O_CREATE |O_WRONLY,mode);
注:open函數(shù)返回的文件描述符一定是最小的未用文件描述符。
Close函數(shù)定義:
Int close(int fd)
fd:文件描述符,0為成功,-1為出錯(cuò)
read函數(shù)定義:
read函數(shù)是用于將指定的文件描述符中讀出數(shù)據(jù),當(dāng)從終端設(shè)備中讀出數(shù)據(jù)時(shí),通常一次最多讀一行。
函數(shù)原型:ssize_t read(int fd,void *buf,size_t count)
Buf:指定存儲(chǔ)器讀出數(shù)據(jù)的緩沖區(qū)
Count:指定讀出的字節(jié)數(shù)
函數(shù)返回值:
成功:讀到的字節(jié)數(shù)
0:已達(dá)到文件尾
-1:出錯(cuò)
在讀普通文件時(shí),若讀到要求的字節(jié)數(shù)之前已到達(dá)文件的結(jié)尾,則返回的字節(jié)數(shù)會(huì)小于希望讀出的字節(jié)數(shù)。
write函數(shù)定義:
函數(shù)是用于向打開的文件寫數(shù)據(jù),寫操作從文件的當(dāng)前位移量處開始,若磁盤已滿或超出該文件的長(zhǎng)度,則write函數(shù)返回失敗。
函數(shù)原型:ssize_t write(int fd,void *buf,size_t count)
Buf:指定存儲(chǔ)器寫入數(shù)據(jù)的緩沖區(qū)
Count:指定讀出的字節(jié)數(shù)
函數(shù)返回值:
成功:已寫的字節(jié)數(shù)
-1:出錯(cuò)
在寫普通文件時(shí),寫操作從文件的當(dāng)前位移處開始。
lseek函數(shù)定義:
函數(shù)是用于在指定的文件描述符中將文件指針定位到相應(yīng)的位置。
函數(shù)原型:ssize_t lseek(int fd,off_t offset,int whence)
offset:偏移量,每一次讀寫操作所需要移動(dòng)的距離,單位是字節(jié)的數(shù)量,可正可負(fù)(向前移,向后移)。
whence:SEEK_SET:當(dāng)前位置為文件指針的位置,新位置為當(dāng)前位置加上偏移量
SEEK_CUR:
SEEK_END: 當(dāng)前位置為文件的結(jié)尾,新位置為偏移量的大小加上偏移量的大小
函數(shù)返回值:
成功:文件的當(dāng)前位移
-1:出錯(cuò)
Offset可取負(fù)值,即可將文件指針相對(duì)當(dāng)前位置向前移動(dòng)5個(gè)字節(jié),lseek函數(shù)的返回值為文件指針相對(duì)于文件頭的位置。