free 命令是Linux系統(tǒng)上查看內(nèi)存使用狀況最常用的工具,然而很少有人能說清楚 “buffers” 與 “cached” 之間的區(qū)別:
我們先拋出結(jié)論,如果你對(duì)研究過程感興趣可以繼續(xù)閱讀后面的段落:
buffers 表示塊設(shè)備(block device)所占用的緩存頁,包括:直接讀寫塊設(shè)備、以及文件系統(tǒng)元數(shù)據(jù)(metadata),比如SuperBlock所使用的緩存頁;
cached 表示普通文件數(shù)據(jù)所占用的緩存頁。
下面是分析過程:
先用 strace 跟蹤 free 命令,看看它是如何計(jì)算 buffers 和 cached 的:
#stracefree ... open("/proc/meminfo",O_RDONLY)=3 lseek(3,0,SEEK_SET)=0 read(3,"MemTotal:3848656kB MemF"...,2047)=1170 ...
顯然 free 命令是從 /proc/meminfo 中讀取信息的,跟我們直接讀到的結(jié)果一樣:
#cat/proc/meminfo MemTotal:3848656kB MemFree:865640kB Buffers:324432kB Cached:2024904kB ... SwapTotal:2031612kB SwapFree:2031612kB ... Shmem:5312kB ...
那么 /proc/meminfo 中的 Buffers 和 Cached 又是如何得來的呢?這回沒法偷懶,只能去看源代碼了。源代碼文件是:fs/proc/meminfo.c ,我們感興趣的函數(shù)是:meminfo_proc_show(),閱讀得知:
Cached 來自于以下公式:
global_page_state(NR_FILE_PAGES)–total_swapcache_pages–i.bufferram
global_page_state(NR_FILE_PAGES) 表示所有的緩存頁(page cache)的總和,它包括:
Cached
Buffers 也就是上面公式中的 i.bufferram,來自于 nr_blockdev_pages() 函數(shù)的返回值。
交換區(qū)緩存(swap cache)
global_page_state(NR_FILE_PAGES) 來自 vmstat[NR_FILE_PAGES],vmstat[NR_FILE_PAGES] 可以通過 /proc/vmstat 來查看,表示所有緩存頁的總數(shù)量:
#cat/proc/vmstat ... nr_file_pages587334 ...
注意以上nr_file_pages是以page為單位(一個(gè)page等于4KB),而free命令是以KB為單位的。
直接修改 nr_file_pages 的內(nèi)核函數(shù)是:__inc_zone_page_state(page, NR_FILE_PAGES) 和__dec_zone_page_state(page, NR_FILE_PAGES),一個(gè)用于增加,一個(gè)用于減少。
Swap Cache是什么?
用戶進(jìn)程的內(nèi)存頁分為兩種:file-backed pages(與文件對(duì)應(yīng)的內(nèi)存頁)和anonymous pages(匿名頁)。匿名頁(anonymous pages)是沒有關(guān)聯(lián)任何文件的,比如用戶進(jìn)程通過malloc()申請(qǐng)的內(nèi)存頁,如果發(fā)生swapping換頁,它們沒有關(guān)聯(lián)的文件進(jìn)行回寫,所以只能寫入到交換區(qū)里。
交換區(qū)可以包括一個(gè)或多個(gè)交換區(qū)設(shè)備(裸盤、邏輯卷、文件都可以充當(dāng)交換區(qū)設(shè)備),每一個(gè)交換區(qū)設(shè)備在內(nèi)存里都有對(duì)應(yīng)的swap cache,可以把swap cache理解為交換區(qū)設(shè)備的page cache:page cache對(duì)應(yīng)的是一個(gè)個(gè)文件,swap cache對(duì)應(yīng)的是一個(gè)個(gè)交換區(qū)設(shè)備,kernel管理swap cache與管理page cache一樣,用的都是radix-tree,唯一的區(qū)別是:page cache與文件的對(duì)應(yīng)關(guān)系在打開文件時(shí)就確定了,而一個(gè)匿名頁只有在即將被swap-out的時(shí)候才決定它會(huì)被放到哪一個(gè)交換區(qū)設(shè)備,即匿名頁與swap cache的對(duì)應(yīng)關(guān)系在即將被swap-out時(shí)才確立。
并不是每一個(gè)匿名頁都在swap cache中,只有以下情形之一的匿名頁才在:
匿名頁即將被swap-out時(shí)會(huì)先被放進(jìn)swap cache,但通常只存在很短暫的時(shí)間,因?yàn)榫o接著在pageout完成之后它就會(huì)從swap cache中刪除,畢竟swap-out的目的就是為了騰出空閑內(nèi)存;【注:參見mm/vmscan.c: shrink_page_list(),它調(diào)用的add_to_swap()會(huì)把swap cache頁面標(biāo)記成dirty,然后它調(diào)用try_to_unmap()將頁面對(duì)應(yīng)的page table mapping都刪除,再調(diào)用pageout()回寫dirty page,最后try_to_free_swap()會(huì)把該頁從swap cache中刪除。】
曾經(jīng)被swap-out現(xiàn)在又被swap-in的匿名頁會(huì)在swap cache中,直到頁面中的內(nèi)容發(fā)生變化、或者原來用過的交換區(qū)空間被回收為止。【注:當(dāng)匿名頁的內(nèi)容發(fā)生變化時(shí)會(huì)刪除對(duì)應(yīng)的swap cache,代碼參見mm/swapfile.c: reuse_swap_page()。】
cached:
Cached 表示除去 buffers 和 swap cache 之外,剩下的也就是普通文件的緩存頁的數(shù)量:
global_page_state(NR_FILE_PAGES)–total_swapcache_pages–i.bufferram
所以關(guān)鍵還是要理解 buffers 是什么含義。
buffers:
從源代碼中看到,buffers 來自于 nr_blockdev_pages() 函數(shù)的返回值:
longnr_blockdev_pages(void) { structblock_device*bdev; longret=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); returnret; }
這段代碼的意思是遍歷所有的塊設(shè)備(block device),累加每個(gè)塊設(shè)備的inode的i_mapping的頁數(shù),統(tǒng)計(jì)得到的就是 buffers。顯然 buffers 是與塊設(shè)備直接相關(guān)的。
那么誰會(huì)更新塊設(shè)備的緩存頁數(shù)量(nrpages)呢?我們繼續(xù)向下看。
搜索kernel源代碼發(fā)現(xiàn),最終點(diǎn)我更新mapping->nrpages字段的函數(shù)就是:
pagemap.h:add_to_page_cache >filemap.c:add_to_page_cache_locked
C__add_to_page_cache_locked >page_cache_tree_insert 和: filemap.c:delete_from_page_cache >__delete_from_page_cache >page_cache_tree_delete
staticinlineintadd_to_page_cache(structpage*page, structaddress_space*mapping,pgoff_toffset,gfp_tgfp_mask) { interror; __set_page_locked(page); error=add_to_page_cache_locked(page,mapping,offset,gfp_mask); if(unlikely(error)) __clear_page_locked(page); returnerror; } voiddelete_from_page_cache(structpage*page) { structaddress_space*mapping=page->mapping; void(*freepage)(structpage*); BUG_ON(!PageLocked(page)); freepage=mapping->a_ops->freepage; spin_lock_irq(&mapping->tree_lock); __delete_from_page_cache(page,NULL); spin_unlock_irq(&mapping->tree_lock); mem_cgroup_uncharge_cache_page(page); if(freepage) freepage(page); page_cache_release(page); }
這兩個(gè)函數(shù)是通用的,block device 和 文件inode 都可以調(diào)用,至于更新的是塊設(shè)備的(buffers)還是文件的(cached),取決于參數(shù)變量mapping:如果mapping對(duì)應(yīng)的是塊設(shè)備,那么相應(yīng)的統(tǒng)計(jì)信息會(huì)反映在 buffers 中;如果mapping對(duì)應(yīng)的是文件inode,影響的就是 cached。我們下面看看kernel中哪些地方會(huì)把塊設(shè)備的mapping傳遞進(jìn)來。
首先是塊設(shè)備本身,打開時(shí)使用 bdev->bd_inode->i_mapping。
staticintblkdev_open(structinode*inode,structfile*filp) { structblock_device*bdev; /* *Preservebackwardscompatibilityandallowlargefileaccess *evenifuserspacedoesn'taskforitexplicitly.Somemkfs *binaryneedsit.Wemightwanttodropthisworkaround *duringanunstablebranch. */ filp->f_flags|=O_LARGEFILE; if(filp->f_flags&O_NDELAY) filp->f_mode|=FMODE_NDELAY; if(filp->f_flags&O_EXCL) filp->f_mode|=FMODE_EXCL; if((filp->f_flags&O_ACCMODE)==3) filp->f_mode|=FMODE_WRITE_IOCTL; bdev=bd_acquire(inode); if(bdev==NULL) return-ENOMEM; filp->f_mapping=bdev->bd_inode->i_mapping; returnblkdev_get(bdev,filp->f_mode,filp); }
其次,文件系統(tǒng)的Superblock也是使用塊設(shè)備:
structsuper_block{ ... structblock_device*s_bdev; ... } intinode_init_always(structsuper_block*sb,structinode*inode) { ... if(sb->s_bdev){ structbacking_dev_info*bdi; bdi=sb->s_bdev->bd_inode->i_mapping->backing_dev_info; mapping->backing_dev_info=bdi; } ... }
sb表示SuperBlock,s_bdev就是塊設(shè)備。Superblock是文件系統(tǒng)的metadata(元數(shù)據(jù)),不屬于文件,沒有對(duì)應(yīng)的inode,所以,對(duì)metadata操作所涉及的緩存頁都只能利用塊設(shè)備mapping,算入 buffers 的統(tǒng)計(jì)值內(nèi)。
如果文件含有間接塊(indirect blocks),因?yàn)殚g接塊也屬于metadata,所以走的也是塊設(shè)備的mapping。查看源代碼,果然如此:
ext4_get_blocks ->ext4_ind_get_blocks ->ext4_get_branch ->sb_getblk staticinlinestructbuffer_head* sb_getblk(structsuper_block*sb,sector_tblock) { return__getblk(sb->s_bdev,block,sb->s_blocksize); }
這樣我們就知道了buffers 是塊設(shè)備(block device)占用的緩存頁,分為兩種情況:
直接對(duì)塊設(shè)備進(jìn)行讀寫操作;
文件系統(tǒng)的metadata(元數(shù)據(jù)),比如 SuperBlock。
驗(yàn)證:
現(xiàn)在我們來做個(gè)測試,驗(yàn)證一下上述結(jié)論。既然文件系統(tǒng)的metadata會(huì)用到 buffers,我們用 find 命令掃描文件系統(tǒng),觀察 buffers 增加的情況:
#free totalusedfreesharedbufferscached Mem:3848656288950895914853162638962023340 -/+buffers/cache:6022723246384 Swap:203161202031612 #find/-nameabc.def #free totalusedfreesharedbufferscached Mem:3848656298405286460453203196122023348 -/+buffers/cache:6410923207564 Swap:203161202031612
再測試一下直接讀取block device,觀察buffers增加的現(xiàn)象:
#free totalusedfreesharedbufferscached Mem:3848656300694484171253163310202028648 -/+buffers/cache:6472763201380 Swap:203161202031612 #ddif=/dev/sda1of=/dev/nullcount=2000 2000+0recordsin 2000+0recordsout 1024000bytes(1.0MB)copied,0.026413s,38.8MB/s #free totalusedfreesharedbufferscached Mem:3848656300770484095253163318722028692 -/+buffers/cache:6471403201516 Swap:203161202031612
結(jié)論:
free 命令所顯示的 buffers 表示塊設(shè)備(block device)所占用的緩存頁,包括直接讀寫塊設(shè)備、以及文件系統(tǒng)元數(shù)據(jù)(metadata)如SuperBlock所使用的緩存頁;而 cached 表示普通文件所占用的緩存頁。
-
內(nèi)存
+關(guān)注
關(guān)注
8文章
3108瀏覽量
74979 -
Linux系統(tǒng)
+關(guān)注
關(guān)注
4文章
603瀏覽量
28292 -
命令
+關(guān)注
關(guān)注
5文章
726瀏覽量
22668 -
Buffers
+關(guān)注
關(guān)注
0文章
13瀏覽量
10543
原文標(biāo)題:【內(nèi)存】buffers與cached的區(qū)別
文章出處:【微信號(hào):嵌入式與Linux那些事,微信公眾號(hào):嵌入式與Linux那些事】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
堆棧內(nèi)存和堆內(nèi)存之間的區(qū)別

評(píng)論