free 命令解釋

free 命令 buffers and cached 解釋

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;
把重疊部分讀減去了。
相關文章
相關標籤/搜索