天天看點

[深入了解檔案系統之八]SVR4中的Page Cache

  在SVR4中,所有檔案的讀寫都會經過Page Cache。不同于有固定長度的實體cache,或者buffer cache, 或者DNLC. PageCache和其他cache的差別在于:它可以根據軟體需求換進或者換出。另外也和buffer cache不同:buffer cache用裝置和塊号來索引cache,而page cache用vnode和offset來索引。

page cache的構成

     一個支援seg_map操作(比如目前段的缺頁異常處理)的段;

     一個具有多種用途的空閑頁表連結清單(list)

    當用到的page of file data 從memory cache中換出來(離開cache)的時候,它被加入空閑頁表;

反正也可從空閑頁表中取走。但有一點需要特别注意的是,盡管這個頁在free list上,但它的辨別符和資料還在,這樣一旦核心想再次讀那份資料,不用再從磁盤上去取,而直接把個頁從free lis中移除即可。 

在unix系統上,實際是按照下面chunk的方式組織的:

The segmap structure is part of the kernel address space and is underpinned by the segmap_datastructure that describes the properties of the segment. The size of the segment is tunable and is split into MAXBSIZE(8KB) chunks where each 8KB chunk represents an 8KB window into a file. Each chunk is referenced by an smapstructure that contains a pointer to a vnode for the file and the offset within the file.

可以看到實際chunks的容量和條目有限,當都用盡的時候,就需要把一些smap搬出去。但考慮到後面可能還有對它的通路,是以就放在一個free list表上,這樣下次讀的時候,就也不需要再從磁盤讀了。由此可見,Page Cache實際也是實作了兩層的cache。這可能是後來zfs兩級cache的前世了吧。SVR4中Page Cache 相關的資料結構整體的框圖如下:

<a href="https://s3.51cto.com/wyfs02/M02/8E/78/wKiom1jBQ_HDuwW-AACKdITHnMw058.png" target="_blank"></a>

由上圖可以看到,每個程序描述符号proc資料結構中的address space 成員 as指向目前程序的一個段表,每個段對應一個用來記錄頁映射的segmap_data資料結構,其中的smd_sm指向一組包含vnode和偏移的資料結構的連結清單。

PageCache 的操作

申請PageCache的時候:類似buffer cache中getblk()的調用,在page cache中是調用segmap_getmap()來實作的。

addr_tsegmap_getmap(struct seg *seg, vnode_t *vp, uint_t *offset);

這個函數從seg資料結構中s_base 到s_base + s_size的範圍内,往其成員segmap_data所指向的smd_sm連結清單中新增一個表示其起始虛拟位址和長度的節點,然後把這個虛拟位址傳回。這裡需要注意,在這裡隻配置設定了核心虛拟位址,并沒有核心上的實體頁框和其對應。

而在釋放PageCache的時候,調用類似buffer cache 中的brelse()函數。

int segmap_release(struct seg *seg, addr_t addr, u_int flags)

注意:這是SVR4中的Page Cache 和早期其他核心中的page cache 不一樣的一個重要差別,在SVR4中segmap_getmap()傳回的位址并沒有真實實體頁框和其對應,而是在後面真正有讀操作發生的時候才有實體頁框的資源。具體的實作過程,可以通過下面一個完整讀的例子看到。

在從檔案系統讀資料過程中,在Page Cache層會執行一端下面的代碼:

...............

kaddr = segmap_getmap(segkmap, vp, 8192);

uiomove(kaddr, 1024, UIO_READ, uiop);

segmap_release(segkmap, kaddr, SM_FREE);

segmap_getmap傳回的虛拟位址會傳遞給uiomove(), 這個函數根據UIO_READ标志開始執行具體的讀操作。首先,當它通路kaddr的時候,由于并沒有實體頁和其對應,于是會觸發一個缺頁中斷,它會從核心中kernel address space (kas)已記錄的所有的段的起始位址和長度的資訊,索引到目前位址對應的段,進而調用這個段對應的缺頁處理程式,核心的示意代碼如下:

segkmap-&gt;s_ops-&gt;fault(seg, addr, ssize, type, rw);

根據傳遞到fault句柄的s_base和addr參數, 可以從smap資料結構中找到對飲的vnode,然後調用對用的VOP_GETPAGE()函數,他會申請合适的頁并從磁盤讀回資料。等這些做完之後,page fault 函數才處理完,然後繼續執行uiomove()後續的工作。更詳細的過程可以參考下面的流程圖:

<a href="https://s4.51cto.com/wyfs02/M00/8E/78/wKiom1jBRAuj5y20AACxC4NC_r4368.png" target="_blank"></a>

通過上面的流程可以看到,SVR4通過在真正讀的時候才出發page fault的機制實作了頁隻有在真正發生讀的時候才配置設定(延時配置設定)。而檔案寫的過程在segmap_release()函數之前和讀操作的流程差不多,可能的差別在于它的flags參數,此時可能包括下面的多種:

SM_WRITE:頁應該寫回到檔案,通過VOP_PUTPAGE()

SM_ASYNC:頁可以異步寫

SM_FREE:頁可以釋放了

SM_INVAL:無效的頁

SM_DONTNEED:檔案系統不會再通路該頁了

看上去,上面的操作是不是實體cache的基本操作很像呢?

本文轉自存儲之廚51CTO部落格,原文連結:http://blog.51cto.com/xiamachao/1904907,如需轉載請自行聯系原作者

繼續閱讀