• free 命令解释


    free 命令 buffers and cached 解释

    N多人总是询问,当在linux在输入free时内存总数怎么加起来不一样啊,下面我来解释一下free命令的输出。

    我们运行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
    应该一样了吧。

    那么具体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;
    把重叠部分读减去了。
  • 相关阅读:
    win10系统下office 2019激活
    如何根据【抖音分享链接】去掉抖音水印
    Java多线程学习之ThreadLocal源码分析
    Java多线程学习之synchronized总结
    Java多线程学习之线程的取消与中断机制
    Java多线程学习之Lock与ReentranLock详解
    Java多线程学习之线程池源码详解
    MyBatis 一、二级缓存和自定义缓存
    Spring 高级依赖注入方式
    Spring 依赖注入的方式
  • 原文地址:https://www.cnblogs.com/super-king/p/3296215.html
Copyright © 2020-2023  润新知