1. 程式人生 > >Cache 和 Buffer 都是快取,主要區別是什麼?

Cache 和 Buffer 都是快取,主要區別是什麼?

linux的buffer與cache,見文章:

如文中我有理解錯誤的地方也請各位及時指出,如轉載請註明出處。

(本文所有截圖來自《深入理解linux核心-第三版》 DANIEL P.BOVET & MARCO
CESATE著 陳莉君 張瓊聲 張巨集偉 譯 中國電力出版社)

先說總結:

1. Linux2.4.10之前的核心中,分兩種disk cache, 分別為buffer cache和page cache,區別見文章最後兩個截圖(至於具體裡面放什麼結構的資料,我也不知道,還沒看過2.4核心的塊裝置和VFS這部分內容)。

2. 大約2.4.10後的核心,buffer cache已經不存在了(或者說換了一種資料存放的方法) 而這種頁面的叫法也變了,叫做 buffer page, 而buffer page放在什麼地主呢? 它放在page cache中。

聽著很繞口, 或者可以這樣講: 2.4.10之後的核心的disk cache 只有 page cache, 而page cache中有些頁面被叫做buffer page的,是因為這些頁面(buffer page)都有與其相關的buffer_head 描述符,也正是這樣頁面被free 統計為 buffer 佔用。如果沒有buffer_head與該頁相關,則被free統計為 cache佔用。 Buffer page 與 buffer_head(下圖中右邊方框裡的 緩衝區首部 )的關聯如下 截圖1:

<img src="https://pic2.zhimg.com/50/v2-4cbec5e9539e6dd78193a7214e168b8b_hd.jpg" data-size="normal" data-rawwidth="554" data-rawheight="279" class="origin_image zh-lightbox-thumb" width="554" data-original="https://pic2.zhimg.com/v2-4cbec5e9539e6dd78193a7214e168b8b_r.jpg">
(截圖 1, 書611頁)

緩衝區首部(buffer_head)的結構(include/linux/fs.h):

struct buffer_head {
/* First cache line: */
struct buffer_head *b_next; /* Hash queue list*/
unsigned long b_blocknr; /* block number */
unsigned short b_size; /* block size */
unsigned short b_list; /* List that this buffer appears */
kdev_t b_dev; /* device (B_FREE = free) */
atomic_t b_count; /* users using this block */
kdev_t b_rdev; /* Real device */
……
char * b_data; /* pointer to data block (512byte) */
struct page *b_page; /* the page this bh is mapped to */
void (*b_end_io)(struct buffer_head *bh, intuptodate); /* I/O completion */
void *b_private; /* reserved for b_end_io */
unsigned long b_rsector; /* Real buffer location on disk */
wait_queue_head_t b_wait;
struct inode * b_inode;
struct list_head b_inode_buffers; /* doubly
linked list of inode dirty buffers */
};

注意結構中下面的三個欄位:

  • kdev_t b_dev:表示包含該塊的塊裝置,通常是一個磁碟或分割槽
  • unsigned long b_blocknr;存放邏輯塊號,表示塊在磁碟或分割槽上的邏輯塊號
  • struct page *b_page; 指向包含該塊的頁描述符

buffer_head 結構中以上三個欄位把頁中的內容和磁碟上的塊直接關聯起來。

下面來說說,buffer page裡面放的是什麼

首先我們應該瞭解何時,核心會分配buffer page:

<img src="https://pic4.zhimg.com/50/v2-a519950740989c94d9b5e2e2f538dc6a_hd.jpg" data-size="normal" data-rawwidth="554" data-rawheight="94" class="origin_image zh-lightbox-thumb" width="554" data-original="https://pic4.zhimg.com/v2-a519950740989c94d9b5e2e2f538dc6a_r.jpg">(截圖2, 書609頁)<img src="https://pic3.zhimg.com/50/v2-2ec35fcdfcb0831f09bb6e0f12d7dbed_hd.jpg" data-size="normal" data-rawwidth="612" data-rawheight="343" class="origin_image zh-lightbox-thumb" width="612" data-original="https://pic3.zhimg.com/v2-2ec35fcdfcb0831f09bb6e0f12d7dbed_r.jpg">(截圖3,書610頁)

對於截圖3中說的第一種情況,這裡舉個例子,如下面圖中,如果該頁屬於同一個檔案,且檔案所在的檔案系統的 block size 為1k, 頁面中的4個block如果在磁碟上都不連續,那麼這個頁面就成為buffer page,且有與頁中4個block相關的buffer_head結構:

<img src="https://pic2.zhimg.com/50/v2-142a68961a3497709a44a39a6c617f89_hd.jpg" data-size="normal" data-rawwidth="423" data-rawheight="342" class="origin_image zh-lightbox-thumb" width="423" data-original="https://pic2.zhimg.com/v2-142a68961a3497709a44a39a6c617f89_r.jpg">(截圖4,書560頁)<img src="https://pic2.zhimg.com/50/v2-17c08ddf65cc8967a0f5721601009bea_hd.jpg" data-size="normal" data-rawwidth="554" data-rawheight="73" class="origin_image zh-lightbox-thumb" width="554" data-original="https://pic2.zhimg.com/v2-17c08ddf65cc8967a0f5721601009bea_r.jpg">(截圖5,書610頁)

對於截圖3中所說的第二種情況:

<img src="https://pic1.zhimg.com/50/v2-4eb05423ccc752725b2553e834488607_hd.jpg" data-size="normal" data-rawwidth="538" data-rawheight="290" class="origin_image zh-lightbox-thumb" width="538" data-original="https://pic1.zhimg.com/v2-4eb05423ccc752725b2553e834488607_r.jpg">(截圖6,書610頁)

由些可見,從磁碟讀出來的檔案在file system上的inode(ext3_inode ?)資訊是放在buffer page裡的,但是,這種page的所有者(如果一個cache頁面是某檔案的內容,那麼我們說這個檔案是此頁面的所有都,或者屬主)是哪個檔案呢?往下看截圖7的第二段:

<img src="https://pic1.zhimg.com/50/v2-a2f47616f0a7fff780ce30e0c6674a78_hd.jpg" data-size="normal" data-rawwidth="554" data-rawheight="255" class="origin_image zh-lightbox-thumb" width="554" data-original="https://pic1.zhimg.com/v2-a2f47616f0a7fff780ce30e0c6674a78_r.jpg">(截圖7-書599頁)

Buffer Page的所有者(即屬主)是這個檔案系統所在裝置的總裝置檔案(如,所有 /dev/sdb1,/dev/sdb2, 的buffer page的所有者都是 /dev/sdb這主個裝置檔案,因為對於磁碟裝置驅動程式來說,處理各分割槽讀寫是無差別的,而且對分割槽的讀寫最後是要合併後並給主裝置的,所以分割槽並不會提高讀寫效能,但分割槽可以保持資料隔離,一個分割槽檔案系統崩毀不會影響到其他分割槽的檔案)。

最後,直接訪問一個裝置檔案會發生什麼呢?架個虛擬機器,啟動一段時間後,待free輸出cache和buffer部分穩定後,再執行下面內容:

#free 記錄輸出中buffer和cache的大小

#cat /proc/slabinfo |grep buffer_head 記錄buffer_head的分配和使用數量

#cat /dev/sdb1 >/dev/null 這條命令執行完成後,再執行free 和cat /proc/slabinfo 檢視輸出 並和前面做對比。

buffer:

從 free 命令的程式碼裡(專案地址 http://procps.sourceforge.net)
可以得知命令的數值實際上是 從 /proc/meminfo 來的,而 meminfo 的數值是從系統呼叫 sysinfo 來的。

文章裡提到了一個函式 si_meminfo mm/page_alloc.c

void si_meminfo(struct sysinfo *val) 
{ 
val->totalram = totalram_pages; 
val->sharedram = 0; 
val->freeram = global_page_state(NR_FREE_PAGES); 
val->bufferram = nr_blockdev_pages(); 
val->totalhigh = totalhigh_pages; 
val->freehigh = nr_free_highpages(); 
val->mem_unit = PAGE_SIZE; 
} 

可以看到 bufferram 的值是函式 nr_blockdev_pages() 得到的 fs/block_dev.c

long nr_blockdev_pages(void)
{
struct block_device *bdev;
long ret = 0;
spin_lock(&bdev_lock);
list_for_each_entry(bdev,&all_bdevs, bd_list) 
{
    ret+= bdev->bd_inode->i_mapping->nrpages;
}
spin_unlock(&bdev_lock);
return ret;
}

可以看到這個函式其實就是,遍歷了所有的塊裝置,然後把這些裝置 bd_inode 上的 pages 數目加起來。這些 page 由核心通過一顆 radix tree 來維護,而 nrpages 記錄了tree pages 的數量。buffer 統計的是所有block device 對應的inode的 address_space的 page的數量。

如何增加 buffers ?

cat /dev/sda1 > /dev/null

所以可以確定的是直接訪問 block 裝置產生的 page cache是儲存到 block device的 bd_inode 裡面的。

另:關於linux 2.4.10之前核心對buffer和cache 的描述:

<img src="https://pic2.zhimg.com/50/v2-96ba2b2596202feeaa34efd01a8d8bb8_hd.jpg" data-size="normal" data-rawwidth="554" data-rawheight="312" class="origin_image zh-lightbox-thumb" width="554" data-original="https://pic2.zhimg.com/v2-96ba2b2596202feeaa34efd01a8d8bb8_r.jpg">(截圖8—書607頁)

英文版:

<img src="https://pic4.zhimg.com/50/v2-967aae8304770127ef99f283a3c46f31_hd.jpg" data-caption="" data-size="normal" data-rawwidth="554" data-rawheight="281" class="origin_image zh-lightbox-thumb" width="554" data-original="https://pic4.zhimg.com/v2-967aae8304770127ef99f283a3c46f31_r.jpg">