編程專題講座-linux c程序設(shè)計(jì)第17章文件_第1頁
編程專題講座-linux c程序設(shè)計(jì)第17章文件_第2頁
編程專題講座-linux c程序設(shè)計(jì)第17章文件_第3頁
編程專題講座-linux c程序設(shè)計(jì)第17章文件_第4頁
編程專題講座-linux c程序設(shè)計(jì)第17章文件_第5頁
已閱讀5頁,還剩42頁未讀, 繼續(xù)免費(fèi)閱讀

付費(fèi)下載

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

17I/O進(jìn)Linux文件系統(tǒng)模型,最后部分的內(nèi)容介紹了基于文件描述符的操作,本章最后的兩個(gè)小節(jié)則介紹兩種特殊的文件——符號(hào)連接和。Linux環(huán)境下每一個(gè)磁盤文件在打開時(shí)都會(huì)在內(nèi)核中建立一個(gè)文件表項(xiàng)。該文件表用open函數(shù)打開文件,使用close函數(shù)關(guān)閉文件;使用lseek函數(shù)在文件中定位;使用read函數(shù)和write函數(shù)讀寫文件。打開一個(gè)該文件)。Open函數(shù)的原型如下:#include#includeintopen(constchar*pathname,intoflag,…/*mode_tmode open("./test.txt",O_RDONLY);open("./test.txt",O_RDONLY);分別表示打開當(dāng)前下的test.txt文件和打開當(dāng)前的上一級(jí)下的test.txt文件。使用“.”當(dāng)前下的文件和直接使用文件名兩種方式看起來差不多,但是如果在當(dāng)前下多個(gè),第1種方式的優(yōu)勢(shì)就顯現(xiàn)出來了。open("./admin/test.txt", 表示應(yīng)用當(dāng)前下的admintest.txt文件open函數(shù)的2個(gè)參數(shù)指定打開文件的選項(xiàng),其值是一個(gè)位向量,通常使用下列一個(gè)或者多個(gè)常量進(jìn)行或操作構(gòu)成oflag參數(shù),這些常量值由宏表示,其定義在fcntl.h頭文件中。3個(gè)選項(xiàng)是打開文件的模式,Linux支持以只讀、只寫或者讀寫的方式O_WRONLY(Writeonly):只寫打開,值為如“O_RDONLY|O_WRONLY”之類的選項(xiàng)是沒有意義的。O_APPENDO_CREATopen函數(shù)的第3個(gè)參數(shù)。O_TRUNC:如果此文件存在,且以寫方式成功打開,則將該文件截短為0。本O_NOTTYopen函O_DSYNC、O_RSYNCO_SYNC3個(gè)選項(xiàng)和文件同步打開文件選項(xiàng)的講解比較復(fù)雜,筆者安排在涉及該選項(xiàng)的時(shí)候再對(duì)其進(jìn)行詳細(xì)的講注意:在創(chuàng)建一個(gè)新文件時(shí),用戶指定的權(quán)限會(huì)受到 文件權(quán)限掩碼的影響3個(gè)文件——標(biāo)準(zhǔn)輸入(文件描述符為0)、標(biāo)準(zhǔn)輸出(文件描述符為1)和標(biāo)準(zhǔn)出錯(cuò)(文件描述符為2),如果該進(jìn)程第一次打開文件且打開成功時(shí),其文件描述符一定是3,即open函數(shù)返回值為3。假設(shè)關(guān)閉標(biāo)準(zhǔn)輸出,再次打開一個(gè)文件,其文件描述符則一定是1,該文件替代了剛失敗,則返回–1,并且設(shè)置errno變量值。還改變了一些全局資源,那么該函數(shù)就是有副作用的,如圖17-1所示。17-1函數(shù)的副作用示意注意:所有的系統(tǒng)調(diào)用都是有副作用的,對(duì)于有副作用的函數(shù),使用時(shí)要特別,以下面是一個(gè)open函數(shù)的實(shí)例,實(shí)例中使用O_CREAT選項(xiàng),并對(duì)其詳細(xì)講解,其他打open函數(shù)打開一個(gè)文件,open函數(shù)被設(shè)置了O_CREAT選項(xiàng)。因此,如果文件不存在,則創(chuàng)建該文件。#include<stdio.h>#include<stdio.h>#include<fcntl.h>int{int/*文件描述符/*以“讀寫”模式打開當(dāng) *并且將其文件權(quán)限字設(shè)置為八進(jìn)制的fd=open("test.txt",O_RDWR|O_CREAT,0700);if(fd==-1){perror("failto /*printf("open/*關(guān)閉文件,有關(guān)內(nèi)容見本書后面小節(jié)return}17-1open.c$gccopen.c-o $ls$ls-l-rw------ open$rm open$ls$ls-l-rwx----- 以上程序打開一個(gè)文件,如果當(dāng)前下沒有test.txt文件,運(yùn)行該程序后出現(xiàn)一個(gè)新關(guān)于O_CREAT選項(xiàng),有兩點(diǎn)需要解釋。如果需要?jiǎng)?chuàng)建文件,必須保證open函數(shù)的1個(gè)參數(shù)正確。即路徑名有效,且每一個(gè)組成部分都有相應(yīng)的權(quán)限。否則會(huì)出現(xiàn)權(quán)限錯(cuò)誤,導(dǎo)致open函數(shù)返回–1。如果不指定open函數(shù)的第3個(gè)參數(shù),系統(tǒng)不會(huì)提供一個(gè)默認(rèn)值作為新文件的權(quán)限open函數(shù)返回–1,但是在后續(xù)操作open函數(shù)的第3個(gè)參數(shù)。打開文件的出錯(cuò)情L(zhǎng)inux系統(tǒng)來講,open函數(shù)不太容易出錯(cuò),所以,在實(shí)際編程過程中,檢查errno對(duì)當(dāng)前沒有寫權(quán)限。注意:檢查errno可以有助于迅速排查程序#include<stdio.h>#include<fcntl.h>#include<stdio.h>#include<fcntl.h>int{intfdopen("no_such_fileO_RDONLY*嘗試打開一個(gè)不存在的文件*/if(fd==-1)perror("failtoopen");fd=open("denied",if(fd==-/* 17-2file_error.c演示常見的文件打開錯(cuò)perror("failperror("failtoopen");close(fd*return}$gcc$gccfile_error.c-ofailtoopen:Nosuchfileordirectoryfailtoopen:Permissiondenied關(guān)閉一個(gè)#include#includeintclose(int該函數(shù)的參數(shù)是需要關(guān)閉的文件的文件描述符,成功關(guān)閉返回1,失敗則返回–1。open函數(shù)一樣,close函數(shù)同樣很少出錯(cuò),因此,在一般的編程過程中都不對(duì)closeclose函數(shù)的返回值是十分必須的,例如,close函數(shù)關(guān)閉的文件是一個(gè)的網(wǎng)絡(luò)文件時(shí),這時(shí)就需要檢查close函數(shù)是否成close函數(shù)在關(guān)閉的時(shí)候會(huì)將內(nèi)存緩沖區(qū)中的內(nèi)容寫到外存上,因此,關(guān)閉一個(gè)能導(dǎo)致數(shù)據(jù)包的丟失,如圖17-2所示。17-2close函數(shù)關(guān)閉網(wǎng)絡(luò)文創(chuàng)建一個(gè)新文#include#includeintcreat(constchar*pathname,mode_t1個(gè)參數(shù)和open12個(gè)參數(shù)則和openopen(pathname,O_CREATE|O_WRONLY|O_TRUNC, 則將其截短為0,再以只寫方式打開。說明所謂截短為 就是將該文件原有內(nèi)容全部丟棄,在寫文件時(shí)會(huì)對(duì)原文件造成覆蓋#include<stdio.h>#include<fcntl.h> 17-3creat.c創(chuàng)建一#include<stdio.h>#include<fcntl.h>intint{intfdcreat("test.txt0700*700*/if(fd==-1){perror("failtocreat"); /*輸出提示信息return}在 $gcccreat.c-o 在 creat$ls$ls-l-rwx----- creat函數(shù)只能以只寫方式打開新創(chuàng)建的文件。如果要對(duì)新文件進(jìn)行讀操作,則需要關(guān)閉該文件再重新以讀方式打開。這樣做很不方便,在有了更好的替代方法后,creat函數(shù)現(xiàn)open(pathname,O_CREATE|O_RDWR|O_TRUNC, 圖17-3所示。17-3讀寫操作改變文件的讀寫位#include#includeoff_tlseek(intfiledes,off_toffset,intlseek1個(gè)參數(shù)是一個(gè)已經(jīng)打開的文件的文件描述符。lseek2個(gè)參數(shù)和第3個(gè)參數(shù)的解釋如下:offsetwhenceSEEK_CUR時(shí),表示將該文件的文件偏移量設(shè)置為當(dāng)前文件偏移位置增加offset個(gè)字節(jié),offset的值可以是一個(gè)負(fù)數(shù)。whenceSEEK_END時(shí),表示將該文件的文件偏移量設(shè)置為當(dāng)前文件結(jié)尾位置增加offset個(gè)字節(jié),offset的值可以是一個(gè)負(fù)數(shù)。注意:lseek函數(shù)3個(gè)參數(shù)只3個(gè)宏所代表的整型值中的一個(gè),上述3個(gè)宏的定義也在unistd.h中??赡転樨?fù)值,所以檢查lseek函數(shù)是否出錯(cuò)時(shí),不能檢查其返回值是否小于0,而應(yīng)檢查其lseek函數(shù)得到當(dāng)前文件的偏移值。該程序打開一個(gè)文件,得出未進(jìn)行任何讀寫操作的文件的偏移值。之后調(diào)用read函數(shù)5個(gè)字節(jié)的內(nèi)容,再次調(diào)用lseek函數(shù)得到文件的讀寫位置。#include<stdio.h>#include<fcntl.h>#defineMAX1024intmain(void){intfd;off_t#include<stdio.h>#include<fcntl.h>#defineMAX1024intmain(void){intfd;off_toff;charfdopen("test.txtO_RDWR*打開一*/if(fd==-1){perror("failtoopen");}/*offlseek(fd,0SEEK_CUR);*lseekif(off==-perror("failtolseek");}printf("theoffsetisd\noff)*if(read(buf,5,fd)==-1){/*perror("failotread");}5 /*輸出提示信息offlseek(fd0SEEK_CUR*lseek*/if(off==-1){prerror("failtolseek");}printf("theoffsetis%d\n"off)*/*關(guān)閉文件return}$gcclseek.c-o theoffsetis:0afterreadingtheoffsetis:被系統(tǒng)自動(dòng)放棄,如圖17-4所示。17-4文件截短示意#include#includeinttruncate(constchar*pathname,off_t2個(gè)參數(shù)表示將文件截短的字節(jié)數(shù),超過該字節(jié)數(shù)的部分將被系統(tǒng)放棄。如果文件之間形成一個(gè)文件空洞,如圖17-5所示。17-5文件拓展后的空洞示個(gè)已經(jīng)存在的文件,該文件的實(shí)際大小小于指定的截短字節(jié)數(shù)。該程序首先拓展調(diào)用程序名拓展后的文件字節(jié)數(shù)實(shí)際的文件字節(jié) BBB17-6實(shí)現(xiàn)截短文件的程序流程#include<stdio.h>#include<fcntl.h>#defineMAXintmain(intargc,char#include<stdio.h>#include<fcntl.h>#defineMAXintmain(intargc,char*argv[{intfd;intlen;intrest;inti;if(argc3*根據(jù)命令行參數(shù)設(shè)置擴(kuò)展后的文件字節(jié)數(shù)和需要填充的字節(jié)*/len=MAX;rest=len=atoi(argv[1]);rest=atoi(argv[2]);}if(truncate("test.txtMAX1)*截短操作,將文件拓展為指定字節(jié)*/perror("failtotruncate");}/*添加寫方式打開文件,每次寫的內(nèi)容會(huì)自動(dòng)添加到文件的結(jié)尾*/fd=open("test.txt",O_RDWR|O_APPEND);if(fd==-perror("failtoopen");}i=while(irest*設(shè)置填充內(nèi)容,余下的文件內(nèi)容填充為字符‘0buf[i]=}if(write(fdbufrest1**/perror("failtowrite");}close(fdclose(fd*return}$gcc$gcctruncate.c-o$cato$ls-l

-rwxr--r--1root Feb1219:12$./truncate$./truncate32$cat$cato$ls-l-rw-r--r--1root Feb1219:14#include#includeintftruncate(intfiledes,off_tftruncate1個(gè)參數(shù)表示需要進(jìn)行截短操作的文件,該參數(shù)是一個(gè)已經(jīng)打開文件的文件描述符。ftruncate2個(gè)參數(shù)表示需要截短的長(zhǎng)度,該參數(shù)同樣也可以大于文件的實(shí)際大小。如果成功截短一個(gè)文件,ftruncate函數(shù)返回0,失敗則返回–1。清空一個(gè)0即可。下面實(shí)例演示了截短一個(gè)已經(jīng)打開的文件。該程序首先打開一個(gè)文件,之后調(diào)用ftruncate函數(shù)將其截短為0。當(dāng)關(guān)閉該文件后,其原來的文件內(nèi)容就不到了。#include<stdio.h>#include<fcntl.h> 17-6clear.c使用截短函數(shù)清空一#include<stdio.h>#include<fcntl.h>intint{intfdfopen("test.txtO_WRONLY*寫if(fd==-perror("failtoopen");}if(ftruncate(fd0)1*0perror("failtotruncate");}close(fd*return}$gccclear.c-o $cat$cato$ls-l-rw-r--r--1root Feb1217:12 $cat$cat $ls-l-rw-r--r--1root Feb1217:18說明:open#include<stdio.h>#include<stdio.h>#includeint{intchar*p="linux/*fdfopen("test.txtO_WRONLY|O_TRUNC*if(fd==-perror("failtoopen");}perror("failtowrite");}close(fd**/return0;}17-7clear.c$gcccut.c-o $cat$cato linuxok文件的讀寫操#include#includessize_tread(intfiledes,void*buf,size_tread函數(shù)的第1個(gè)參數(shù)是一個(gè)文件描述符,表示從哪個(gè)文件中,該文件一定是一個(gè)具有讀方式打開的文件。第2個(gè)參數(shù)是緩沖區(qū),將文件內(nèi)容讀入到該緩沖區(qū)中。第3個(gè)nb,返回值是實(shí)際讀出的字節(jié)數(shù),如果文件一節(jié)到達(dá)末尾則返回值為0。#define#defineMAXintbytes;/*緩沖區(qū)/*讀入的字節(jié)數(shù)read(fdbufMAX1*MAX1個(gè)字節(jié),緩沖區(qū)最后一個(gè)位置留給'\0'束符*/buf[bytes]='\0'; /*添加'\0'結(jié)束符*/#include#includesszie_twrite(intfiledes,void*buf,size_tread函數(shù)類似,write3個(gè)參數(shù)分別代表要寫的文件,需要寫到文件中內(nèi)容說明:對(duì)于write函數(shù)來講,常見的出錯(cuò)原因是外存沒有足夠的空間或者文件長(zhǎng)度my_cp目標(biāo)文件名源文件 容并將其寫入到目標(biāo)文件中,其程序執(zhí)行流程如圖17-7所示。 my_cp程序的執(zhí)行流#include<stdio.h>#include<fcntl.h>/*linuxcp命令的簡(jiǎn)單實(shí)現(xiàn),命令格式:cp#include<stdio.h>#include<fcntl.h>/*linuxcp命令的簡(jiǎn)單實(shí)現(xiàn),命令格式:cpdes成功返回0,失敗返回-1,失敗原因保存在errno**argv[2src(本例使用絕對(duì)路徑intmain(intargc,char{charintinout**/intn;if(argc!=if((inopen(argv[2O_RDONLY1**/perror("failtoopen");}/*目標(biāo)文件,該文件不存在則創(chuàng)建,該文件存在則覆蓋且只寫打開*/if((outopen(argv[1O_WRONLY|O_TRUNC|O_CREAT1){perror("failtoopen");}while((nread(inbufMAX0*if(write(outbufnn)*n*/perror("failtowrite");}if(n<0){/*讀入出錯(cuò)*/perror("failtoread");}printf("copydone\n")*/*關(guān)閉兩個(gè)文件return}$gccmy_cp.c-o $cat$cato 在 $./my_cp/home/admindest.txt/home/admin $cat$cato17-8緩沖區(qū)機(jī)制示意為了避免該種情況,用戶可以指定系統(tǒng)在緩沖區(qū)并未填滿的時(shí)候?qū)⑽募?nèi)容回寫到磁Lnux#include#includeintfsync(intintfdatasync(intfiledes);voidsync(void); 函數(shù)所做的操作最簡(jiǎn)單,該函數(shù)將所有打開的文件寫回到磁盤上,將文件中修改fsync函數(shù)可以確保文件的實(shí)際寫出,該函數(shù)會(huì)阻塞直到修改的盤塊寫到外存后才返回,成功返回0,如出錯(cuò)則返回–1。注意:fsync函數(shù)是需要保持同步文件的描述符。fsync函數(shù)可保證文件內(nèi)容及時(shí)更新,并fsync函數(shù)進(jìn)行文件的同步操作。該程序首先打開一個(gè)文件,之5fsymc函數(shù)將輸出的內(nèi)容回#include<stdio.h>#include<stdio.h>#include<fcntl.h>int{intfd;inti;fdopen("test.txtO_RDWR*打開一個(gè)文件,其打開方式為可*/if(fd==-1){perrror("failtoopen");}i=while(i3){/*5o\n"1*向文件輸出一行字符串作為提perror("failtowrite");}if(fsync(fd1*輸出信息后立即進(jìn)行文件同步,保證輸出信息即時(shí)寫perror("failtofsync");}17-9fsync.cfsync函數(shù)進(jìn)行文件同}close(fd**/return0;}$gcc$gccfsync.c-o (5秒鐘之后 (5秒鐘之后 (5秒鐘之后$cat$cato$cat$cato(5秒鐘之后(5秒鐘之后(5秒鐘之后fdatasyncsyncfdatasync函數(shù)只更新文件的數(shù)據(jù)部分,17.2I/O,后面的幾個(gè)小節(jié)將討論關(guān)于文件描述符的操作,這些述符的本質(zhì)有些遺忘,請(qǐng)回顧本章17.1節(jié)的內(nèi)容。文件描述#include#includeintdup(intintdup2(intfiledes,intdup函數(shù)的參數(shù)是需要的文件描述符,該函數(shù)總是找到進(jìn)程文件表中第1個(gè)可用的文件描述符,將參數(shù)指定的文件描述符到該描述符后返回該描述符。例如,一個(gè)進(jìn)3個(gè)文件——分別是標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)出錯(cuò),3個(gè)文件的文件描述符分別是0、1和2。所以這時(shí)執(zhí)行如下操作:fd= 本例中為3。后的文件描述符共用一個(gè)文件表項(xiàng),所以其兩個(gè)文件的偏移量,問價(jià)狀態(tài)標(biāo)志等都是共享的,如圖17-9所示。17-9共享文件示意 17-10判斷當(dāng)前進(jìn)程第一個(gè)可用的文件描述符是多少的錯(cuò)誤用#include<unistd.h>intfirst_avail_fd(){intfd=dup(0);/* 次變?yōu)榭捎胷eturn 因?yàn)榇?個(gè)文件共用一個(gè)文件表項(xiàng)。#include<unistd.h>intfirst_avail_fd(){int/*打開一個(gè)肯定存在的文件,open函數(shù)會(huì)返回第一個(gè)可用的文件描#include<unistd.h>intfirst_avail_fd(){int/*打開一個(gè)肯定存在的文件,open函數(shù)會(huì)返回第一個(gè)可用的文件描述*/fd=open("test.txt",O_RDONLY);close(fd*關(guān)閉該文件,使第一個(gè)可用文件描述符再次變?yōu)榭捎?/returnfd;}dup2dup類似,不過dup函數(shù)允許用戶使用第2個(gè)參數(shù)指定將文件描述符到哪一個(gè)文件描述符上(dup2實(shí)際上應(yīng)該讀成dupto而不是duptwo)intdup(fd1, 該操作表示將fd1到fd2上,并且返回fd2的值。如果fd2已經(jīng)打開,則將fd2所代表的文件關(guān)閉,再將fd1到fd2。例如,以下使用情況:fd=dup2(fd1, 該種使用是很的,在時(shí)會(huì)關(guān)閉fd1所代表的文件,這時(shí)過來的fd1文件if(fd1if(fd1!=fd=dup2(fd1,I/O重定向概念及其應(yīng)如,使用test.txt文件進(jìn)行重定向輸入,就是用test.txt代替原來的標(biāo)準(zhǔn)輸入。這時(shí)應(yīng)用程序不再?gòu)逆I盤輸入中輸入內(nèi)容,而是從test.txt文件中輸入內(nèi)容。同樣,使用test.txt的內(nèi)容輸出到文件test.txt中。以輸入為例,重定向的示意圖如圖17-10所示。17-10輸入重定向的示意Linux環(huán)境下,可以在s 程序?qū)膄ilename文件中取得輸入內(nèi)容,而不從鍵盤輸入中取得輸入。“>filename”表示輸出重定向,程序會(huì)將內(nèi)容輸出到filname文件上,而不輸出到屏幕輸出?!?>filename”表示標(biāo)準(zhǔn)出錯(cuò)重定向,程序?qū)⒊鲥e(cuò)信息輸出到filname文件上,而不再輸出到屏幕上。下面以ls命令舉例重定向最基本的應(yīng)用。$ls> test.cI/OI/O重定向的應(yīng)用。該程序需要從input.txt文件中讀入10個(gè)數(shù)據(jù),并且找出其中的最大值,并且將該最大值輸出到res.txt文件中。該程序編寫過程中并不直接進(jìn)行文件操作,而是將對(duì)文件操作的細(xì)節(jié)交給I/O重定向進(jìn)行處理。#include 17-12get_max.c取得10個(gè)數(shù)據(jù)的最#include#define#defineMAXint{inti;intfor(i=0;i<MAX;i++)/*從標(biāo)準(zhǔn)輸 數(shù)據(jù)max=for(i=0;i<MAX;i++)if(max<array[i])max=/*printf("theresultisd\n"max)*return}$gccget_max.c-o $./$./123456789theresultis:說明:程序本身并沒有涉及到文件操作的細(xì)節(jié),因此,使用重定向?qū)崿F(xiàn)文件的輸入輸出。$cat$cat123456789$./get_max<input.txt> $cat$cattheresultis:種方法的缺點(diǎn)也很明顯,首先,其輸入過于復(fù)雜(需要5個(gè)命令行參數(shù)),過多令參解決的辦法是將重定向令輸入封裝到一個(gè)s中,用戶只需要執(zhí)行該 ./get_max<input.txt> 17-13start.sh odu+x $cat$cattheresultis:使用I/O重定向操作文件輸入輸出的第2個(gè)缺點(diǎn)是該種方法過于依賴s。假如程序替的方法是在程序中使用C語言庫(kù)函數(shù)或者Linux系統(tǒng)提供的系統(tǒng)調(diào)用接口實(shí)現(xiàn)文件fcntl函數(shù)實(shí)現(xiàn)。該函數(shù)的每一個(gè)功能都可以找到一個(gè)其他的系統(tǒng)函數(shù)替代。Linux環(huán)境使用fcntl函數(shù)進(jìn)行文件屬性的操作,其函數(shù)原型如下:#include#includeintfcntl(intfiledes,intcmd,fcntl1個(gè)參數(shù)表示需要進(jìn)行屬性的文件描述符。該文件必須是一個(gè)已經(jīng)打開同的功能。這些命令有10個(gè),分為5種功能,如表17-1所示。表17- fcntl函數(shù)fcntl32個(gè)參數(shù)的不同而不同。也就是說,第3個(gè)參數(shù)的意義由fcntl函數(shù)選擇令決定,如表17-2所示。表17- fcntl函數(shù)的第3個(gè)參數(shù)的意fcntl函數(shù)3個(gè)參數(shù)的意00000017-3fcntl函數(shù)的返回fcntl函數(shù)000000修改打開文件的文件狀fcntl命令中比較常用的是設(shè)置文件狀態(tài)標(biāo)志令F_GETFL,以及獲得文件狀態(tài)標(biāo)志令F_SETFL。由于文件的狀態(tài)標(biāo)志在調(diào)用open函數(shù)打開文件時(shí)已經(jīng)確定,當(dāng)需要fcntlfcntl函數(shù)修改,表17-4列出了文件的狀態(tài)標(biāo)志以及其是否能被fcntl函數(shù)修改。17-4文件狀態(tài)標(biāo)志是否能被fcntl函數(shù)修否否否是是是是是執(zhí)行流程如圖17-11所示。17-11控制文件程序的執(zhí)行流#include<stdio.h>#include<stdio.h>#include<fcntl.h>#includeint{intfd;intflag;char*p="1stlinux";char*q="2nd/*fdopen("test.txtO_WRONLY*if(fd==-perror("failtoopen");}perror("failtowrite");}flag=fcntl(fd,F_GETFL,0);if(flag==-1){perror("failtofcntl");}flag|=/*F_GETFL/*if(fcntl(fdF_SETFLflag1*perror("failtofcntl");}if(write(fdqstrlen(q1){*的后面perror("failtowrite");}close(fd*return}17-13file_status.cfcntl$gccfile_status.c-o $cat$cato$./ 1stlinuxd2ndlinux說明:首先寫入的1stlinux\n覆蓋了部分oworld的內(nèi)容,而第2次寫入的2ndlinux\n則追加到了test.txt文件的結(jié)尾。第2行的字符‘d’是test.txt文件原來的內(nèi)容oworld中僅存的一個(gè)字符了。I/OI/O會(huì)造成讀寫函數(shù)阻塞。非阻I/O適用于低速的外部設(shè)備,例如,屏幕輸出、網(wǎng)絡(luò)文件系統(tǒng)等。這里所說的低速是相對(duì)于計(jì)算的處理速度而言的。本章將詳細(xì)講解非阻塞方式的文件I/O。非阻I/O的概I/O與外部設(shè)備進(jìn)行交互。這些外部設(shè)備可能是本地磁盤,也可能是一I/O速度是不同的,有的很快(例如本地的磁盤讀寫),有的卻很慢(例如網(wǎng)絡(luò)間的數(shù)據(jù)傳輸)。速度較慢的I/O會(huì)造成讀寫函數(shù)阻塞。例如,一個(gè)應(yīng)用程序調(diào)用write函數(shù)從的文件系統(tǒng)中數(shù)據(jù),這時(shí)需要傳輸?shù)膿?jù)丟失了。此時(shí)應(yīng)用程序調(diào)用的write函數(shù)就會(huì)因?yàn)椴坏綌?shù)據(jù)而阻塞,直到需要的數(shù)據(jù)可以讀寫未知。這種情況如圖17-12所示。數(shù)據(jù)不可用,I/O操作函數(shù)不會(huì)阻塞而是返回,并將errno變量設(shè)置為EAGAIN。該錯(cuò)誤號(hào)表示I/O系統(tǒng)調(diào)用沒有阻塞等待數(shù)據(jù),導(dǎo)致本次讀寫操作失敗。17-12I/O模式發(fā)生阻塞示意以非阻塞方式打開文Linux環(huán)境下可以在調(diào)用open函數(shù)打開一個(gè)文件的同時(shí)使用O_NONBLOCK選項(xiàng)將打開的文件設(shè)置為非阻塞方式。下面實(shí)例演示了非阻塞方式的I/O操作。該程序的運(yùn)行格程序名讀入的大文件的文件名存放輸出結(jié)果的文件 而是返回后將errno變量設(shè)置為EAGAIN。該程序一個(gè)大文件,并且將因?yàn)榉亲枞?include<stdio.h>#inlcude<fcntl.h>#defineMAX#include<stdio.h>#inlcude<fcntl.h>#defineMAX#defineLEN1024/*使用宏作為緩沖區(qū)的大intmain(intargc,char*argv[{intfd1,fd2;FILEfp; /*大文件的緩沖區(qū)*/intn,rest;char*p=charif(argc3)*缺少文件}fd1=open(argv[1],O_RDONLY);if(fd1==-1){perror("failtoread");}/*fp=fopen(argv[2],"w");if(fp==-1){perror("failtoread");}fd2open("test.txtO_WRONLY*test.txt*/if(fd2==-1){perror("failtoread");exit(1);}rest=read(fd1,buf,/*printf("get%dbytesfrom%s\n",rest,while(rest0**/errno=0;nwrite(fd2p /*輸出緩沖區(qū)內(nèi)容fprintf(fp,"write%d,errno%s\n"n,/**/if(rest>0){/*計(jì)算剩余的字節(jié)數(shù)*/p+=n;rest-=n;}}return0;}$gccnoblock.c-o $./noblockin.txterr.txtget$./noblockin.txterr.txtget87961bytesfromin.txt觀察輸出系統(tǒng)調(diào)用出錯(cuò)原因的文件,會(huì)發(fā)現(xiàn)其中有很多錯(cuò)誤號(hào)為EAGAIN的出將一個(gè)打開文件設(shè)置為非阻塞方對(duì)于一個(gè)已經(jīng)打開的文件,Linux系統(tǒng)同樣可以將其設(shè)置為非阻塞方式。非阻塞被實(shí)說明:fcntl函數(shù)對(duì)此進(jìn)行fcntl函數(shù)可以修改一個(gè)打開文件的文件狀態(tài)標(biāo)志,使用令(fcntl函數(shù)的第二個(gè)參數(shù))是F_SETFL和F_GETFL。首先使用F_SETFL命令得到文件的狀態(tài)標(biāo)志字,之后修改F_GETFL命令設(shè)置為新的文件狀態(tài)字,如圖17-13所示。17-13fcntl函數(shù)修改打開文件狀態(tài)示意fcntl函數(shù)不需要關(guān)閉再打開標(biāo)準(zhǔn)輸出,只需要使用F_SETFDF_GETFD命令對(duì)其程序名讀入的大文件的文件名存放輸出結(jié)果的文件 #include程序17-15 tl.c使用非阻塞方式寫一個(gè)低速設(shè)備——標(biāo)準(zhǔn)輸出,使用fcntl函數(shù)#include#inlcude<fcntl.h>#inlcude<errno.h>#defineMAX#defineLENintmain(intargc,char*argv[{intfd1,fd2;FILE*fp;Intflags;buf[MAX*intn,rest;char*p=buf;charif(argc3)**/printf("expectargs\n");}fd1open(argv[1O_RDONLY**/if(fd1==-1){perror("failtoread");}fd2open(argv[2O_WRONLY**/if(fd2==-1){perror("failtoread");}fp=fdopen(fd2,"w");if(fp==NULL){perror("failtoopen");}/*flagsfcntl(STDOUT_FILENOF_GETFL0*if(flags==1){perror("failtofcntl");exit(1);}flags|=O_NONBlock; /*設(shè)置非阻塞標(biāo)志*/if(fcntl(STDOUT_FILENOF_SETFL,01**/perror("failtofcntl");}}rest=read(fd1,buf,MAX);/*讀入文件*/printf("get%dbytesfroms\n"restargv[1]);while(rest0**/errno=0;nwrite(STDOUT_FILENOp,rest)*fprintf(fp,"write%d,errno%s\n"n,/*if(rest0)**/p+=n;rest-=}}close(fd1**/return}$gccnoblock_fcntl.c-onoblock_ $./noblock_$./noblock_fcntlin.txterr.txtget87961bytesfromin.txt 觀察輸出系統(tǒng)調(diào)用出錯(cuò)原因的文件,同樣會(huì)發(fā)現(xiàn)其中有很多錯(cuò)誤號(hào)為I/O概念的根本原因是為了解決讀寫文件的效率問題。如果一個(gè)程序需I/OI/O往往能夠使程序執(zhí)行的速度有很大地提高,內(nèi)存映射I/O也可以算作是一種空間換時(shí)間的機(jī)制,本章將詳細(xì)講解內(nèi)存映射I/O。內(nèi)存映射I/O的概I/O操作時(shí),通常需要使用一個(gè)內(nèi)核的緩沖區(qū)。輸入或者輸出注意:文件讀寫函數(shù)的緩沖區(qū)的大小是由內(nèi)核確定的,用戶無法更改。致了程序運(yùn)行速度的損失。Linux系統(tǒng)下可以使用內(nèi)存映射I/O來解決該問題。I/OI/O方式,其允許將一個(gè)磁盤文件與內(nèi)存中的一readwriteI/O操作,從而可以避免由于內(nèi)核提供的緩沖區(qū)過小而造成的I/O效率的瓶頸,如圖17-14所示。17-14內(nèi)存映射文件的示意創(chuàng)建一個(gè)內(nèi)存映在使用內(nèi)存映射I/O之前需要首先創(chuàng)建一個(gè)內(nèi)存映射I/O,將磁盤文件的內(nèi)容映射到內(nèi)存中。Linux環(huán)境下使用mmap函數(shù)創(chuàng)建一個(gè)磁盤文件到內(nèi)存的映射,其函數(shù)原型如下:#inlcude#inlcudevoidmmap(void*addr,size_tlen,intprot,intflag,intfiledes,off_tmmap1個(gè)參數(shù)表示建立內(nèi)存映射的起始地址。該參數(shù)通常設(shè)NULL,表示由機(jī)器自動(dòng)選擇內(nèi)存映射的起始地址。在一個(gè)由操作系統(tǒng)進(jìn)行內(nèi)存保護(hù)mmap5個(gè)參數(shù)表示需要進(jìn)行內(nèi)存映射的文件描述符。由于該參數(shù)是一個(gè)文件mmap2個(gè)參數(shù)表示需要映射磁盤文件的長(zhǎng)度,其單位是字節(jié)。而最后一個(gè)參數(shù)off表示磁盤文件的起始映射位置在文件中的偏移值。表17-5內(nèi)存映射區(qū)的權(quán)注意:指定的映射區(qū)的權(quán)限不能和文件的打開方式17-6建立映射區(qū)的屬mmap1addr所表示的起始地址處建立內(nèi)存映射返回MAP_FAILED,其是一個(gè)宏,定義在sys/mman.h文件中。#include 17-16mmap.c創(chuàng)建內(nèi)存映射文件內(nèi)#include#include<stdlib.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>int{intfd;inti;structstatif(stat("test.txt&statbuf1*的大小perror("failtogetstat");}fd=open("test.txt",O_RDONLY);if(fd==-1){perror("failtoopen");}/*buf=(char*)mmap(NULL,statbuf.st_size,PROT_READ,MAP_PRIVATE,fd,if(bufMAP_FAILED){perror("failtommap");exit(1);}i=while(istatbuf.st_zise*mmap函數(shù)不會(huì)添加’\0’結(jié)束符*/}if(munmap(bufstatbuf.st_zise1)**/perror("failtomunmap");}close(fd*returnreturn}$gcc$gccmmap.c-o$cat$catoo撤銷一個(gè)內(nèi)存映#include#includeintmunmap(caddr_taddr,size_tmunmap12個(gè)參數(shù)表示該映射區(qū)的長(zhǎng)度。如果成功撤銷一個(gè)映射區(qū),munmap函數(shù)返回0,失敗則返回–1。注意:munmap函數(shù)撤銷一個(gè)映射區(qū),可是并不能影響到被映射的文件。也就是說調(diào)用MAP_PRIVATEMAP_PRIVATE屬性創(chuàng)建一個(gè)映射區(qū),之后修改其內(nèi)容。待程序結(jié)束運(yùn)行之后,其磁盤文件內(nèi)容并未被修改。#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<string.h>int 17-17discard.c丟棄MAP_PRIVATE映#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<string.h>int{{intfd;structstatif(stat("test.txt&statbuf1*的大小perror("failtogetstat");}fd=open("test.txt",O_WRONLY);if(fd==-1){perror("failtoopen");}/*以可寫方式打開文件buf=(char*)mmap(NULL,statbuf.st_size,PROT_WRITE,MAP_SHARED,fd,if(bufMAP_FAILED){perror("failtommap");exit(1);}strpcy(buf,"China/*修改映射的文件內(nèi)容perror("failtomunmap");}/*關(guān)閉文件return}$gccdiscard.c-o $cat$cato $cat$cato內(nèi)存映射readwrite函數(shù)讀寫文件內(nèi)容一樣,使用內(nèi)存映射的方法同樣無法保證#include#includeintmsync(void*addr,size_tlen,int17-7msync函數(shù)的標(biāo)志及其意其中,MS_ASYNC標(biāo)志和MS_SYNC標(biāo)志必須指定其中的一個(gè),而 標(biāo)志則是可如果成功將指定的頁面寫回,msync函數(shù)返回0,失敗則返回–1。下面實(shí)例演示了該路程如圖17-15所示。#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<string.h>int 17-18msync.c創(chuàng)建#include<stdio.h>#include<stdlib.h>#include<fcntl.h>#include<unistd.h>#include<strin

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論