




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
Lighttpd源碼分析(網(wǎng)絡(luò)編程技術(shù)與工具)6-9章目錄\h第6章文件狀態(tài)緩存器\h6.1概述\h6.2ETag知識(shí)36、37、38\h6.2.1ETag的定義\h6.2.2ETag的功能\h6.2.3ETag的優(yōu)勢\h6.2.4Lighttpd中ETag的實(shí)現(xiàn)\h6.2.5Lighttpd中ETag的使用\h6.3文件狀態(tài)緩存器\h6.3.1緩存器設(shè)計(jì)思路\h6.3.2緩存器結(jié)構(gòu)定義\h6.3.3緩存器實(shí)現(xiàn)\h6.4本章總結(jié)\h第7章配置信息加載\h7.1概述\h7.2配置信息范例與程序加載結(jié)果\h7.2.1Lighttpd配置信息的范例\h7.2.2Lighttpd配置信息范例的加載結(jié)果\h7.3加載配置信息的源碼分析\h7.3.1Lighttpd配置信息存儲(chǔ)結(jié)構(gòu)\h7.3.2Lighttpd配置信息加載的函數(shù)調(diào)用流程\h7.4客戶端連接配置信息\h7.4.1條件配置信息緩存存儲(chǔ)結(jié)構(gòu)\h7.4.2客戶端連接配置信息動(dòng)態(tài)獲取\h7.5本章總結(jié)\h第8章I/O多路復(fù)用技術(shù)模型\h8.1概述\h8.2I/O模型基礎(chǔ)知識(shí)\h8.2.1I/O模型分類介紹\h8.2.2常見I/O多路復(fù)用實(shí)現(xiàn)技術(shù)\h8.3Lighttpd中多路復(fù)用技術(shù)模型應(yīng)用\h8.3.1整合多種復(fù)用技術(shù)模型的數(shù)據(jù)結(jié)構(gòu)封裝\h8.3.2I/O多路復(fù)用技術(shù)模型的使用\h8.3.3六種I/O多路復(fù)用技術(shù)模型的實(shí)現(xiàn)\h8.4本章總結(jié)\h第9章插件鏈\h9.1概述\h9.2插件內(nèi)部結(jié)構(gòu)\h9.2.1數(shù)據(jù)結(jié)構(gòu)\h9.2.2函數(shù)接口\h9.3插件組織結(jié)構(gòu)\h9.3.1串鏈結(jié)構(gòu)\h9.3.2插件組織結(jié)構(gòu)源碼分析\h9.4本章總結(jié)第6章文件狀態(tài)緩存器6.1概述在WebServer中,最重要的是保存在服務(wù)器里供客戶端請(qǐng)求的文件資源,包括靜態(tài)頁面(如HTML文件、文本文件、Word文件等)、動(dòng)態(tài)文件(如PHP、JSP、ASP等)還有圖片文件、聲音視頻多媒體文件等。存在于服務(wù)器上的這些文件又是動(dòng)態(tài)變化的,比如某一時(shí)刻某文件被刪除或被修改,WebServer必須時(shí)刻知道這些變化的發(fā)生,才能對(duì)客戶端的請(qǐng)求做出正確的響應(yīng)。舉個(gè)例子,客戶端請(qǐng)求訪問一個(gè)服務(wù)器上并不存在的文件資料,WebServer必須返回一個(gè)錯(cuò)誤。WebServer如何知道這個(gè)文件不存在呢?WebServer可以去文件系統(tǒng)對(duì)應(yīng)路徑查找,但是對(duì)每次客戶端的請(qǐng)求,WebServer都去磁盤查找,其效率當(dāng)然不會(huì)理想。因此,文件狀態(tài)緩存器的作用就是將最近客戶端請(qǐng)求過的文件資源的信息緩存在內(nèi)存中,對(duì)于客戶端的文件資源請(qǐng)求先查詢緩存器,因?yàn)橛辛诉@個(gè)設(shè)計(jì),WebServer性能也就能有一定的提升。另外,文件狀態(tài)緩存器里保存的文件資源信息必須是正確的,也就是要和文件系統(tǒng)同步(某文件資源之前還存在,但是此刻卻被Web站點(diǎn)管理員刪除了,這個(gè)變化必須被實(shí)時(shí)地反映到緩存器中),即文件系統(tǒng)里文件資源的變化必須要及時(shí)地更新到文件狀態(tài)緩存器中。Lighttpd源碼里設(shè)計(jì)的文件狀態(tài)緩沖器對(duì)于這些問題都已經(jīng)考慮到了,下面就開始解析這部分以及相關(guān)內(nèi)容。本節(jié)相關(guān)部分源碼:base.hetag.hstat_cache.hetag.cstat_cache.c6.2ETag知識(shí)36、37、38在開始分析文件狀態(tài)緩存器源碼之前,先來講解兩個(gè)Lighttpd源碼文件etag.h和etag.c。雖然這兩個(gè)文件代碼量不多,但是其涉及關(guān)于HTTP協(xié)議中的一個(gè)很重要的設(shè)計(jì),這個(gè)設(shè)計(jì)應(yīng)該在所有的成熟WebServer中都有實(shí)現(xiàn),在Lighttpd中也不例外。6.2.1ETag的定義ETag的定義出現(xiàn)在RFC2616文檔的14.19ETag一節(jié)。原文如下(第一句):TheETagresponse-headerfieldprovidesthecurrentvalueoftheentitytagfortherequestedvariant.(ETag響應(yīng)頭域提供了請(qǐng)求對(duì)應(yīng)變量(variant)的當(dāng)前實(shí)體標(biāo)簽。)我們可以簡單地認(rèn)為ETag是一個(gè)與某一時(shí)刻的Web資源相關(guān)聯(lián)的記號(hào)(token)。ETag存在的意義就在于能夠通過比較某Web資源前后兩次對(duì)應(yīng)ETag值讓W(xué)eb服務(wù)器獲知該Web資源在這段間隔時(shí)間內(nèi)是否有變化發(fā)生,而具體如何做到這點(diǎn)就看ETag怎么與某一時(shí)刻的Web資源去相對(duì)應(yīng),即如何由某一時(shí)刻的Web資源來生成其對(duì)應(yīng)的ETag(RFC2616對(duì)ETag并沒有具體值的定義,對(duì)于Lighttpd里面如何生成ETag,請(qǐng)待后面的源碼分析)。6.2.2ETag的功能讓W(xué)eb服務(wù)器獲知Web資源是否有變化發(fā)生有什么作用?答案就是提高Web服務(wù)器性能,節(jié)約網(wǎng)絡(luò)帶寬。既然在客戶端前后兩次資源請(qǐng)求之間的這段時(shí)間內(nèi)被請(qǐng)求的Web資源都沒有發(fā)生變化,Web服務(wù)器當(dāng)然可以直接返回一個(gè)狀態(tài)碼告訴客戶端:“該資源自上次傳送給你之后未發(fā)生任何變化,你可以直接使用本地緩存的副本?!睆亩鳺eb服務(wù)器簡簡單單的一個(gè)狀態(tài)碼返回就完成了一次客戶端請(qǐng)求。再完整地看一下帶有ETag的Web服務(wù)器端和客戶端之間請(qǐng)求響應(yīng)流程,如下所示。第一次請(qǐng)求1)客戶端發(fā)起一個(gè)HTTPGET請(qǐng)求(假設(shè)請(qǐng)求一個(gè)文件A)。2)Web服務(wù)器處理請(qǐng)求并返回響應(yīng)頭和文件內(nèi)容,其中響應(yīng)頭里就包括有ETag(例如"6a3d8e-47c-6ba-587624053"),狀態(tài)碼200。3)客戶端收到Web服務(wù)器響應(yīng),除了展示內(nèi)容給最終用戶外還會(huì)將文件內(nèi)容、ETag等響應(yīng)信息緩存起來。第二次請(qǐng)求1)客戶端第二次發(fā)起HTTPGET請(qǐng)求(當(dāng)然仍然請(qǐng)求的是文件A),此次客戶端除了發(fā)送常規(guī)請(qǐng)求頭外,還會(huì)發(fā)送一個(gè)If-None-Match頭,其內(nèi)容就是第一次請(qǐng)求時(shí)服務(wù)器返回的ETag"6a3d8e-47c-6ba-587624053"。2)Web服務(wù)器判斷客戶端發(fā)送過來的ETag和此刻重新計(jì)算出來的ETag是否匹配,如果匹配則表示自客戶端第一次請(qǐng)求文件A之后,文件A未發(fā)生任何有影響的(之所以這么說,看了之后的內(nèi)容就懂了)變化,因此If-None-Match為False,于是Web服務(wù)器直接返回304。3)客戶端收到Web服務(wù)器304狀態(tài)碼響應(yīng),知道Web服務(wù)器上的文件A未發(fā)生變化,于是繼續(xù)使用本地緩存。圖6-1是利用Lighttpd做Web服務(wù)器時(shí)某客戶端對(duì)站點(diǎn)上同一index.html文件請(qǐng)求兩次的具體請(qǐng)求響應(yīng)消息內(nèi)容,我們可以看到在該客戶端第二次請(qǐng)求index.html文件內(nèi)容時(shí),Lighttpd服務(wù)器并沒有發(fā)送任何實(shí)體數(shù)據(jù)到客戶端,而客戶端直接使用本地緩存數(shù)據(jù)。圖6-1Lighttpd服務(wù)器與支持ETag客戶端的通信流程6.2.3ETag的優(yōu)勢在HTTP協(xié)議里,與ETag非常類似的另一個(gè)頭域?yàn)長ast-Modified頭域。Last-Modified頭域(結(jié)合If-Modified-Since頭域使用)也能完成ETag上面介紹的功能,但是ETag更靈活(即ETag提供更靈活的驗(yàn)證模式)。首先,Last-Modified對(duì)文件的新舊檢查只能到秒一級(jí)。如果文件修改非常頻繁,比如在秒以下的時(shí)間內(nèi)進(jìn)行修改,這種修改Last-Modified無法判斷。其次,如果文件的更改對(duì)用戶的查閱影響并不大(比如一些文件周期性的更改,但是它的內(nèi)容并不改變而僅僅改變修改時(shí)間),也沒必要把文件重發(fā)給客戶端,此時(shí)Last-Modified無法做到判斷,而使用ETag只需不驗(yàn)證修改時(shí)間就可以滿足需求(像Apache,可由FileETag指令配置ETag值是由文件的inode(索引節(jié)點(diǎn))、大小、最后修改時(shí)間之一或它們的組合來確定)。另外,有些服務(wù)器并不能精確地得到文件的最后修改時(shí)間。當(dāng)一個(gè)請(qǐng)求里ETag和Last-Modified都存在時(shí),只有兩者各自判斷都滿足返回304狀態(tài)碼的情況下,Web服務(wù)器才能返回304狀態(tài)碼。另外,因?yàn)镋Tag的生成計(jì)算也是需要發(fā)費(fèi)Web服務(wù)器時(shí)間的,所以在實(shí)際的Web服務(wù)器使用中需要權(quán)衡處理。6.2.4Lighttpd中ETag的實(shí)現(xiàn)從上面敘述可知ETag就是一個(gè)字符串,因此Lighttpd中并沒有關(guān)于ETag的結(jié)構(gòu)化定義,直接采用buffer數(shù)據(jù)結(jié)構(gòu)體來存儲(chǔ)ETag。Lighttpd中關(guān)于ETag的實(shí)現(xiàn)源碼也很簡單,僅包含三個(gè)函數(shù)。1.intetag_is_equal(buffer*etag,constchar*matches)該函數(shù)(如清單6-1所示)比較一個(gè)非空ETag是否與指定字符串相匹配,匹配返回1,否則返回0。清單6-1函數(shù)etag_is_equal//etag.c/*比較一個(gè)非空ETag是否與指定字符串相匹配,匹配返回1,否則返回0。*/1.intetag_is_equal(buffer*etag,constchar*matches){2.if(etag&&!buffer_is_empty(etag)&&0==strcmp(etag->ptr,matches))return1;3.return0;4.}5.2.intetag_create(buffer*etag,structstat*st,etag_flags_tflags)該函數(shù)(如清單6-2所示)用于根據(jù)指定的某文件狀態(tài)信息創(chuàng)建對(duì)應(yīng)的ETag(事實(shí)上此處創(chuàng)建的ETag還不是發(fā)送到客戶端的最終值,其通過散列之后的值才是實(shí)際當(dāng)作ETag響應(yīng)頭域域值發(fā)送到客戶端)。通過判斷參數(shù)flags值來獲取文件狀態(tài)參數(shù)st內(nèi)指定的數(shù)據(jù)信息創(chuàng)建ETag并保存到buffer類型參數(shù)etag內(nèi)隱性傳出,函數(shù)返回0。etag_create函數(shù)將被stat_cache.c源文件內(nèi)的stat_cache_get_entry函數(shù)(本章接下來會(huì)分析到)調(diào)用,創(chuàng)建的ETag保存在文件狀態(tài)緩存器內(nèi)。參數(shù)flags的取值如何,可根據(jù)函數(shù)的被調(diào)用情況向后回溯,首先是stat_cache.c源文件函數(shù)stat_cache_get_entry內(nèi)的sce->etag,&(sce->st),con->etag_flags);調(diào)用,接著可以在configfile.c源文件內(nèi)看到:con->etag_flags=(con->conf.etag_use_mtime?ETAG_USE_MTIME:0)|(con->conf.etag_use_inode?ETAG_USE_INODE:0)|(con->conf.etag_use_size?ETAG_USE_SIZE:0);而con->conf.etag_use_mtime、con->conf.etag_use_inode、con->conf.etag_use_size是用戶配置信息,對(duì)應(yīng)于用戶在Lighttpd配置文件里對(duì)etag.use-mtime、etag.use-inode、etag.use-size三個(gè)配置項(xiàng)的值設(shè)置。因此參數(shù)flags的取值如何由用戶決定,即用戶可以自由指定ETag由Web資源文件的哪些信息生成,這里開發(fā)人員提供了三個(gè)文件信息(文件最后修改時(shí)間、文件結(jié)點(diǎn)號(hào)、以字節(jié)為單位的文件總大小)的組合選擇,如果我們覺得還是無法滿足特殊的需求則可自行修改該etag_create函數(shù)(體現(xiàn)了ETag提供更靈活驗(yàn)證模式的強(qiáng)大)。清單6-2函數(shù)etag_create//etag.h6.typedefenum{ETAG_USE_INODE=1,ETAG_USE_MTIME=2,ETAG_USE_SIZE=4}etag_flags_t;7.//etag_create(buffer*etag,structstat*st,etag_flags_tflags){9.if(0==flags)return0;10.11.buffer_reset(etag);12.13.if(flags&ETAG_USE_INODE){14.buffer_append_off_t(etag,st->st_ino);/*st_ino文件的i-node。*/15.buffer_append_string_len(etag,CONST_STR_LEN("-"));16.}17.18.if(flags&ETAG_USE_SIZE){19.buffer_append_off_t(etag,st->st_size);/*st_size文件大小,以字節(jié)計(jì)算。*/20.buffer_append_string_len(etag,CONST_STR_LEN("-"));21.}22.23.if(flags&ETAG_USE_MTIME){/*st_mtime文件最后一次被修改的時(shí)間,一般只有在用mknod、utime和write時(shí)才會(huì)改變。*/24.buffer_append_long(etag,st->st_mtime);25.}26.27.return0;28.}29.3.intetag_mutate(buffer*mut,buffer*etag)etag_mutate函數(shù)(如清單6-3所示)作用就是獲取參數(shù)etag的hash值,并保存到參數(shù)mut中隱性傳出。函數(shù)局部變量h的uint32_t類型定義出現(xiàn)在庫文件stdint.h(/usr/include/stdint.h)中。#ifndef__uint32_t_definedtypedefunsignedintuint32_t;接下來是一個(gè)hash計(jì)算方法39,將字符串中字符逐個(gè)異或(上一個(gè)結(jié)果左移5位異或上一個(gè)結(jié)果右移27位再異或下一個(gè)字符值得到下一個(gè)結(jié)果)得到hash值。在實(shí)際應(yīng)用中,該hash方法(或類似hash計(jì)算)得到的hash值是很可靠的,即基本上不會(huì)出現(xiàn)兩個(gè)不同字符串得到同一個(gè)hash值的情況。清單6-3函數(shù)etag_mutate//etag_mutate(buffer*mut,buffer*etag){31.size_ti;32.uint32_th;33.34.for(h=0,i=0;i<etag->used;++i)h=(h<<5)^(h>>27)^(etag->ptr[i]);35.36.buffer_reset(mut);37.buffer_copy_string_len(mut,CONST_STR_LEN("\""));38.buffer_append_long(mut,h);39.buffer_append_string_len(mut,CONST_STR_LEN("\""));40.41.return0;42.}Lighttpd中ETag的使用Lighttpd中對(duì)ETag的使用主要是體現(xiàn)在對(duì)比客戶端請(qǐng)求消息頭域里傳遞的ETag值和請(qǐng)求文件當(dāng)前計(jì)算出來的ETag值,如果兩者相等(可能還接合Last-Modified頭域)則直接返回304狀態(tài)碼以減少不必要的數(shù)據(jù)內(nèi)容發(fā)送。下面,我們就來分析這段實(shí)現(xiàn)代碼,如清單6-4所示。清單6-4函數(shù)http_response_handle_http_response_handle_cachable(server*srv,connection*con,buffer*mtime){45./*46.*14.26If-None-Match47.*[...]48.*Ifnoneoftheentitytagsmatch,thentheserverMAYperformthe49.*requestedmethodasiftheIf-None-Matchheaderfielddidnotexist,50.*butMUSTalsoignoreanyIf-Modified-Sinceheaderfield(s)inthe51.*request.Thatis,ifnoentitytagsmatch,thentheserverMUSTNOT52.*returna304(NotModified)response.53.*//*這里的英文注釋來之RFC2616第14.26節(jié),其表明如果If-None-Match不匹配,那么服務(wù)器就不能返回狀態(tài)碼304響應(yīng),即使根據(jù)If-Modified-Since頭域來看好像應(yīng)該是要返回304,原因就在于前面曾提到的ETag能夠比Last-Modified頭域提供更多的驗(yàn)證信息,如果ETag不匹配就表示該請(qǐng)求文件已經(jīng)被更新過,只不過Last-Modified頭域僅根據(jù)時(shí)間來檢測判斷不出來而已,因此此時(shí)服務(wù)器不能返回狀態(tài)碼304響應(yīng)。*/54./*last-modifiedhandling*//*con->request.http_if_none_match為字符指針(constchar*)類型變量,初始值為NULL,如果某客戶端請(qǐng)求包含有If-None-Match頭域,則在解析客戶端請(qǐng)求信息后,con->request.http_if_none_match指向If-None-Match頭域的字符串域值。此處判斷為真則表示客戶端請(qǐng)求包含有If-None-Match頭域。*/55.if(con->request.http_if_none_match){/*ETag匹配?con->physical.etag存放當(dāng)前服務(wù)器上該請(qǐng)求文件對(duì)應(yīng)的ETag值,con->request.http_if_none_match存放客戶端請(qǐng)求頭域里的ETag值,此處進(jìn)行匹配比較。*/56.if(etag_is_equal(con->physical.etag,con->request.http_if_none_match)){/*根據(jù)RFC2616第14.26節(jié),If-None-Match頭域僅適合GET或HEAD請(qǐng)求方法,對(duì)于所有其他方法,服務(wù)器必須以412(PreconditionFailed,先決條件失?。顟B(tài)碼響應(yīng)。*/57.if(con->request.http_method==HTTP_METHOD_GET||58.con->request.http_method==HTTP_METHOD_HEAD){59./*checkifetag+last-modified*//*如果ETag+Last-Modified都存在,則需要兩者都完全匹配時(shí),服務(wù)器才能返回狀態(tài)碼304。*/60.if(con->request.http_if_modified_since){61.size_tused_len;62.char*semicolon;/*根據(jù)RFC2616第2.1節(jié),分號(hào)后是注釋信息,需要截?cái)唷?/63.if(NULL==(semicolon=strchr(con->request.http_if_modified_since,';'))){64.used_len=strlen(con->request.http_if_modified_since);65.}else{66.used_len=semicolon-con->request.http_if_modified_since;67.}68.if(0==strncmp(con->request.http_if_modified_since,mtime->ptr,used_len)){/*比較,Last-Modified也匹配,返回304狀態(tài)碼。*/69.con->http_status=304;70.returnHANDLER_FINISHED;71.}else{/*sizeof運(yùn)算符得到的字符串長度包含末尾的字符'\0',因此此處結(jié)果為30。*/72.charbuf[sizeof("Sat,23Jul200521:20:01GMT")];73.time_tt_header,t_file;74.structtmtm;75./*checkifwecansafelycopythestring*/76.if(used_len>=sizeof(buf)){/*請(qǐng)求頭域包含的日期字符串長度太長。*/77.log_error_write(srv,__FILE__,__LINE__,"ssdd","DEBUG:Last-Modifiedcheckfailedasthereceivedtimestampwastoolong:",con->request.http_if_modified_since,used_len,sizeof(buf)-1);78.con->http_status=412;79.con->mode=DIRECT;80.returnHANDLER_FINISHED;81.}82.strncpy(buf,con->request.http_if_modified_since,used_len);83.buf[used_len]='\0';/*函數(shù)strptime()原型為char*strptime(constchar*s,constchar*format,structtm*tm);,它完成的功能和前面介紹的strftime()函數(shù)相反,其將由s所指向的字符串格式(該格式由控制串format指定)時(shí)間轉(zhuǎn)換并存儲(chǔ)到由參數(shù)tm所指向的structtm結(jié)構(gòu)體格式時(shí)間。此處轉(zhuǎn)換請(qǐng)求頭域內(nèi)包含的字符串日期和時(shí)間。*/84.if(NULL==strptime(buf,"%a,%d%b%Y%H:%M:%SGMT",&tm)){85.con->http_status=412;86.con->mode=DIRECT;87.returnHANDLER_FINISHED;88.}/*利用函數(shù)mktime()返回從UTC時(shí)間1970年1月1日0時(shí)0分0秒到請(qǐng)求頭域指定時(shí)間所經(jīng)過的絕對(duì)時(shí)間秒數(shù),這是個(gè)time_t類型(一般等同于longint類型),因此便于下面的時(shí)間進(jìn)行前后比較。*/89.t_header=mktime(&tm);/*計(jì)算服務(wù)器上請(qǐng)求文件的修改最后修改時(shí)間。*/90.strptime(mtime->ptr,"%a,%d%b%Y%H:%M:%SGMT",&tm);91.t_file=mktime(&tm);/*比較,如果服務(wù)器上請(qǐng)求文件的最后修改時(shí)間戳比請(qǐng)求頭域指定的時(shí)間戳新,此時(shí)不能返回304狀態(tài)碼,因此函數(shù)返回GOON退出。*/92.if(t_file>t_header)returnHANDLER_GO_ON;/*比較,如果服務(wù)器上請(qǐng)求文件的最后修改時(shí)間戳舊于請(qǐng)求頭域指定的時(shí)間戳,此時(shí)返回304狀態(tài)碼。*/93.con->http_status=304;94.returnHANDLER_FINISHED;95.}96.}else{/*只有ETag存在,則只要ETag匹配服務(wù)器就直接返回狀態(tài)碼304。*/97.con->http_status=304;98.returnHANDLER_FINISHED;99.}100.}else{/*根據(jù)RFC2616第14.26節(jié),If-None-Match頭域僅適合GET或HEAD請(qǐng)求方法,對(duì)于所有其他方法,服務(wù)器必須以412(PreconditionFailed,先決條件失?。顟B(tài)碼響應(yīng)。*/101.con->http_status=412;102.con->mode=DIRECT;103.returnHANDLER_FINISHED;104.}105.}/*只有Last-Modified存在的情況,和上面部分代碼一致(除了處理錯(cuò)誤細(xì)節(jié)方面),在此略過重復(fù)注釋。*/106.}elseif(con->request.http_if_modified_since){107.size_tused_len;108.char*semicolon;109.if(NULL==(semicolon=strchr(con->request.http_if_modified_since,';'))){110.used_len=strlen(con->request.http_if_modified_since);111.}else{112.used_len=semicolon-con->request.http_if_modified_since;113.}114.if(0==strncmp(con->request.http_if_modified_since,mtime->ptr,used_len)){115.con->http_status=304;116.returnHANDLER_FINISHED;117.}else{118.charbuf[sizeof("Sat,23Jul200521:20:01GMT")];119.time_tt_header,t_file;120.structtmtm;121./*converttotimestamp*/122.if(used_len>=sizeof(buf))returnHANDLER_GO_ON;123.strncpy(buf,con->request.http_if_modified_since,used_len);124.buf[used_len]='\0';125.if(NULL==strptime(buf,"%a,%d%b%Y%H:%M:%SGMT",&tm)){126./**127.*parsingfailed,let'sgetoutofhere128.*/129.log_error_write(srv,__FILE__,__LINE__,"ss",130."strptime()failedon",buf);131.returnHANDLER_GO_ON;132.}133.t_header=mktime(&tm);134.strptime(mtime->ptr,"%a,%d%b%Y%H:%M:%SGMT",&tm);135.t_file=mktime(&tm);136.if(t_file>t_header)returnHANDLER_GO_ON;137.con->http_status=304;138.returnHANDLER_FINISHED;139.}140.}141.returnHANDLER_GO_ON;142.}函數(shù)http_response_handle_cachable的執(zhí)行流程圖如圖6-2所示,該流程圖把同時(shí)存在ETag+Last-Modified和只有Last-Modified存在的兩種情況進(jìn)行了重疊,因?yàn)樗鼈兊拇a僅在處理錯(cuò)誤發(fā)生時(shí)有所不同,差別較小,重疊在一起便于理解整個(gè)處理過程。圖6-2函數(shù)http_response_handle_cachable的執(zhí)行流程6.3文件狀態(tài)緩存器6.3.1緩存器設(shè)計(jì)思路文件狀態(tài)緩存器用于緩存文件狀態(tài):將stat()調(diào)用得到的結(jié)果(以及附屬結(jié)果比如由當(dāng)前文件狀態(tài)生成的ETag、獲知的文件類型等)保存在Lighttpd自己構(gòu)造的緩存器中,在下一次需要獲得文件狀態(tài)時(shí)就直接返回緩存的值,這樣提高程序執(zhí)行效率。當(dāng)然如果外部文件在其狀態(tài)被緩存后發(fā)生了變化(被修改甚至被刪除),這種變化應(yīng)該同步反應(yīng)到緩存器中去,否則程序從緩存器中獲得的值將是錯(cuò)誤的。Lighttpd實(shí)現(xiàn)源碼通過FAM(Filealterationmonitor40)(或稱為sgi_fam)的子系統(tǒng)來獲取外部文件系統(tǒng)的變化。該子系統(tǒng)由SiliconGraphics開發(fā),它可以替應(yīng)用程序監(jiān)視某一些(需要被監(jiān)視的)文件,當(dāng)這些(被監(jiān)視的)文件發(fā)生變化(如被修改或被刪除等)時(shí)通知(這種通知有兩種方式,一種是FAM把被監(jiān)控文件的變化反應(yīng)在一個(gè)文件描述符內(nèi),應(yīng)用程序通過select或epoll等函數(shù)調(diào)用來獲知,另一種是應(yīng)用程序直接通過FAM庫提供的FAMPending函數(shù)調(diào)用來獲知)應(yīng)用程序,從而使得應(yīng)用程序可以做出相應(yīng)的處理(比如Lighttpd應(yīng)用程序需要的更新文件狀態(tài)緩存器)。很顯然,這比傳統(tǒng)的由應(yīng)用程序自身每隔一段時(shí)間去查詢各個(gè)被監(jiān)視的文件是否發(fā)生變化要高效得多,而且同步更及時(shí)。Lighttpd源碼里關(guān)于緩存器的實(shí)現(xiàn)可以敘述如下:1)對(duì)于某文件A(假設(shè)文件完整路徑為"/web/111/a.htm"),Lighttpd應(yīng)用程序會(huì)在首次獲取它的文件狀態(tài)相關(guān)信息時(shí)將其緩存到緩存器內(nèi)(數(shù)據(jù)結(jié)構(gòu)體stat_cache_entry),同時(shí)把文件A所在的目錄("/web/111/")添加到FAM監(jiān)控(數(shù)據(jù)結(jié)構(gòu)體fam_dir_entry)內(nèi),并且此時(shí)在保存數(shù)據(jù)的兩個(gè)數(shù)據(jù)結(jié)構(gòu)體stat_cache_entry和fam_dir_entry內(nèi)保存的版本號(hào)(stat_cache_entry.dir_version==fam_dir_entry.version)是一致的。2)當(dāng)某時(shí)刻文件A發(fā)生了變化,Lighttpd應(yīng)用程序從FAM子系統(tǒng)那獲得這個(gè)變化信息,于是改變文件A所在目錄對(duì)應(yīng)的版本號(hào)(fam_dir_entry.version)。3)Lighttpd應(yīng)用程序需要再次獲得文件A的文件狀態(tài)相關(guān)信息時(shí)會(huì)先查詢緩存器,并且比較文件A和所在目錄的版本號(hào),如果一致則表示文件A在這段時(shí)間內(nèi)未發(fā)生變化,于是直接從緩存器內(nèi)獲取文件狀態(tài)相關(guān)信息。如果不一致則表示緩存器內(nèi)的緩存信息不是最新的,需重新獲取。上面去掉了很多細(xì)節(jié)的描述,具體的情況下面的源碼分析部分會(huì)詳細(xì)說明。6.3.2緩存器結(jié)構(gòu)定義與文件狀態(tài)緩存器有關(guān)的數(shù)據(jù)結(jié)構(gòu)定義在base.h頭文件內(nèi),stat_cache.c也有部分定義,結(jié)構(gòu)如清單6-5所示。清單6-5文件狀態(tài)緩存器數(shù)據(jù)結(jié)構(gòu)定義//base.h143.typedefstruct{144.buffer*name;/*保存文件名。*/145.buffer*etag;/*保存文件對(duì)應(yīng)的ETag,因?yàn)橛?jì)算ETag也是需要時(shí)間的。*//*保存文件狀態(tài)信息,程序通過調(diào)用lstat()函數(shù)或stat()函數(shù)來獲得文件狀態(tài)信息。*/146.structstatst;/*time_t存的是1970年1月1日0時(shí)0分0秒算起至今的UTC時(shí)間所經(jīng)過的秒數(shù),time_t是longint類型。#ifndef_TIME_T_DEFINEDtypedeflongtime_t;#define_TIME_T_DEFINED#endif*/147.time_tstat_ts;148.149.#ifdefHAVE_LSTAT150.charis_symlink;/*符號(hào)鏈接標(biāo)記。*/151.#endif152.153.#ifdefHAVE_FAM_H/*FAM相關(guān)字段。*/154.intdir_version;/*文件目錄版本號(hào)。*/155.intdir_ndx;/*索引。*/156.#endif157.158.buffer*content_type;/*文件類型。*/159.}stat_cache_entry;160.161.typedefstruct{/*緩存文件狀態(tài)信息的節(jié)點(diǎn)都插入到這個(gè)files伸展樹內(nèi)。*/162.splay_tree*files;/*thenodesofthetreearestat_cache_entry's*/163.buffer*dir_name;/*forbuildingthedirnamefromthefilename*/164.#ifdefHAVE_FAM_H/*記錄被監(jiān)控文件夾的節(jié)點(diǎn)都插入到這個(gè)dirs伸展樹內(nèi)。*/165.splay_tree*dirs;/*thenodesofthetreearefam_dir_entry*/166.FAMConnection*fam;167.intfam_fcce_ndx;168.#endif169.buffer*hash_key;/*temp-storeforthehash-key*/170.}stat_cache;171.//stat_cache.c172./*173.*stat-cache174.*wecachethestat()callsinourownstorage175.*thedirectoriesarecachedinFAM176.*ifwegetachange-eventfromFAM,weincrementtheversionintheFAM->dirmapping177.*ifthestat()-cacheisqueriedwecheckiftheversionidforthedirectoryisthe178.*sameandreturnimmediatly.179.*Whatweneed:180.*-foreachstat-cacheentryweneedafastindirectlookuponthedirectoryname181.*-foreachFAMRequestwehavetofindtheversioninthedirectorycache(indexasuserdata)182.*stat<<->directory<->FAMRequest183.*iffileisdeleted,directoryisdirty,fileisrechecked...184.*ifdirectoryisdeleted,directorymappingisremoved185.**/186.187.#ifdefHAVE_FAM_H188.typedefstruct{189.FAMRequest*req;190.FAMConnection*fc;191.buffer*name;192.intversion;/*文件目錄版本號(hào)。*/193.}fam_dir_entry;194.#endif195.196./*thedirectorynameistoolongtoalwayscompareonit197.*-weneedahash198.*-thehash-keyisusedassortingcriteriaforatree199.*-asplay-treeisusedaswecanusethecachingeffectofit200.*/201./*wewanttocleanupthestat-cacheeveryfewseconds,let'ssay10202.*203.*-removeentrieswhichareoutdatedsince30s204.*-removeentrieswhicharefreshbuthavn'tbeenusedsince60s205.*-ifwedon'thaveastat-cacheentryforadirectory,releaseitfromthemonitor206.*/207.#ifdefDEBUG_STAT_CACHE208.typedefstruct{209.int*ptr;210.size_tused;211.size_tsize;212.}fake_keys;213.staticfake_keysctrl;214.#endif這幾個(gè)結(jié)構(gòu)體的字段都不多,而且源代碼里有Lighttpd開發(fā)者的詳細(xì)英文注釋(Lighttpd開發(fā)者關(guān)于緩存器的注解也列出來了,方便讀者理解。),如果將它們放在函數(shù)代碼里再看就更容易理解了。6.3.3緩存器實(shí)現(xiàn)前面講了文件狀態(tài)緩存器的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì),其相關(guān)功能設(shè)計(jì)在stat_cache.c源文件內(nèi)得以實(shí)現(xiàn),下面就來分析這個(gè)文件,將其具體的實(shí)現(xiàn)細(xì)節(jié)介紹給讀者。1.stat_cache*stat_cache_init(void)、voidstat_cache_free(stat_cache*sc)stat_cache結(jié)構(gòu)體的初始化和釋放操作函數(shù),如清單6-6所示。清單6-6函數(shù)stat_cache_init、stat_cache_free//stat_cache.c215.stat_cache*stat_cache_init(void){216.stat_cache*fc=NULL;217.218.fc=calloc(1,sizeof(*fc));219.220.fc->dir_name=buffer_init();221.fc->hash_key=buffer_init();222.#ifdefHAVE_FAM_H223.fc->fam=calloc(1,sizeof(*fc->fam));224.#endif225.226.#ifdefDEBUG_STAT_CACHE227.ctrl.size=0;228.#endif229.230.returnfc;231.}232.233.voidstat_cache_free(stat_cache*sc){234.while(sc->files){235.intosize;236.splay_tree*node=sc->files;237.238.osize=sc->files->size;239.240.stat_cache_entry_free(node->data);/*先釋放數(shù)據(jù)data空間。*//*再釋放根節(jié)點(diǎn),并返回新根節(jié)點(diǎn)以便繼續(xù)循環(huán)釋放。*/241.sc->files=splaytree_delete(sc->files,node->key);242.243.assert(osize-1==splaytree_size(sc->files));244.}245.246.buffer_free(sc->dir_name);247.buffer_free(sc->hash_key);248.249.#ifdefHAVE_FAM_H250.while(sc->dirs){251.intosize;252.splay_tree*node=sc->dirs;253.254.osize=sc->dirs->size;255.256.fam_dir_entry_free(node->data);257.sc->dirs=splaytree_delete(sc->dirs,node->key);258.259.if(osize==1){260.assert(NULL==sc->dirs);261.}else{262.assert(osize==(sc->dirs->size+1));263.}264.}265.if(sc->fam){/*函數(shù)FAMClose原型:intFAMClose(FAMConnection*fc);該函數(shù)關(guān)閉到FAM的連接,執(zhí)行成功返回0,失敗返回-1。*/266.267.FAMClose(sc->fam);268.free(sc->fam);269.}270.#endif271.free(sc);272.}2.staticstat_cache_entry*stat_cache_entry_init(void)、staticvoidstat_cache_entry_free(void*data)stat_cache_entry結(jié)構(gòu)體的初始化和釋放操作函數(shù)如清單6-7所示。清單6-7函數(shù)stat_cache_entry_init、stat_cache_entry_free//stat_cache.c273.staticstat_cache_entry*stat_cache_entry_init(void){274.stat_cache_entry*sce=NULL;275.276.sce=calloc(1,sizeof(*sce));277.278.sce->name=buffer_init();279.sce->etag=buffer_init();280.sce->content_type=buffer_init();281.282.returnsce;283.}284.285.staticvoidstat_cache_entry_free(void*data){286.stat_cache_entry*sce=data;287.if(!sce)return;288.289.buffer_free(sce->etag);290.buffer_free(sce->name);291.buffer_free(sce->content_type);292.293.free(sce);294.}295.3.staticfam_dir_entry*fam_dir_entry_init(void)、staticvoidfam_dir_entry_free(void*data)fam_dir_entry結(jié)構(gòu)體的初始化和釋放操作函數(shù)如清單6-8所示。清單6-8函數(shù)fam_dir_entry_init、fam_dir_entry_free//stat_cache.c296.#ifdefHAVE_FAM_H297.staticfam_dir_entry*fam_dir_entry_init(void){298.fam_dir_entry*fam_dir=NULL;299.300.fam_dir=calloc(1,sizeof(*fam_dir));301.302.fam_dir->name=buffer_init();303.304.returnfam_dir;305.}306.307.staticvoidfam_dir_entry_free(void*data){308.fam_dir_entry*fam_dir=data;309.310.if(!fam_dir)return;/*函數(shù)FAMCancelMonitor原型:intFAMCancelMonitor(FAMConnection*fc,FAMRequest*fr);該函數(shù)取消對(duì)fr指定的文件或文件夾的監(jiān)視,執(zhí)行成功返回0,失敗返回-1。*/311.FAMCancelMonitor(fam_dir->fc,fam_dir->req);312.313.buffer_free(fam_dir->name);314.free(fam_dir->req);315.316.free(fam_dir);317.}318.#endif319.4.staticintstat_cache_attr_get(buffer*buf,char*name)該函數(shù)(如清單6-9所示)獲取指定路徑文件的文件類型。函數(shù)attr_get屬于XFS41(Thexattrextensionallowsforthemanipulationofextendedattributesonafilesystem),用于獲取文件的擴(kuò)展屬性(關(guān)于這方面的更多知識(shí),讀者可以查閱注解里提供的網(wǎng)站)。清單6-9函數(shù)stat_cache_attr_get//stat_cache.c320.#ifdefHAVE_XATTR321.staticintstat_cache_attr_get(buffer*buf,char*name){322.intattrlen;323.intret;324.325.attrlen=1024;326.buffer_prepare_copy(buf,attrlen);327.attrlen--;328.if(0==(ret=attr_get(name,"Content-Type",buf->ptr,&attrlen,0))){329.buf->used=attrlen+1;330.buf->ptr[attrlen]='\0';331.}332.returnret;333.}334.#endif335.5.staticuint32_thashme(buffer*str)該函數(shù)(如清單6-10所示)獲取對(duì)應(yīng)字符串(保存在結(jié)構(gòu)體buffer內(nèi))的hash值。該函數(shù)采用的是非常流行的DJB42hashfunction,俗稱"Times33"算法。Times33算法其實(shí)很簡單,就是不斷地乘33。這里是將hash左移5位再加上hash(即hash*32+hash,也就是乘33)。因?yàn)門ime33在效率和隨機(jī)性方面都俱佳,所以幾乎所有流行的HashMap都采用了它。清單6-10函數(shù)hashme//stat_cache.c336./*thefamousDJBhashfunctionforstrings*/337.staticuint32_thashme(buffer*str){338.uint32_thash=5381;339.constchar*s;340.for(s=str->ptr;*s;s++){341.hash=((hash<<5)+hash)+*s;342.}343.344.hash&=~(1<<31);/*stripthehighestbit*/345.346.returnhash;347.}348.6.handler_tstat_cache_handle_fdevent(void*_srv,void*_fce,intrevent)、staticintbuffer_copy_dirname(buffer*dst,buffer*file)前面說了,F(xiàn)AM可以把被監(jiān)控文件的變化反映在一個(gè)文件描述符內(nèi)使得應(yīng)用程序通過select或epoll等函數(shù)調(diào)用來獲知這種變化。Lighttpd里正是利用這種方法,將對(duì)外部文件變化事件的監(jiān)控和客戶端的訪問請(qǐng)求事件的監(jiān)控統(tǒng)一管理。函數(shù)stat_cache_handle_fdevent主要將在外部被監(jiān)控文件發(fā)生變化時(shí)(還有Lighttpd斷開和FAM的連接時(shí))被調(diào)用,用來對(duì)外部文件變化事件做相應(yīng)處理。該函數(shù)最新出現(xiàn)在server.c源文件內(nèi),用于事件監(jiān)控處理函數(shù)注冊(cè),源碼如清單6-11所示。清單6-11事件監(jiān)控處理函數(shù)注冊(cè)349.#ifdefHAVE_FAM_H350./*setupFAM*/351.if(srv->srvconf.stat_cache_engine==STAT_CACHE_ENGINE_FAM){352.if(0!=FAMOpen2(srv->stat_cache->fam,"lighttpd")){353.log_error_write(srv,__FILE__,__LINE__,"s",354."couldnotopenafamconnection,dieing.");355.return-1;356.}357.#ifdefHAVE_FAMNOEXISTS/*FAMNoExists函數(shù)是Gamin里新擴(kuò)展的函數(shù)。Gamin是Gnome里的一個(gè)項(xiàng)目,實(shí)現(xiàn)的也是FAM同樣的功能。Gamin項(xiàng)目頁里提及說Gamin和FAM2.6.8在二進(jìn)制和源代碼級(jí)兼容,只有一點(diǎn)點(diǎn)不同與一個(gè)有意義的擴(kuò)展。此處FAMNoExists函數(shù)就是這個(gè)有意義的擴(kuò)展,但是其是否被調(diào)用都不影響FAM主要功能。具體內(nèi)容可以參閱注解提供的網(wǎng)站43。*/358.FAMNoExists(srv->stat_cache->fam);359.#endif/*FAMCONNECTION_GETFD是一個(gè)宏,用于從FAMConnection結(jié)構(gòu)體中獲取FAM連接的文件描述符。*/360.srv->stat_cache->fam_fcce_ndx=-1;361.fdevent_register(srv->ev,FAMCONNECTION_GETFD(srv->stat_cache->fam),stat_cache_handle_fdevent,NULL);362.fdevent_event_add(srv->ev,&(srv->stat_cache->fam_fcce_ndx),FAMCONNECTION_GETFD(srv->stat_cache->fam),FDEVENT_IN);363.}364.#endif在FAM提供了兩個(gè)函數(shù)(函數(shù)FAMOpen44和函數(shù)FAMOpen2)用于打開一個(gè)到FAM服務(wù)器的連接,這兩個(gè)函數(shù)差別很小,僅函數(shù)FAMOpen2比FAMOpen多了第二個(gè)參數(shù)(保存應(yīng)用程序名稱),因此可以告訴FAM服務(wù)器更多的一點(diǎn)FAM客戶端信息。函數(shù)打開連接成功則返回0,否則返回-1。函數(shù)原型如清單6-12所示,其中的FAMConnection參數(shù)將在FAMOpen(FAMOpen2)成功執(zhí)行過程中被初始化,在之后的所有FAM函數(shù)調(diào)用過程中都將使用到這個(gè)被初始化的結(jié)構(gòu)體變量。清單6-12函數(shù)FAMOpen、FAMOpen2原型365.intFAMOpen(FAMConnection*fc);366.intFAMOpen2(FAMConnection*fc,constchar*appName);367.接下來就是利用fdevent_register和fdevent_event_add將FAM文件描述符加入到多路I/O復(fù)用監(jiān)控(監(jiān)控FDEVENT_IN事件,這個(gè)具體過程后面章節(jié)詳細(xì)講解)里。如此之后,只要FAM監(jiān)控的文件有變化發(fā)生導(dǎo)致FAM文件描述符有事件(FAMevent)發(fā)生,應(yīng)用程序通過Select或Epoll等獲知發(fā)生事件,從而調(diào)用此處注冊(cè)的stat_cache_handle_fdevent函數(shù)進(jìn)行處理?;剡^來再看stat_cache_handle_fdevent函數(shù)。函數(shù)對(duì)FAM通知過來的事件做分別處理,如果是可讀事件,表示外部文件有變化,于是更新緩存器內(nèi)容,如果是掛斷事件則注銷事件、關(guān)閉連接、釋放內(nèi)存,最后返回HANDLER_GO_ON標(biāo)志繼續(xù)程序的執(zhí)行。讀者在第一次閱讀理解這個(gè)函數(shù)有困難時(shí),請(qǐng)先閱讀后面即將講到的另一個(gè)重要函數(shù)stat_cache_get_entry,如清單6-13所示。清單6-13函數(shù)stat_cache_get_entry368.//stat_cache.c369.#ifdefHAVE_FAM_H/*handler_t是個(gè)枚舉類型結(jié)構(gòu)體,定義在settings.h頭文件內(nèi),在后面的Lighttpd插件分析章節(jié)再詳細(xì)講解此數(shù)據(jù)結(jié)構(gòu)。typedefenum{HANDLER_UNSET,HANDLER_GO_ON,HANDLER_FINISHED,HANDLER_COMEBACK,HANDLER_WAIT_FOR_EVENT,HANDLER_ERROR,HANDLER_WAIT_FOR_FD}handler_t;*/370.handler_tstat_cache_handle_fdevent(void*_srv,void*_fce,intrevent){371.size_ti;372.server*srv=_srv;373.stat_cache*sc=srv->stat_cache;374.size_tevents;375.376.UNUSED(_fce);377./**//*可讀事件發(fā)生。*/378.if((revent&FDEVENT_IN)&&379.sc->fam){/*獲取可讀事件數(shù)目。*/380.events=FAMPending(sc->fam);/*函數(shù)FAMPending原型:intFAMPending(FAMConnection*fc);該函數(shù)返回正整數(shù)表示有FAMevent在隊(duì)列中,返回0表示沒有事件發(fā)生,-1表示發(fā)生錯(cuò)誤。該函數(shù)調(diào)用后馬上返回,即不阻塞等待事件發(fā)生。函數(shù)FAMNextEvent原型:intFAMNextEvent(FAMConnection*fc,FAMEvent*fe);該函數(shù)執(zhí)行成功返回0,返回-1表示發(fā)生錯(cuò)誤。*//*對(duì)各個(gè)可讀事件分別進(jìn)行處理。*/381.for(i=0;i<events;i++){382.FAMEventfe;383.fam_dir_entry*fam_dir;384.splay_tree*node;385.intndx,j;/*FAMNextEvent函數(shù)將逐個(gè)把事件信息存儲(chǔ)到結(jié)構(gòu)體FAMEvent參數(shù)fe內(nèi)隱性傳出。typedefstruct{FAMConnection*fc;FAMRequestfr;char*hostname;charfilename[PATH_MAX];void*userdata;FAMCodescode;}FAMEvent;fc是通過函數(shù)FAMOpen或FAMOpen2初始化的。fr是通過函數(shù)FAMMonitorFile()或FAMMonitorDirectory()調(diào)用初始化的(這兩個(gè)函數(shù)后面會(huì)講到)。hostname現(xiàn)在已經(jīng)不使用了,一般不要用它。filename(發(fā)生改變的)被監(jiān)控文件或文件夾的完整路徑或是被監(jiān)控目錄下的文件名。userdata可以指向任何數(shù)據(jù),因此提供了從事件監(jiān)控設(shè)置函數(shù)到事件發(fā)生處理函數(shù)之間數(shù)據(jù)傳遞方法。code是枚舉結(jié)構(gòu)FAMCodes中的一個(gè)取值,表示當(dāng)前發(fā)生的是哪個(gè)事件(比如,F(xiàn)AMChanged、FAMDeleted)、FAMCreated、FAMMoved等。*/386.FAMNextEvent(sc->fam,&fe);387.388./*handleevent*/389.390.switch(fe.code){391.caseFAMChanged:392.caseFAMDeleted:393.caseFAMMoved:394./*ifthefilenameisadirectoryremovetheentry*//*如果文件名所指的是目錄(并且事件是FAMDeleted或FAMMoved)則移除相應(yīng)節(jié)點(diǎn)。獲得用戶數(shù)據(jù),該數(shù)據(jù)從函數(shù)stat_cache_get_entry內(nèi)傳遞過來。*/395.fam_dir=fe.userdata;396.fam_dir->version++;/*更新版本號(hào),表示有文件發(fā)生變化。*/397.398./*file/dirisstillhere*//*只是目錄文件狀態(tài)改變事件則跳出,不用進(jìn)行接下來的節(jié)點(diǎn)刪除操作。*/399.if(fe.code==FAMChanged)break;400.401./*wehave2versions,followandno-follow-symlink*//*有兩種情況(followandno-follow-symlink),但是并不知道是哪種情況,因此對(duì)這種情況的刪除操作都要嘗試進(jìn)行,結(jié)果最多就是某一次嘗試失?。凑也坏侥莻€(gè)節(jié)點(diǎn),if判斷為假,表示不是這種情況)。*/402.for(j=0;j<2;j++){403.buffer_copy_string(sc->hash_key,fe.filename);/*獲取發(fā)生改變的文件名。*/404.buffer_append_long(sc->hash_key,j);405.406.ndx=hashme(sc->hash_key);/*如果不是目錄移動(dòng)、刪除事件,則這里的伸展操作應(yīng)該是找不到節(jié)點(diǎn)的。*/407.sc->dirs=splaytree_splay(sc->dirs,ndx);408.node=sc->dirs;409.410.if(node&&(node->key==ndx)){411.intosize=splaytree_size(sc->dirs);412.413.fam_dir_entry_free(node->data);414.sc->dirs=splaytree_delete(sc->dirs,ndx);415.416.assert(osize-1==splaytree_size(sc->dirs));417.}418.}419.break;420.default:421.break;422.}423.}424.}425.426.if(revent&FDEVENT_HUP){427./*famclosedtheconnection*/428.srv->stat_cache->fam_fcce_ndx=-1;429.430.fdevent_event_del(srv->ev,&(sc->fam_fcce_ndx),FAMCONNECTION_GETFD(sc->fam));431.fdevent_unregister(srv->ev,FAMCONNECTION_GETFD(sc->fam));432.433.FAMClose(sc->fam);434.free(sc->fam);435.436.sc->fam=NULL;437.}438.439.returnHANDLER_GO_ON;440.}/*獲取指定文件所在的父目錄路徑。*/441.staticintbuffer_copy_dirname(buffer*dst,buffer*file){442.size_ti;443.444.if(buffer_is_empty(file))return-1;445.446.for(i=file->used-1;i+1>0;i--){447.if(file->ptr[i]=='/'){448.buffer_copy_string_len(dst,file->ptr,i);449.return0;450.}451.}452.453.return-1;454.}455.#endif7.staticintstat_cache_lstat(server*srv,buffer*dname,structstat*lst)該函數(shù)(如清單6-14所示)檢查dname是否為符號(hào)鏈接,是返回0,不是返回1,如果出錯(cuò)返回-1。另外,調(diào)用lstat函數(shù)獲得的文件狀態(tài)信息放入?yún)?shù)lst中隱性傳出。清單6-14函數(shù)stat_cache_lstat//stat_cache.c456.#ifdefHAVE_LSTAT457.staticintstat_cache_lstat(server*srv,buffer*dname,structstat*lst){458.if(lstat(dname->ptr,lst)==0){459.returnS_ISLNK(lst->st_mode)?0:1;460.}461.else{462.log_error_write(srv,__FILE__,__LINE__,"sbs","lstatfailedfor:",dname,strerror(errno));463.};464.return-1;465.}466.#endif467.8.handler_tstat_cache_get_entry(server*srv,connection*con,buffer*name,stat_cache_entry**ret_sce)函數(shù)stat_cache_get_entry(如清單6-15所示)是緩存器實(shí)現(xiàn)的重點(diǎn)。函數(shù)首先聲明了一些必要的臨時(shí)變量并進(jìn)行了初始化,接著將字符串形式(存儲(chǔ)在buffer結(jié)構(gòu)體內(nèi))的參數(shù)name(待查文件完整路徑)通過Hash轉(zhuǎn)換成整型索引值,并在當(dāng)前工作進(jìn)程的文件狀態(tài)緩存器中查找(文件狀態(tài)緩存器以伸展樹結(jié)構(gòu)存儲(chǔ),所以節(jié)點(diǎn)查找是通過對(duì)伸展樹的伸展訪問進(jìn)行)。需要注意的是,在伸展樹中查找到待查文件記錄節(jié)點(diǎn)(以下簡稱為記錄節(jié)點(diǎn))仍不能保證該記錄節(jié)點(diǎn)就是程序原本想要的那個(gè),程序是通過name參數(shù)Hash轉(zhuǎn)換得到整型索引值查找的。如果兩個(gè)不同的name字符串得到同一個(gè)整型索引值會(huì)怎么樣呢?(這種可能當(dāng)然存在,就算Times33算法隨機(jī)性再好也不能保證完全沒有沖突的發(fā)生)。所以程序仍需要對(duì)查找到的記錄節(jié)點(diǎn)內(nèi)保存的name字符串和函數(shù)原本的參數(shù)name進(jìn)行對(duì)比判斷,只有它們兩個(gè)也相等時(shí)才表示查找成功。查找成功意味著待查文件狀態(tài)就緩存在文件狀態(tài)緩存器中,如果該記錄節(jié)點(diǎn)又是最新的(即記錄節(jié)點(diǎn)時(shí)間戳和當(dāng)前工作進(jìn)程時(shí)間戳一致)則直接使用即可,無須再次調(diào)用stat()函數(shù)從而提高效率(這就是文件狀態(tài)緩存器的關(guān)鍵作用),將結(jié)果存入指針參數(shù)ret_sce中,并返回執(zhí)行成功的狀態(tài)碼HANDLER_GO_ON。上面提及的是一切都進(jìn)行得非常順利的情況,再來看看不順利的情況下程序執(zhí)行流程會(huì)怎樣,繼續(xù)往下看。程序接著判斷待查文件是否記錄有變化(通過比較記錄節(jié)點(diǎn)與待查文件所在目錄的監(jiān)控節(jié)點(diǎn)各自的版本號(hào),如果相等則表示待查文件沒有變化),如果沒
溫馨提示
- 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. 人人文庫網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- Module7 Unit1 He spent about twenty-one hours in space(教學(xué)設(shè)計(jì))-2023-2024學(xué)年外研版(三起)英語六年級(jí)下冊(cè)
- 華北理工大學(xué)冀唐學(xué)院《研究型建筑設(shè)計(jì)》2023-2024學(xué)年第二學(xué)期期末試卷
- 山西國際商務(wù)職業(yè)學(xué)院《計(jì)算機(jī)組成原理理論》2023-2024學(xué)年第二學(xué)期期末試卷
- 哈爾濱鐵道職業(yè)技術(shù)學(xué)院《班級(jí)活動(dòng)的組織》2023-2024學(xué)年第二學(xué)期期末試卷
- 遼寧民族師范高等專科學(xué)?!稒C(jī)電系統(tǒng)設(shè)計(jì)與控制》2023-2024學(xué)年第二學(xué)期期末試卷
- 廣州東華職業(yè)學(xué)院《海洋生物技術(shù)綜合實(shí)驗(yàn)》2023-2024學(xué)年第二學(xué)期期末試卷
- 南昌大學(xué)科學(xué)技術(shù)學(xué)院《新編大學(xué)生安全教育》2023-2024學(xué)年第二學(xué)期期末試卷
- 河北科技師范學(xué)院《西方財(cái)務(wù)會(huì)計(jì)雙語》2023-2024學(xué)年第二學(xué)期期末試卷
- 共青科技職業(yè)學(xué)院《學(xué)前兒童保育學(xué)》2023-2024學(xué)年第二學(xué)期期末試卷
- 陜西理工大學(xué)《數(shù)字信號(hào)處理》2023-2024學(xué)年第二學(xué)期期末試卷
- 不吃路邊攤精品課件
- 《網(wǎng)絡(luò)服務(wù)器搭建、配置與管理-Linux(RHEL8、CentOS8)(微課版)(第4版)》全冊(cè)電子教案
- 心理評(píng)估與診斷簡介
- 無痛病房管理課件
- 讓孩子變成學(xué)習(xí)的天使——由《第56號(hào)教室的奇跡》讀書分享
- 球泡檢驗(yàn)標(biāo)準(zhǔn)
- 公安筆錄模板之詢問嫌疑人(書面?zhèn)鲉局伟舶讣?
- 振動(dòng)分析基礎(chǔ)講義1
- 記賬憑證匯總表excel模板
- 鄧麗君經(jīng)典歌曲30首簡譜(共33頁)
- 園林綠化施工通用表格模板
評(píng)論
0/150
提交評(píng)論