N多人老是詢問,當在linux在輸入free時內存總數怎麼加起來不同啊,下面我來解釋一下free命令的輸出。node
咱們運行free命令時都會看到以下的信息:
#free
total used free shared buffers cached
Mem: 1025236 1002324 22912 0 26900 228140
-/+ buffers/cache: 747284 277952
Swap: 1044216 53912 990304
今天我主要講講第一行的buffers和cached的含義。
free命令顯示的 buffers 上面是 26900 和 cached 上面是 228140,這些數字是從 /proc/meminfo中獲取的。
#cat /proc/meminfo | grep Buffers
Buffers: 26912 kB
#cat /proc/meminfo | grep Cached
Cached: 228180 kB
會有些不一樣是由於你沒有在同時運行命令,命令之間有時間間隔,你再試試這個命令:
#cat /proc/meminfo | grep Buffers;free
Buffers: 27624 kB
total used free shared buffers cached
Mem: 1025236 987924 37312 0 27624 197972
應該同樣了吧。linux
那麼具體buffers和cached表明什麼意思吶,咱們看一下kernel code. 首先咱們看buffers proc中的meminfo初始化是在 proc_misc_init ,其中會 初始化 meminfo_read_proc 函數,當咱們cat /proc/meminfo此函數會被調用。 meminfo_read_proc 會打印Buffers信息 是經過 i.bufferram的值打印的,具體 i 是什麼結構很簡單你看看代碼就明白了。 咱們主要看看i.bufferram是怎麼統計的。 meminfo_read_proc -> si_meminfo(&i) 進行統計 void si_meminfo(struct sysinfo *val) { ... val->bufferram = nr_blockdev_pages(); //統計Buffers ... } long nr_blockdev_pages(void) { struct list_head *p; long ret = 0; spin_lock(&bdev_lock); list_for_each(p, &all_bdevs) { //查看全部塊設備 struct block_device *bdev; bdev = list_entry(p, struct block_device, bd_list); //指向一個塊設備 ret += bdev->bd_inode->i_mapping->nrpages; //統計頁數 } spin_unlock(&bdev_lock); return ret; } 函數意思是查找全部的塊設備而後經過塊設備的inode的i_mapping統計頁數,那麼nrpages是怎麼增長的,誰又會引用inode和i_mapping,咱們繼續向下看。 int add_to_page_cache(struct page *page, struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) { int error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); if (error == 0) { write_lock_irq(&mapping->tree_lock); error = radix_tree_insert(&mapping->page_tree, offset, page); if (!error) { page_cache_get(page); SetPageLocked(page); page->mapping = mapping; page->index = offset; mapping->nrpages++; //更新mapping的nrpages字段 __inc_zone_page_state(page, NR_FILE_PAGES); } write_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); } return error; } void __remove_from_page_cache(struct page *page) { struct address_space *mapping = page->mapping; radix_tree_delete(&mapping->page_tree, page->index); page->mapping = NULL; mapping->nrpages--; __dec_zone_page_state(page, NR_FILE_PAGES); } 我搜索kernel發現最終更新mapping->nrpages字段的函數就是add_to_page_cache和__femove_frome_page_cache函數, 那麼他們使用的mapping是塊設備的嗎仍是文件的吶,文件的inode中自帶了mapping若是是文件的mapping傳遞進來了,那麼更新的就不是塊設備的信息,Buffer信息也不會反應。 若是傳遞的是塊設備的mapping那麼相應的統計信息會從Buffer中反應出來。 那麼咱們下面就能夠看看kernel中哪些地方是把塊設備的mapping傳遞進來了。 sys_read ->vfs_read 其中會調用 file->f_op->read(file, buf, count, pos); 那麼若是文件是一個目錄就會調用相關目錄的操做函數read,咱們以ext3舉例: sys_getdents (讀目錄系統調用) -> vfs_readdir -> file->f_op->readdir(file, buf, filler); 調用相關文件系統的讀目錄函數 那麼ext3就下面 const struct file_operations ext3_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = ext3_readdir, // 讀目錄函數 .ioctl = ext3_ioctl, /* BKL held */ .fsync = ext3_sync_file, /* BKL held */ #ifdef CONFIG_EXT3_INDEX .release = ext3_release_dir, #endif }; ext3_readdir 函數會調用 ext3_get_blocks_handle,page_cache_readahead等函數 調用page_cache_readahead 時明顯傳遞的是 sb->s_bdev->bd_inode->i_mapping,都會更新塊設備的mapping->nrpages字段, 就是說當進行讀目錄時可能會增長Buffers的值,相應的當對目錄進行操做時,像在目錄中創建文件,重命名等動做都會涉及到mapping->nrpage字段。 還有就是當讀取文件的間接塊時也會更新mapping->nrpage字段,具體看看ext3_get_blocks_handle函數你就會明白了。 而後咱們看cached meminfo_read_proc中有 cached = global_page_state(NR_FILE_PAGES) - total_swapcache_pages - i.bufferram; //NR_FILE_PAGES的頁面 - 全部交換緩存頁和咱們上面講的buffers頁。 那麼global_page_state(NR_FILE_PAGES)是什麼,咱們下面進行解釋。 static inline unsigned long global_page_state(enum zone_stat_item item) { long x = atomic_long_read(&vm_stat[item]); #ifdef CONFIG_SMP if (x < 0) x = 0; #endif return x; } vm_stat是一個枚舉數組,下面的結構是枚舉值。 enum zone_stat_item { NR_ANON_PAGES, /* Mapped anonymous pages */ NR_FILE_MAPPED, /* pagecache pages mapped into pagetables. only modified from process context */ NR_FILE_PAGES, NR_SLAB, /* Pages used by slab allocator */ NR_PAGETABLE, /* used for pagetables */ NR_FILE_DIRTY, NR_WRITEBACK, NR_UNSTABLE_NFS, /* NFS unstable pages */ NR_BOUNCE, #ifdef CONFIG_NUMA NUMA_HIT, /* allocated in intended node */ NUMA_MISS, /* allocated in non intended node */ NUMA_FOREIGN, /* was intended here, hit elsewhere */ NUMA_INTERLEAVE_HIT, /* interleaver preferred this zone */ NUMA_LOCAL, /* allocation from local node */ NUMA_OTHER, /* allocation from other node */ #endif NR_VM_ZONE_STAT_ITEMS }; 看看上面兩個函數 add_to_page_cache 有 __inc_zone_page_state(page, NR_FILE_PAGES); _remove_from_page_cache 有 __dec_zone_page_state(page, NR_FILE_PAGES); __add_to_swap_cache 也有 __inc_zone_page_state(page, NR_FILE_PAGES); __delete_from_swap_cache 進行減小操做。 就是說全部調用 add_to_page_cache的函數都會進行統計,包括目錄操做,文件的數據讀取,再加上swap的就是NR_FILE_PAGES全部進行統計的信息。 這樣會不會有重疊吶,不會看上面 cached 的算法 cached = global_page_state(NR_FILE_PAGES) - total_swapcache_pages - i.bufferram; 把重疊部分讀減去了。