(萊昂氏unix原始碼分析導讀-35)快取管理(上)
by cszhao1980
系統定義了NBUF個快取區域,每個514個位元組:
4720: char buffers[NBUF][514];
【注】:514個位元組稍稍大於一個物理盤塊的size,多出的2個byte的用途不明。
而“快取頭”陣列buf[NBUF]的每個entry對應一個快取區域,其b_addr被設定為對應的快取區
的首地址,如:&buffers[1]。對於快取的使用都是通過其快取頭陣列entry來完成。
【思考題】:為什麼這裡可以直接使用邏輯地址,如:&buffers[1]
Unix使用兩種(帶頭結點的)雙向迴圈連結串列(佇列)來管理這些快取,即
(1) AV佇列:即空閒佇列,對首為buf bfreelist;
(2) B佇列:即裝置的任務佇列,對首為某裝置(devtab型別)。
顯然,可以有多個b佇列。
【注】:在b隊列當中,對首是devtab型別,而成員都是buf型別。該連結串列得以形成的原因是兩種類
型都用同名的成員(b_forw和b_back)作為前後指標,而且,它們的這兩種struct中的位置是相同的。
總的原則是,空閒的快取將掛在AV佇列中,當需要操作某裝置時就從AV佇列中取下,掛到該
裝置的b佇列中,而裝置操作完成後,又重新將其放還到AV
系統的某些實現使這一過程顯得有些晦澀:
(1) 在裝置操作完成後,會將快取放回到AV佇列,但沒有將該快取從原裝置的b佇列中取
下來——這一步是在再次分配該快取時完成的。即在分配快取時,分為3步
i. 從AV佇列中取下;
ii. 從原裝置佇列中取下;
iii. 掛到新裝置佇列中。
(2) 問題來了,在系統初啟階段,所有快取都沒有分配過裝置,那麼其“原裝置佇列”在哪?
為解決這一問題,系統定義了一個特殊的裝置佇列——空閒裝置佇列,其裝置號為-1,其
首指標為我們的老朋友buf freelist
之所以會採用這樣的設計,是為了支援“延遲寫”技術,以提高效率。所謂“延遲寫”即寫入快取
的內容不會馬上被寫入磁碟——真正的寫入是在使用者程式或“定時”程式呼叫flush時完成的。
(1) 要支援“延遲寫”,快取必須仍掛在裝置佇列中,否則磁碟內容就會丟失;
(2) 如果直到真正寫入後,快取才可以釋放回AV佇列,有可能造成快取資源緊張;
所以系統採用了這樣的方式:
(1) 一旦對快取的寫入結束,該快取即釋放入AV佇列,但其flag設定了B_DELWRI標誌;
(2) 如果該快取被再次分配,會檢查器B_DELWRI標誌,如其置位,則首先執行寫入,再
將此快取分配給新裝置。