文件描述符的本質(zhì)
2161
1. 文件描述符的本質(zhì)是數(shù)組元素的下標(biāo)
右側(cè)的表稱為i節(jié)點(diǎn)表,在整個(gè)系統(tǒng)中只有1張吮播。該表可以視為結(jié)構(gòu)體數(shù)組王浴,該數(shù)組的一個(gè)元素對(duì)應(yīng)于一個(gè)物理文件姨伟。
中間的表稱為文件表,在整個(gè)系統(tǒng)中只有1張哮独。該表可以視為結(jié)構(gòu)體數(shù)組,一個(gè)結(jié)構(gòu)體中有很多字段察藐,其中有3個(gè)字段比較重要:
file status flags:用于記錄文件被打開來讀的皮璧,還是寫的。其實(shí)記錄的就是open調(diào)用中用戶指定的第2個(gè)參數(shù)
current file offset:用于記錄文件的當(dāng)前讀寫位置(指針)分飞。正是由于此字段的存在悴务,使得一個(gè)文件被打開并讀取后,下一次讀取將從上一次讀取的字符后開始讀取
v-node ptr:該字段是指針浸须,指向右側(cè)表的一個(gè)元素惨寿,從而關(guān)聯(lián)了物理文件。
左側(cè)的表稱為文件描述符表删窒,每個(gè)進(jìn)程有且僅有1張裂垦。該表可以視為指針數(shù)組,數(shù)組的元素指向文件表的一個(gè)元素肌索。最重要的是:數(shù)組元素的下標(biāo)就是大名鼎鼎的文件描述符蕉拢。
open系統(tǒng)調(diào)用執(zhí)行的操作:新建一個(gè)i節(jié)點(diǎn)表元素,讓其對(duì)應(yīng)打開的物理文件(如果對(duì)應(yīng)于該物理文件的i節(jié)點(diǎn)元素已經(jīng)建立,就不做任何操作)晕换;新建一個(gè)文件表的元素午乓,根據(jù)open的第2個(gè)參數(shù)設(shè)置file status flags字段,將current file offset字段置0闸准,將v-node ptr指向剛建立的i節(jié)點(diǎn)表元素益愈;在文件描述符表中,尋找1個(gè)尚未使用的元素夷家,在該元素中填入一個(gè)指針值蒸其,讓其指向剛建立的文件表元素。最重要的是:將該元素的下標(biāo)作為open的返回值返回库快。
這樣一來摸袁,當(dāng)調(diào)用read(write)時(shí),根據(jù)傳入的文件描述符义屏,OS就可以找到對(duì)應(yīng)的文件描述符表元素靠汁,進(jìn)而找到文件表的元素,進(jìn)而找到i節(jié)點(diǎn)表元素闽铐,從而完成對(duì)物理文件的讀寫蝶怔。
fork會(huì)導(dǎo)致子進(jìn)程繼承父進(jìn)程打開的文件描述符,其本質(zhì)是將父進(jìn)程的整個(gè)文件描述符表復(fù)制一份阳啥,放到子進(jìn)程的PCB中添谊。因此父、子進(jìn)程中相同文件描述符(文件描述符為整數(shù))指向的是同一個(gè)文件表元素察迟,這將導(dǎo)致父(子)進(jìn)程讀取文件后斩狱,子(父)進(jìn)程將讀取同一文件的后續(xù)內(nèi)容。
案例分析(forkfd.c):
#include?
#include?
#include?
#include?
#include?
#include?
intmain(void)
{
intfd,?pid,?status;
charbuf[10];
if((fd?=?open("./test.txt",?O_RDONLY))?<?0)?{
perror("open");??exit(-1);
}
if((pid?=?fork())?<?0)?{
perror("fork");??exit(-1);
}elseif(pid?==?0)?{//child
read(fd,?buf,?2);
write(STDOUT_FILENO,?buf,?2);
}else{//parent
sleep(2);
lseek(fd,?SEEK_CUR,?1);
read(fd,?buf,?3);
write(STDOUT_FILENO,?buf,?3);
write(STDOUT_FILENO,"\n",?1);
}
return0;
}
假設(shè)扎瓶,./test.txt的內(nèi)容是abcdefg所踊。那么子進(jìn)程的18行將讀到字符ab;由于概荷,父秕岛、子進(jìn)程的文件描述符fd都指向同一個(gè)文件表元素,因此當(dāng)父進(jìn)程執(zhí)行23行時(shí)误证,fd對(duì)應(yīng)的文件的讀寫指針將移動(dòng)到字符d继薛,而不是字符b,從而24行讀到的是字符def愈捅,而不是字符bcd遏考。程序運(yùn)行的最終結(jié)果是打印abdef,而不是abbcd蓝谨。
相對(duì)應(yīng)的灌具,如果是兩個(gè)進(jìn)程獨(dú)立調(diào)用open去打開同一個(gè)物理文件青团,就會(huì)有2個(gè)文件表元素被創(chuàng)建,并且他們都指向同一個(gè)i節(jié)點(diǎn)表元素咖楣。兩個(gè)文件表元素都有自己獨(dú)立的current file offset字段督笆,這將導(dǎo)致2個(gè)進(jìn)程獨(dú)立的對(duì)同一個(gè)物理文件進(jìn)行讀寫,因此第1個(gè)進(jìn)程讀取到文件的第1個(gè)字符后诱贿,第2個(gè)進(jìn)程再去讀取該文件時(shí)娃肿,仍然是讀到的是文件的第1個(gè)字符,而不是第1個(gè)字符的后續(xù)字符珠十。
對(duì)應(yīng)用程序員而言咸作,最重要結(jié)論是: 如果子進(jìn)程不打算使用父進(jìn)程打開的文件,那么應(yīng)該在fork返回后立即調(diào)用close關(guān)閉該文件宵睦。