文件描述符
進(jìn)程每打開(kāi)一個(gè)文件的時(shí)候,會(huì)獲得該文件的文件描述符,而后續(xù)的讀寫操作都把文件描述符作為參數(shù)。在用戶空間或者內(nèi)核空間,都是通過(guò)文件描述符來(lái)唯一地索引一個(gè)打開(kāi)的文件。文件描述符使用int類型表示,文件描述符的范圍從0開(kāi)始,到上限值-1,默認(rèn)情況下,上限值為1024,也就是說(shuō),進(jìn)程默認(rèn)情況下最多可以打開(kāi)1024個(gè)文件。負(fù)數(shù)是不合法的文件描述符,當(dāng)函數(shù)調(diào)用出錯(cuò)時(shí),返回的文件描述符為-1。
每個(gè)進(jìn)程都至少包含三個(gè)文件描述符:
遵循Linux一切皆文件的概念,文件描述符除了訪問(wèn)普通文件外,幾乎能夠訪問(wèn)任何能夠讀寫的東西。包括設(shè)備文件、管道、先進(jìn)先出緩沖區(qū)、套接字等。
open()系統(tǒng)調(diào)用
對(duì)文件進(jìn)行讀寫之前,必須先打開(kāi)文件。Linux提供了系統(tǒng)調(diào)用open()。open()有兩個(gè)函數(shù)原型:
兩個(gè)函數(shù)均可用來(lái)打開(kāi)文件,第二個(gè)函數(shù)比第一個(gè)多了參數(shù)mode,mode指定文件的權(quán)限---當(dāng)創(chuàng)建新文件的時(shí)候才需要。如果文件打開(kāi)成功,則返回文件描述符,指向pathname所指定的文件。flags參數(shù)用于指定打開(kāi)的方式,它支持三種訪問(wèn)模式:
flags參數(shù)還可以與下面的值進(jìn)行按位或運(yùn)算,修改打開(kāi)文件的行為:
舉個(gè)例子,下面的句子表示:以寫的方式打開(kāi)文件,如果文件不存在,則創(chuàng)建新的文件,并且文件的內(nèi)容為空:
int fd ;
fd = open("file.txt",O_WRONLY|O_CREAT|O_TRUNC,0644);
這里的0644指定了新創(chuàng)建的文件訪問(wèn)權(quán)限,參數(shù)mode的取值如下:
實(shí)際上最終寫入磁盤的文件訪問(wèn)權(quán)限是由mode參數(shù)和用戶的文件創(chuàng)建掩碼(umask)執(zhí)行按位與操作得到的。舉個(gè)例子:
按理來(lái)說(shuō),創(chuàng)建出來(lái)的文件的訪問(wèn)權(quán)限應(yīng)該是-rwxrwxrwx,而查看后發(fā)現(xiàn)其實(shí)不是:
ls -l TEST.txt
-rwxrwxr-x 1 huanzhewu huanzhewu 0 8月 30 21:29 TEST.txt【權(quán)限為0775】
查看當(dāng)前的掩碼:
$ umask
0002
可以發(fā)現(xiàn) 0775 = 0777 ^ (~0002) ,所以0775才是最后的文件訪問(wèn)權(quán)限。umask是進(jìn)程級(jí)屬性,通過(guò)調(diào)用umask()函數(shù)來(lái)修改,支持用戶修改新創(chuàng)建的文件和目錄的權(quán)限。
總結(jié)起來(lái)可以得到這樣一條公式:
newmode = mode ^ (~ umask)
總結(jié)一下:至此,我們了解了文件打開(kāi)所提供的兩個(gè)系統(tǒng)調(diào)用函數(shù)open(),了解了打開(kāi)文件的方式、新建文件的訪問(wèn)權(quán)限設(shè)置。如果文件打開(kāi)成功,那么將返回一個(gè)文件描述符,這是一個(gè)非零整數(shù)(因?yàn)?,1,2是進(jìn)行已經(jīng)擁有的文件描述符),否則函數(shù)將返回-1
creat()系統(tǒng)調(diào)用
顧名思義,creat函數(shù)用來(lái)創(chuàng)建一個(gè)文件,不過(guò)我們可能產(chǎn)生疑問(wèn):前面的open函數(shù)使用一些選項(xiàng)后,不是也可以創(chuàng)建新文件嗎?沒(méi)錯(cuò),creat函數(shù)完全等價(jià)與下面的open語(yǔ)句:
由于選項(xiàng)O_WRONLY|O_CREAT|O_TRUNC組合經(jīng)常使用,因而系統(tǒng)調(diào)用專門使用creat函數(shù)來(lái)提供這個(gè)功能。creat函數(shù)的原型如下:
其中參數(shù)的描述與open的參數(shù)一致,這里不再贅述。
read()系統(tǒng)調(diào)用
文件打開(kāi)后,就能夠讀文件了。read()是最基礎(chǔ)、最常見(jiàn)的讀取文件的機(jī)制。read的函數(shù)原型為:
fd 為文件描述符。每次調(diào)用read函數(shù)時(shí),會(huì)從fd指向的文件的當(dāng)前偏移(或稱文件位置)開(kāi)始讀取count字節(jié)到buf所指的的內(nèi)存中。隨著文件的讀取,fd的文件位置指針會(huì)向前移動(dòng)。關(guān)于read的讀取,會(huì)出現(xiàn)很多需要思考的問(wèn)題:
問(wèn)題一:如果文件長(zhǎng)度為0
問(wèn)題二:如果文件長(zhǎng)度不夠count長(zhǎng)度
問(wèn)題三:如果讀取時(shí),read被信號(hào)中斷了
我們一一來(lái)看:
問(wèn)題1屬于“沒(méi)有數(shù)據(jù)可讀”,此時(shí)read調(diào)用會(huì)阻塞,直到有數(shù)據(jù)可讀;
問(wèn)題2屬于到達(dá)數(shù)據(jù)結(jié)尾(EOF),此時(shí)read調(diào)用返回0;
問(wèn)題三,read調(diào)用返回大于0小于count的值;如果在讀取任何數(shù)據(jù)之前被信號(hào)中斷,則返回-1,同時(shí)把errno設(shè)置為EINTR。
再來(lái)看看問(wèn)題1,當(dāng)文件沒(méi)有數(shù)據(jù)可以讀時(shí)(一開(kāi)始就沒(méi)有),read調(diào)用會(huì)被阻塞,直到文件有數(shù)據(jù)可以讀,這是一種阻塞I/O。如果文件以O(shè)_NONBLOCK模式打開(kāi),則文件為非阻塞模式,當(dāng)文件沒(méi)有數(shù)據(jù)可以讀時(shí),read系統(tǒng)調(diào)用返回-1,并把errno設(shè)置為EAGAIN。
除了errno被設(shè)置為EINTR與EAGAIN,其他情況下都是出現(xiàn)嚴(yán)重的文件讀取錯(cuò)誤,重新執(zhí)行讀操作不會(huì)成功。
write() 系統(tǒng)調(diào)用
write的函數(shù)原型為:
write的返回值比較簡(jiǎn)單:
寫入失敗返回-1 ,同時(shí)設(shè)置errno的值
寫入成功返回成功寫入的字節(jié)數(shù)。
返回0時(shí)沒(méi)有特殊含義,僅表示寫入了0個(gè)字節(jié)的內(nèi)容。
對(duì)于普通文件,write基本能保證每次執(zhí)行調(diào)用能夠?qū)懭肴康膬?nèi)容。對(duì)于其他文件如socket,需要進(jìn)行循環(huán)寫,保證所有的字節(jié)都寫入了文件中:
同樣的,當(dāng)以非阻塞的模式打開(kāi)文件時(shí)(O_NONBLOCK),系統(tǒng)調(diào)用write()會(huì)返回-1,并把errno設(shè)置為EAGAIN。
系統(tǒng)調(diào)用write()時(shí),數(shù)據(jù)從用戶空間的緩沖區(qū)中拷貝到了內(nèi)核空間的緩沖區(qū),但并沒(méi)有立即把數(shù)據(jù)寫入磁盤中,這稱為延遲寫。延遲寫的問(wèn)題在于,如果在數(shù)據(jù)真正寫入磁盤之前系統(tǒng)崩潰了,則數(shù)據(jù)可能丟失。內(nèi)核設(shè)置了一個(gè)時(shí)間,在該時(shí)間內(nèi)將內(nèi)核空間緩沖區(qū)上的數(shù)據(jù)寫入磁盤,該時(shí)間稱為"最大存放時(shí)效"。Linux系統(tǒng)也支持強(qiáng)制文件立即寫入磁盤上,這在后面介紹。
close()系統(tǒng)調(diào)用
程序完成文件的讀寫后,調(diào)用close函數(shù)關(guān)閉文件描述符與文件之間的連接,使得文件描述符可以被重用。close的函數(shù)原型為:
#incldue
int close(int fd);
文件關(guān)閉成功返回0,出錯(cuò)返回-1,并設(shè)置相應(yīng)的errno。文件成功關(guān)閉并不以為著該文件的數(shù)據(jù)已經(jīng)被寫入磁盤。
總結(jié):本文簡(jiǎn)單介紹了文件的打開(kāi)、創(chuàng)建、讀寫、關(guān)閉操作,介紹了一些常用的open參數(shù)選項(xiàng),creat與open的等價(jià)性,循環(huán)讀、循環(huán)寫的必要性,也關(guān)注了各個(gè)系統(tǒng)調(diào)用的返回值含義,了解如何設(shè)置非阻塞讀寫,并簡(jiǎn)單提到了延遲寫的問(wèn)題,在后續(xù)的文件中將介紹同步I/O的內(nèi)容。
責(zé)任編輯:haq
-
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209323 -
編程
+關(guān)注
關(guān)注
88文章
3614瀏覽量
93686
原文標(biāo)題:Linux系統(tǒng)編程:基本 I/O 系統(tǒng)調(diào)用
文章出處:【微信號(hào):cyuyanxuexi,微信公眾號(hào):C語(yǔ)言編程學(xué)習(xí)基地】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論