今天我們總結(jié)一下,linux中常用文件I/O操作。
目前創(chuàng)新互聯(lián)已為上1000+的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬空間、網(wǎng)站托管、服務(wù)器租用、企業(yè)網(wǎng)站設(shè)計(jì)、南部網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶(hù)導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶(hù)和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。首先讓我們看一下,什么是文件I/O:
所謂文件I/O就是:對(duì)于I/O就是input/output,輸入/輸出。文件IO的意思就是讀寫(xiě)文件。
1、linux給我們留的常用文件I/O接口。
1、open close write read lseek
2、文件操作的一般步驟:
1、在linux中要操作一個(gè)文件,一般是先open打開(kāi)一個(gè)文件,得到文件描述符,然后對(duì)文件進(jìn)行讀寫(xiě)操作(或其他操作),最后是close關(guān)閉文件即可。
2、強(qiáng)調(diào)一點(diǎn):我們對(duì)文件進(jìn)行操作時(shí),一定要先打開(kāi)文件,打開(kāi)成功之后才能操作,如果打開(kāi)失敗,就不用進(jìn)行后邊的操作了,最后讀寫(xiě)完成后,一定要關(guān)閉文件,否則會(huì)造成文件損壞。
3、文件平時(shí)是存放在塊設(shè)備中的文件系統(tǒng)文件中的,我們把這種文件叫靜態(tài)文件,當(dāng)我們?nèi)pen打開(kāi)一個(gè)文件時(shí),linux內(nèi)核做的操作包括:內(nèi)核在進(jìn)程中建立一個(gè)打開(kāi)文件的數(shù)據(jù)結(jié)構(gòu),記錄下我們打開(kāi)的這個(gè)文件;內(nèi)核在內(nèi)存中申請(qǐng)一段內(nèi)存,并且將靜態(tài)文件的內(nèi)容從塊設(shè)備中讀取到內(nèi)核中特定地址管理存放(叫動(dòng)態(tài)文件)。
4、打開(kāi)文件以后,以后對(duì)這個(gè)文件的讀寫(xiě)操作,都是針對(duì)內(nèi)存中的這一份動(dòng)態(tài)文件的,而并不是針對(duì)靜態(tài)文件的。當(dāng)然我們對(duì)動(dòng)態(tài)文件進(jìn)行讀寫(xiě)以后,此時(shí)內(nèi)存中動(dòng)態(tài)文件和塊設(shè)備文件中的靜態(tài)文件就不同步了,當(dāng)我們close關(guān)閉動(dòng)態(tài)文件時(shí),close內(nèi)部?jī)?nèi)核將內(nèi)存中的動(dòng)態(tài)文件的內(nèi)容去更新(同步)塊設(shè)備中的靜態(tài)文件。
5、為什么這么設(shè)計(jì),不直接對(duì)塊設(shè)備直接操作。
塊設(shè)備本身讀寫(xiě)非常不靈活,是按塊讀寫(xiě)的,而內(nèi)存是按字節(jié)單位操作的,而且可以隨機(jī)操作,很靈活。
3、重要概念:
文件描述符:
1、對(duì)于內(nèi)核而言,所有打開(kāi)文件都由文件描述符引用。文件描述符是一個(gè)非負(fù)整數(shù)。當(dāng)打開(kāi)一個(gè)現(xiàn)存文件或者創(chuàng)建一個(gè)新文件時(shí),內(nèi)核向進(jìn)程返回一個(gè)文件描述符。當(dāng)讀寫(xiě)一個(gè)文件時(shí),用open和creat返回的文件描述符標(biāo)識(shí)該文件,將其作為參數(shù)傳遞給read和write。
按照慣例,UNIX shell使用文件描述符0與進(jìn)程的標(biāo)準(zhǔn)輸入相結(jié)合,文件描述符1與標(biāo)準(zhǔn)輸出相結(jié)合,文件描述符2與標(biāo)準(zhǔn)錯(cuò)誤輸出相結(jié)合。STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO這幾個(gè)宏代替了0、1、2這幾個(gè)魔數(shù)。
2、文件描述符,這個(gè)數(shù)字在一個(gè)進(jìn)程中表示一個(gè)特定含義,當(dāng)我們open一個(gè)文件時(shí),操作系統(tǒng)在內(nèi)存中構(gòu)建了一些數(shù)據(jù)結(jié)構(gòu)來(lái)表示這個(gè)動(dòng)態(tài)文件,然后返回給應(yīng)用程序一個(gè)數(shù)字作為文件描述符,這個(gè)數(shù)字就和我們內(nèi)存中維護(hù)的這個(gè)動(dòng)態(tài)文件的這些數(shù)據(jù)結(jié)構(gòu)綁定上了,以后我們應(yīng)用程序如果要操作這個(gè)動(dòng)態(tài)文件,只需要用這個(gè)文件描述符區(qū)分。
3、文件描述符的作用域就是當(dāng)前進(jìn)程,出了這個(gè)進(jìn)程文件描述符就沒(méi)有意義了。
open函數(shù)打開(kāi)文件,打開(kāi)成功返回一個(gè)文件描述符,打開(kāi)失敗,返回-1。
補(bǔ)充:這里我們補(bǔ)充一點(diǎn),
1、學(xué)習(xí)linux過(guò)程中注意學(xué)會(huì)使用man手冊(cè),查詢(xún)幫助文檔。
2、man 1 xx查linux shell命令,man 2 xxx查API, man 3 xxx查庫(kù)函數(shù)
4、常用文件I/O操作的使用:
1、open函數(shù)(打開(kāi)文件操作)
需要用到的頭文件 #include#include #include 函數(shù)原型: int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); 返回值:若成功返回文件描述符,若出錯(cuò)返回 -1
對(duì)于open函數(shù)而言,僅當(dāng)創(chuàng)建新文件時(shí)才使用第三個(gè)參數(shù)。namepath是要打開(kāi)或創(chuàng)建的文件的名字。oflag參數(shù)可以用來(lái)說(shuō)明函數(shù)的多個(gè)選項(xiàng)。用下列一個(gè)或者多個(gè)常數(shù)進(jìn)行或運(yùn)算構(gòu)成oflag參數(shù)(這些參數(shù)定義在
open函數(shù)flags參數(shù)詳解:
讀寫(xiě)權(quán)限:O_RDONLY O_WRONLY O_RDWR
O_RDONLY 只讀打開(kāi)
O_WRONLY 只寫(xiě)打開(kāi)
O_RDWR 可讀可寫(xiě)打開(kāi)
當(dāng)我們附帶了權(quán)限后,打開(kāi)的文件就只能按照這種權(quán)限來(lái)操作。以上這三個(gè)常數(shù)中應(yīng)當(dāng)只指定一 個(gè)。下列常數(shù)是可選擇的:
O_CREAT 若文件不存在則創(chuàng)建它。使用此選項(xiàng)時(shí),需要同時(shí)說(shuō)明第三個(gè)參數(shù)mode,用其說(shuō)明該新文件的存取許可權(quán)限。
O_EXCL 如果同時(shí)指定了OCREAT,而文件已經(jīng)存在,則出錯(cuò)。這可測(cè)試一個(gè)文件是
否存在,如果不存在則創(chuàng)建此文件成為一個(gè)原子操作。3 . 11節(jié)將較詳細(xì)地說(shuō)明原子操作。
O_APPEND 每次寫(xiě)時(shí)都加到文件的尾端。
O_TRUNC 屬性去打開(kāi)文件時(shí),如果這個(gè)文件中本來(lái)是有內(nèi)容的,而且為只讀或只寫(xiě)成功打開(kāi),則將其長(zhǎng)度截短為0。
重點(diǎn):
一: 打開(kāi)存在并有內(nèi)容的文件時(shí):O_APPEND、O_TRUNC
(1)思考一個(gè)問(wèn)題:當(dāng)我們打開(kāi)一個(gè)已經(jīng)存在并且內(nèi)部有內(nèi)容的文件時(shí)會(huì)怎么樣?
可能結(jié)果1:新內(nèi)容會(huì)替代原來(lái)的內(nèi)容(原來(lái)的內(nèi)容就不見(jiàn)了,丟了)
可能結(jié)果2:新內(nèi)容添加在前面,原來(lái)的內(nèi)容繼續(xù)在后面
可能結(jié)果3:新內(nèi)容附加在后面,原來(lái)的內(nèi)容還在前面
可能結(jié)果4:不讀不寫(xiě)的時(shí)候,原來(lái)的文件中的內(nèi)容保持不變
(2)O_TRUNC屬性去打開(kāi)文件時(shí),如果這個(gè)文件中本來(lái)是有內(nèi)容的,則原來(lái)的內(nèi)容會(huì)被丟棄。這就對(duì)應(yīng)上面的結(jié)果1
(3)O_APPEND屬性去打開(kāi)文件時(shí),如果這個(gè)文件中本來(lái)是有內(nèi)容的,則新寫(xiě)入的內(nèi)容會(huì)接續(xù)到原來(lái)內(nèi)容的后面,對(duì)應(yīng)結(jié)果3
(4)默認(rèn)不使用O_APPEND和O_TRUNC屬性時(shí)就是結(jié)果4
(5)如果O_APPEND和O_TRUNC同時(shí)出現(xiàn)會(huì)會(huì)清空文件。
二:打開(kāi)不存在的文件時(shí):O_CREAT、O_EXCL
(1)思考:當(dāng)我們?nèi)ゴ蜷_(kāi)一個(gè)并不存在的文件時(shí)會(huì)怎樣?當(dāng)我們open打開(kāi)一個(gè)文件時(shí)如果這個(gè)文件名不存在則會(huì)打開(kāi)文件錯(cuò)誤。
(2)vi或者windows下的notepad++,都可以直接打開(kāi)一個(gè)尚未存在的文件。
(3)open的flag O_CREAT就是為了應(yīng)對(duì)這種打開(kāi)一個(gè)并不存在的文件的。O_CREAT就表示我們當(dāng)前打開(kāi)的文件并不存在,我們是要去創(chuàng)建并且打開(kāi)它。
(4)思考:當(dāng)我們open使用了O_CREAT,但是文件已經(jīng)存在的情況下會(huì)怎樣?經(jīng)過(guò)實(shí)驗(yàn)驗(yàn)證發(fā)現(xiàn)結(jié)果是報(bào)錯(cuò)。
(5)結(jié)論:open中加入O_CREAT后,不管原來(lái)這個(gè)文件存在與否都能打開(kāi)成功,如果原來(lái)這個(gè)文件不存在則創(chuàng)建一個(gè)空的新文件,如果原來(lái)這個(gè)文件存在則會(huì)重新創(chuàng)建這個(gè)文件,原來(lái)的內(nèi)容會(huì)被消除掉(有點(diǎn)類(lèi)似于先刪除原來(lái)的文件再創(chuàng)建一個(gè)新的)
(6)這樣可能帶來(lái)一個(gè)問(wèn)題?我們本來(lái)是想去創(chuàng)建一個(gè)新文件的,但是把文件名搞錯(cuò)了弄成了一個(gè)老文件名,結(jié)果老文件就被意外修改了。我們希望的效果是:如果我CREAT要?jiǎng)?chuàng)建的是一個(gè)已經(jīng)存在的名字的文件,則給我報(bào)錯(cuò),不要去創(chuàng)建。
(7)這個(gè)效果就要靠O_EXCL標(biāo)志和O_CREAT標(biāo)志來(lái)結(jié)合使用。當(dāng)這連個(gè)標(biāo)志一起的時(shí)候,則沒(méi)有文件時(shí)創(chuàng)建文件,有這個(gè)文件時(shí)會(huì)報(bào)錯(cuò)提醒我們。
(8)open函數(shù)在使用O_CREAT標(biāo)志去創(chuàng)建文件時(shí),可以使用第三個(gè)參數(shù)mode來(lái)指定要?jiǎng)?chuàng)建的文件的權(quán)限。mode使用4個(gè)數(shù)字來(lái)指定權(quán)限的,其中后面三個(gè)很重要,對(duì)應(yīng)我們要?jiǎng)?chuàng)建的這個(gè)文件的權(quán)限標(biāo)志。譬如一般創(chuàng)建一個(gè)可讀可寫(xiě)不可執(zhí)行的文件就用0666
O_NOCTTY 如果pathname指的是終端設(shè)備,則不將此設(shè)備分配作為此進(jìn)程的控制終端。9.6節(jié)將說(shuō)明控制終端。
O_NONBLOCK 如果pathname指的是一個(gè)FIFO、一個(gè)塊特殊文件或一個(gè)字符特殊文件,則此選擇項(xiàng)為此文件的本次打開(kāi)操作和后續(xù)的I/O操作設(shè)置非阻塞方式。(只用于設(shè)備文件,而不用于普通文件。)
(1)阻塞與非阻塞。如果一個(gè)函數(shù)是阻塞式的,則我們調(diào)用這個(gè)函數(shù)時(shí)當(dāng)前進(jìn)程有可能被卡?。ㄗ枞。瑢?shí)質(zhì)是這個(gè)函數(shù)內(nèi)部要完成的事情條件不具備,當(dāng)前沒(méi)法做,要等待條件成熟),函數(shù)被阻塞住了就不能立刻返回;如果一個(gè)函數(shù)是非阻塞式的那么我們調(diào)用這個(gè)函數(shù)后一定會(huì)立即返回,但是函數(shù)有沒(méi)有完成任務(wù)不一定。
(2)阻塞和非阻塞是兩種不同的設(shè)計(jì)思路,并沒(méi)有好壞??偟膩?lái)說(shuō),阻塞式的結(jié)果有保障但是時(shí)間沒(méi)保障;非阻塞式的時(shí)間有保障但是結(jié)果沒(méi)保障。
(3)操作系統(tǒng)提供的API和由API封裝而成的庫(kù)函數(shù),有很多本身就是被設(shè)計(jì)為阻塞式或者非阻塞式的,所以我們應(yīng)用程度調(diào)用這些函數(shù)的時(shí)候心里得非常清楚。
(4)我們打開(kāi)一個(gè)文件默認(rèn)就是阻塞式的,如果你希望以非阻塞的方式打開(kāi)文件,則flag中要加O_NONBLOCK標(biāo)志。
O_SYNC 使每次write都等到物理I/O操作完成
(1)write阻塞等待底層完成寫(xiě)入才返回到應(yīng)用層。
(2)無(wú)O_SYNC時(shí)write只是將內(nèi)容寫(xiě)入底層緩沖區(qū)即可返回,然后底層(操作系統(tǒng)中負(fù)責(zé)實(shí)現(xiàn)open、write這些操作的那些代碼,也包含OS中讀寫(xiě)硬盤(pán)等底層硬件的代碼)在合適的時(shí)候會(huì)將buf中的內(nèi)容一次性的同步到硬盤(pán)中。這種設(shè)計(jì)是為了提升硬件操作的性能和銷(xiāo)量,提升硬件壽命;但是有時(shí)候我們希望硬件不好等待,直接將我們的內(nèi)容寫(xiě)入硬盤(pán)中,這時(shí)候就可以用O_SYNC標(biāo)志。
2、creat函數(shù)(也可用creat函數(shù)創(chuàng)建一個(gè)新文件)
需要用到的頭文件 #include#include #include 函數(shù)原型: int creat(const char *pathname, mode_t mode); 返回:若成功為只寫(xiě)打開(kāi)的文件描述符,若出錯(cuò)為-1
此函數(shù)等效于 open(pathname,O_WRONLY|O_CRAT|O_TRUNC,mode);
3、read 函數(shù)(用read函數(shù)從打開(kāi)的文件中讀取數(shù)據(jù))
需要的頭文件: #include函數(shù)原型: ssize_t read(int filedes,void *buffer,size_t nbytes); 返回值:讀取到字節(jié)數(shù),若已到文件尾0,則返回-1如果read成功,則返回讀取到字節(jié)數(shù)。如已到文件結(jié)尾返回0;
有多種情況可使實(shí)際讀到的字節(jié)數(shù)少于要求讀字節(jié)數(shù):
1、讀取普通文件時(shí),在讀到要求字節(jié)數(shù)之前已經(jīng)到達(dá)了文件結(jié)尾。例如,若在到達(dá)文件尾端之前還有30個(gè)字節(jié),而要求讀100個(gè)字節(jié),則read返回30,下一次調(diào)用read時(shí),它將返回0(文件尾端)。
2、當(dāng)從終端設(shè)備讀時(shí),通常一次最多讀一行
3、當(dāng)從網(wǎng)絡(luò)讀時(shí),網(wǎng)絡(luò)中緩存機(jī)構(gòu)可能造成返回值小于所要求讀的字節(jié)數(shù)。
4、某些面向記錄的設(shè)備,例如磁帶,一次最多返回一個(gè)記錄。
4、write函數(shù)(用write函數(shù)向打開(kāi)的文件寫(xiě)數(shù)據(jù))
需要用到的頭文件 #include函數(shù)原型: ssize_t write(int filedes,const void buffer,size_t nbytes); 返回值:若成功返回已寫(xiě)字節(jié)數(shù),若出錯(cuò)返回-1 其返回值通常與參數(shù)nbyte的值不同,否則表示出錯(cuò)。write出錯(cuò)的一個(gè)常見(jiàn)原因是:磁盤(pán)已寫(xiě)滿,或者超過(guò)了對(duì)一個(gè)給定進(jìn)程的文件長(zhǎng)度限制。
寫(xiě)入用write系統(tǒng)調(diào)用,write的原型和理解方法和read相似
5、close函數(shù)(可以用close函數(shù)關(guān)閉一個(gè)打開(kāi)是文件)
需要用到的頭文件: #include函數(shù)原型: int close(int filedes); 返回值:成功返回0,若出錯(cuò)返回-1
當(dāng)一個(gè)進(jìn)程終止時(shí),它所有的打開(kāi)的文件都是由內(nèi)核自動(dòng)關(guān)閉。
6、lseek函數(shù)
每個(gè)打開(kāi)文件都有一個(gè)與其相關(guān)聯(lián)的“當(dāng)前文件位移量”。它是一個(gè)非負(fù)整數(shù),用以度量從文件開(kāi)始處計(jì)算的字節(jié)數(shù)。
需要用到的頭文件 #include#include 函數(shù)原型: off_t lseek(int filedes,off_t offset,int whence); 返回值:若成功為新的文件的位移,若出錯(cuò)位-1
對(duì)參數(shù)offset 的解釋與參數(shù)whence的值有關(guān)。
若whence是SEEK_SET,則將該文件的位移量設(shè)置為距文件開(kāi)始處offset 個(gè)字節(jié)。
若whence是SEEK_CUR,則將該文件的位移量設(shè)置為其當(dāng)前值加offset, offset可為正或負(fù)。
若whence是SEEK_END,則將該文件的位移量設(shè)置為文件長(zhǎng)度加offset, offset可為正或負(fù)。*******************************************************************************************
重要概念:
(1)文件指針:當(dāng)我們要對(duì)一個(gè)文件進(jìn)行讀寫(xiě)時(shí),一定需要先打開(kāi)這個(gè)文件,所以我們讀寫(xiě)的所有文件都是動(dòng)態(tài)文件。動(dòng)態(tài)文件在內(nèi)存中的形態(tài)就是文件流的形式。
(2)文件流很長(zhǎng),里面有很多個(gè)字節(jié)。那我們當(dāng)前正在操作的是哪個(gè)位置?GUI模式下的軟件用光標(biāo)來(lái)標(biāo)識(shí)這個(gè)當(dāng)前正在操作的位置,這是給人看的。
(3)在動(dòng)態(tài)文件中,我們會(huì)通過(guò)文件指針來(lái)表征這個(gè)正在操作的位置。所謂文件指針,就是我們文件管理表這個(gè)結(jié)構(gòu)體里面的一個(gè)指針。所以文件指針其實(shí)是vnode中的一個(gè)元素。這個(gè)指針表示當(dāng)前我們正在操作文件流的哪個(gè)位置。這個(gè)指針不能被直接訪問(wèn),linux系統(tǒng)用lseek函數(shù)來(lái)訪問(wèn)這個(gè)文件指針。
(4)當(dāng)我們打開(kāi)一個(gè)空文件時(shí),默認(rèn)情況下文件指針指向文件流的開(kāi)始。所以這時(shí)候去write時(shí)寫(xiě)入就是從文件開(kāi)頭開(kāi)始的。write和read函數(shù)本身自帶移動(dòng)文件指針的功能,所以當(dāng)我write了n個(gè)字節(jié)后,文件指針會(huì)自動(dòng)向后移動(dòng)n位。如果需要人為的隨意更改文件指針,那就只能通過(guò)lseek函數(shù)了
(5)read和write函數(shù)都是從當(dāng)前文件指針處開(kāi)始操作的,所以當(dāng)我們用lseek顯式的將文件指針移動(dòng)后,那么再去read/write時(shí)就是從移動(dòng)過(guò)后的位置開(kāi)始的。
(6)回顧前面一節(jié)中我們從空文件,先write寫(xiě)了12字節(jié),然后read時(shí)是空的(但是此時(shí)我們打開(kāi)文件后發(fā)現(xiàn)12字節(jié)確實(shí)寫(xiě)進(jìn)來(lái)了)。
lseek函數(shù)幾個(gè)用途:
1、用lseek計(jì)算文件長(zhǎng)度
(1)linux中并沒(méi)有一個(gè)函數(shù)可以直接返回一個(gè)文件的長(zhǎng)度。但是我們做項(xiàng)目時(shí)經(jīng)常會(huì)需要知道一個(gè)文件的長(zhǎng)度,怎么辦?自己利用lseek來(lái)寫(xiě)一個(gè)函數(shù)得到文件長(zhǎng)度即可。
#include#include #include #include #include #include #include #include typedef int file_t; #define MAXLENG 1024 int main(int argc,char *argv[]) { file_t fd = -1; ssize_t ret = -1; if(2 != argc) { fprintf(stdout,"usage: %s filename \n",argv[0]); _exit(-1); } char buffer[MAXLENG] = {0}; fd = open(argv[1],O_RDONLY); //文件打開(kāi)成功,文件指針指向文件開(kāi)頭 if(-1 == fd) { perror("open file error:"); _exit(-1); } else { fprintf(stdout,"文件打開(kāi)成功\n"); ret = lseek(fd,0,SEEK_END); } fprintf(stdout,"文件長(zhǎng)度是: %d\n",ret); return 0; }
2、用lseek構(gòu)建空洞文件
(1)空洞文件就是這個(gè)文件中有一段是空的。
(2)普通文件中間是不能有空的,因?yàn)槲覀僿rite時(shí)文件指針是依次從前到后去移動(dòng)的,不可能繞過(guò)前面直接到后面。
(3)我們打開(kāi)一個(gè)文件后,用lseek往后跳過(guò)一段,再write寫(xiě)入一段,就會(huì)構(gòu)成一個(gè)空洞文件。
(4)空洞文件方法對(duì)多線程共同操作文件是及其有用的。有時(shí)候我們創(chuàng)建一個(gè)很大的文件,如果從頭開(kāi)始依次構(gòu)建時(shí)間很長(zhǎng)。有一種思路就是將文件分為多段,然后多線程來(lái)操作每個(gè)線程負(fù)責(zé)其中一段的寫(xiě)入。
補(bǔ)充:
1、exit、_exit、_Exit退出進(jìn)程
(1)當(dāng)我們程序在前面步驟操作失敗導(dǎo)致后面的操作都沒(méi)有可能進(jìn)行下去時(shí),應(yīng)該在前面的錯(cuò)誤監(jiān)測(cè)中結(jié)束整個(gè)程序,不應(yīng)該繼續(xù)讓程序運(yùn)行下去了。
(2)我們?nèi)绾瓮顺龀绦颍?/p>
第一種;在main用return,一般原則是程序正常終止return 0,如果程序異常終止則return -1。
第一種:正式終止進(jìn)程(程序)應(yīng)該使用exit或者_(dá)exit或者_(dá)Exit之一。
2、文件讀寫(xiě)的一些細(xì)節(jié)
<1> 、errno和perror
(1)errno就是error number,意思就是錯(cuò)誤號(hào)碼。linux系統(tǒng)中對(duì)各種常見(jiàn)錯(cuò)誤做了個(gè)編號(hào),當(dāng)函數(shù)執(zhí)行錯(cuò)誤時(shí),函數(shù)會(huì)返回一個(gè)特定的errno編號(hào)來(lái)告訴我們這個(gè)函數(shù)到底哪里錯(cuò)了。
(2)errno是由OS來(lái)維護(hù)的一個(gè)全局變量,任何OS內(nèi)部函數(shù)都可以通過(guò)設(shè)置errno來(lái)告訴上層調(diào)用者究竟剛才發(fā)生了一個(gè)什么錯(cuò)誤。
(3)errno本身實(shí)質(zhì)是一個(gè)int類(lèi)型的數(shù)字,每個(gè)數(shù)字編號(hào)對(duì)應(yīng)一種錯(cuò)誤。當(dāng)我們只看errno時(shí)只能得到一個(gè)錯(cuò)誤編號(hào)數(shù)字(譬如-37),不適應(yīng)于人看。
(4)linux系統(tǒng)提供了一個(gè)函數(shù)perror(意思print error),perror函數(shù)內(nèi)部會(huì)讀取errno并且將這個(gè)不好認(rèn)的數(shù)字直接給轉(zhuǎn)成對(duì)應(yīng)的錯(cuò)誤信息字符串,然后print打印出來(lái)。
<2>、read和write的count
(1)count和返回值的關(guān)系。count參數(shù)表示我們想要寫(xiě)或者讀的字節(jié)數(shù),返回值表示實(shí)際完成的要寫(xiě)或者讀的字節(jié)數(shù)。實(shí)現(xiàn)的有可能等于想要讀寫(xiě)的,也有可能小于(說(shuō)明沒(méi)完成任務(wù))
(2)count再和阻塞非阻塞結(jié)合起來(lái),就會(huì)更加復(fù)雜。如果一個(gè)函數(shù)是阻塞式的,則我們要讀取30個(gè),結(jié)果暫時(shí)只有20個(gè)時(shí)就會(huì)被阻塞住,等待剩余的10個(gè)可以讀。
(3)有時(shí)候我們寫(xiě)正式程序時(shí),我們要讀取或者寫(xiě)入的是一個(gè)很龐大的文件(譬如文件有2MB),我們不可能把count設(shè)置為2*1024*1024,而應(yīng)該去把count設(shè)置為一個(gè)合適的數(shù)字(譬如2048、4096),然后通過(guò)多次讀取來(lái)實(shí)現(xiàn)全部讀完。
<3>、文件IO效率和標(biāo)準(zhǔn)IO
(1)文件IO就指的是我們當(dāng)前在講的open、close、write、read等API函數(shù)構(gòu)成的一套用來(lái)讀寫(xiě)文件的體系,這套體系可以很好的完成文件讀寫(xiě),但是效率并不是最高的。
(2)應(yīng)用層C語(yǔ)言庫(kù)函數(shù)提供了一些用來(lái)做文件讀寫(xiě)的函數(shù)列表,叫標(biāo)準(zhǔn)IO。標(biāo)準(zhǔn)IO由一系列的C庫(kù)函數(shù)構(gòu)成(fopen、fclose、fwrite、fread),這些標(biāo)準(zhǔn)IO函數(shù)其實(shí)是由文件IO封裝而來(lái)的(fopen內(nèi)部其實(shí)調(diào)用的還是open,fwrite內(nèi)部還是通過(guò)write來(lái)完成文件寫(xiě)入的)。標(biāo)準(zhǔn)IO加了封裝之后主要是為了在應(yīng)用層添加一個(gè)緩沖機(jī)制,這樣我們通過(guò)fwrite寫(xiě)入的內(nèi)容不是直接進(jìn)入內(nèi)核中的buf,而是先進(jìn)入應(yīng)用層標(biāo)準(zhǔn)IO庫(kù)自己維護(hù)的buf中,然后標(biāo)準(zhǔn)IO庫(kù)自己根據(jù)操作系統(tǒng)單次write的最佳count來(lái)選擇好的時(shí)機(jī)來(lái)完成write到內(nèi)核中的buf(內(nèi)核中的buf再根據(jù)硬盤(pán)的特性來(lái)選擇好的實(shí)際去最終寫(xiě)入硬盤(pán)中)。
3、linux系統(tǒng)如何管理文件
<1>、硬盤(pán)中的靜態(tài)文件和inode(i節(jié)點(diǎn))
(1)文件平時(shí)都在存放在硬盤(pán)中的,硬盤(pán)中存儲(chǔ)的文件以一種固定的形式存放的,我們叫靜 態(tài)文件。
(2)一塊硬盤(pán)中可以分為兩大區(qū)域:一個(gè)是硬盤(pán)內(nèi)容管理表項(xiàng),另一個(gè)是真正存儲(chǔ)內(nèi)容的區(qū)域。操作系統(tǒng)訪問(wèn)硬盤(pán)時(shí)是先去讀取硬盤(pán)內(nèi)容管理表,從中找到我們要訪問(wèn)的那個(gè)文件的扇區(qū)級(jí)別的信息,然后再用這個(gè)信息去查詢(xún)真正存儲(chǔ)內(nèi)容的區(qū)域,最后得到我們要的文件。
(3)操作系統(tǒng)最初拿到的信息是文件名,最終得到的是文件內(nèi)容。第一步就是去查詢(xún)硬盤(pán)內(nèi)容管理表,這個(gè)管理表中以文件為單位記錄了各個(gè)文件的各種信息,每一個(gè)文件有一個(gè)信息列表(我們叫inode,i節(jié)點(diǎn),其實(shí)質(zhì)是一個(gè)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體有很多元素,每個(gè)元素記錄了這個(gè)文件的一些信息,其中就包括文件名、文件在硬盤(pán)上對(duì)應(yīng)的扇區(qū)號(hào)、塊號(hào)那些東西·····)
強(qiáng)調(diào):硬盤(pán)管理的時(shí)候是以文件為單位的,每個(gè)文件一個(gè)inode,每個(gè)inode有一個(gè)數(shù)字編號(hào),對(duì)應(yīng)一個(gè)結(jié)構(gòu)體,結(jié)構(gòu)體中記錄了各種信息。
(4)聯(lián)系平時(shí)實(shí)踐,大家格式化硬盤(pán)(U盤(pán))時(shí)發(fā)現(xiàn)有:快速格式化和底層格式化??焖俑袷交浅??,格式化一個(gè)32GB的U盤(pán)只要1秒鐘,普通格式化格式化速度慢。這兩個(gè)的差異?其實(shí)快速格式化就是只刪除了U盤(pán)中的硬盤(pán)內(nèi)容管理表(其實(shí)就是inode),真正存儲(chǔ)的內(nèi)容沒(méi)有動(dòng)。這種格式化的內(nèi)容是有可能被找回的。
<2>、內(nèi)存中被打開(kāi)的文件和vnode(v節(jié)點(diǎn))
(1)一個(gè)程序的運(yùn)行就是一個(gè)進(jìn)程,我們?cè)诔绦蛑写蜷_(kāi)的文件就屬于某個(gè)進(jìn)程。每個(gè)進(jìn)程都有一個(gè)數(shù)據(jù)結(jié)構(gòu)用來(lái)記錄這個(gè)進(jìn)程的所有信息(叫進(jìn)程信息表),表中有一個(gè)指針會(huì)指向一個(gè)文件管理表,文件管理表中記錄了當(dāng)前進(jìn)程打開(kāi)的所有文件及其相關(guān)信息。文件管理表中用來(lái)索引各個(gè)打開(kāi)的文件的index就是文件描述符fd,我們最終找到的就是一個(gè)已經(jīng)被打開(kāi)的文件的管理結(jié)構(gòu)體vnode
(2)一個(gè)vnode中就記錄了一個(gè)被打開(kāi)的文件的各種信息,而且我們只要知道這個(gè)文件的fd,就可以很容易的找到這個(gè)文件的vnode進(jìn)而對(duì)這個(gè)文件進(jìn)行各種操作。
<3>、文件與流的概念
(1)流(stream)對(duì)應(yīng)自然界的水流。文件操作中,文件類(lèi)似是一個(gè)大包裹,里面裝了一堆字符,但是文件被讀出/寫(xiě)入時(shí)都只能一個(gè)字符一個(gè)字符的進(jìn)行,而不能一股腦兒的讀寫(xiě),那么一個(gè)文件中N多的個(gè)字符被挨個(gè)一次讀出/寫(xiě)入時(shí),這些字符就構(gòu)成了一個(gè)字符流。
(2)流這個(gè)概念是動(dòng)態(tài)的,不是靜態(tài)的。
(3)編程中提到流這個(gè)概念,一般都是IO相關(guān)的。所以經(jīng)常叫IO流。文件操作時(shí)就構(gòu)成了一個(gè)IO流。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。