1. General
1.1 /proc/meminfo
/proc/meminfo是了解Linux系统内存使用状况主要接口,也是free等命令的数据来源。
下面是cat /proc/meminfo的一个实例。
MemTotal: 8054880 kB---------------------物理内存总容量,对应totalram_pages大小。 MemFree: 4004312 kB---------------------空闲内存容量,对应vm_stat[NR_FREE_PAGES]大小。 MemAvailable: 5678888 kB---------------------MemFree减去保留内存,加上部分pagecache和部分SReclaimable。 Buffers: 303016 kB---------------------块设备缓冲区大小. Cached: 2029616 kB---------------------主要是vm_stat[NR_FILE_PAGES],再减去swap出的大小和块设备缓冲区大小。Buffers+Cached=Active(file)+Inactive(file)+Shmem。 SwapCached: 0 kB---------------------交换缓存上的内容容量。 Active: 2123084 kB---------------------Active=Active(anon)+Active(file)。 Inactive: 1476268 kB---------------------Inactive=Inactive(anon)+Inactive(file)。 Active(anon): 1273544 kB---------------------活动匿名内存,匿名指进程中堆上分配的内存,活动指最近被使用的内存。 Inactive(anon): 547988 kB---------------------不活动匿名内存,在内存不足时优先释放。 Active(file): 849540 kB---------------------活动文件缓存,表示内存内容与磁盘上文件相关联。 Inactive(file): 928280 kB---------------------不活动文件缓存。 Unevictable: 17152 kB---------------------不可移动的内存,当然也不可释放,所以不会放在LRU中。 Mlocked: 17152 kB---------------------使用mlocked()处理的页面。 SwapTotal: 7812092 kB---------------------交换空间总容量。 SwapFree: 7812092 kB---------------------交换空间剩余容量。 Dirty: 6796 kB---------------------脏数据,在磁盘缓冲区中尚未写入磁盘的内存大小。 Writeback: 0 kB---------------------待回写的页面大小。 AnonPages: 1283984 kB---------------------内核中存在一个rmap(Reverse Mapping)机制,负责管理匿名内存中每一个物理页面映射到哪个进程的那个逻辑地址等信息。rmap中记录的内存页综合就是AnonPages值。 Mapped: 455248 kB---------------------映射的文件占用内存大小。 Shmem: 550260 kB---------------------vm_stat[NR_SHMEM],tmpfs所使用的内存,tmpfs即利用物理内存来提供RAM磁盘功能。在tmpfa上保存文件时,文件系统暂时将他们保存到RAM中。 Slab: 268208 kB---------------------slab分配器总量,通过slabinfo工具或者/proc/slabinfo来查看更详细的信息。 SReclaimable: 206964 kB---------------------不存在活跃对象,可回收的slab缓存vm_stat[NR_SLAB_RECLAIMABLE]。 SUnreclaim: 61244 kB---------------------对象处于活跃状态,不能被回收的slab容量。 KernelStack: 12736 kB---------------------内核代码使用的堆栈区。 PageTables: 50376 kB---------------------PageTables就是页表,用于存储各个用户进程的逻辑地址和物理地址的变化关系,本身也是一个内存区域。 NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 11839532 kB Committed_AS: 7934688 kB VmallocTotal: 34359738367 kB------------------理论上内核可以用来映射的逻辑地址范围。 VmallocUsed: 0 kB---------------------内核将空闲内存页。 VmallocChunk: 0 kB HardwareCorrupted: 0 kB AnonHugePages: 0 kB ShmemHugePages: 0 kB ShmemPmdMapped: 0 kB CmaTotal: 0 kB CmaFree: 0 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB DirectMap4k: 226256 kB DirectMap2M: 5953536 kB DirectMap1G: 3145728 kB
/proc/meminfo对应内核的核心函数是meminfo_proc_show(), 包括两个重要的填充sysinfo的函数si_meminfo()和si_swapinfo()。
MemTotal是系统从加电开始到引导完成,除去kernel本身要占用一些内存,最后剩下可供kernel支配的内存。
MemFree表示系统尚未使用的内存;MemAvailable表示系统可用内存,因为应用会根据系统可用内存大小动态调整申请内存大小,MemFree并不适用,因为有些内存是可以回收的,所以这部分内存要加上可回收内存。
PageTables用于将内存的虚拟地址翻译成物理地址,随着内存地址分配的越来越多,PageTable会增大。/proc/meminfo中的PageTables就是统计PageTable所占用内存大小。
KernelStack是常驻内存的,既不包括在LRU链表中,也不包括在进程RSS、PSS中,所以认为它是内核消耗的内存。
static int meminfo_proc_show(struct seq_file *m, void *v) { struct sysinfo i; unsigned long committed; long cached; long available; unsigned long pagecache; unsigned long wmark_low = 0; unsigned long pages[NR_LRU_LISTS]; struct zone *zone; int lru; /* * display in kilobytes. */ #define K(x) ((x) << (PAGE_SHIFT - 10))
si_meminfo(&i); si_swapinfo(&i); committed = percpu_counter_read_positive(&vm_committed_as); cached = global_page_state(NR_FILE_PAGES) - total_swapcache_pages() - i.bufferram;---------------------vm_stat[NR_FILE_PAGES]减去swap的页面和块设备缓存页面。 if (cached < 0) cached = 0; for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++) pages[lru] = global_page_state(NR_LRU_BASE + lru);--------------遍历获取vm_stat中的5种LRU页面大小。 for_each_zone(zone) wmark_low += zone->watermark[WMARK_LOW]; /* * Estimate the amount of memory available for userspace allocations, * without causing swapping. */ available = i.freeram - totalreserve_pages;--------------------------vm_stat[NR_FREE_PAGES]减去保留页面totalreserve_pages。 /* * Not all the page cache can be freed, otherwise the system will * start swapping. Assume at least half of the page cache, or the * low watermark worth of cache, needs to stay. */ pagecache = pages[LRU_ACTIVE_FILE] + pages[LRU_INACTIVE_FILE];------pagecache包括活跃和不活跃文件LRU页面两部分。 pagecache -= min(pagecache / 2, wmark_low);-------------------------保留min(pagecache/2, wmark_low)大小,确保不会被释放。 available += pagecache;---------------------------------------------可用页面增加可释放的pagecache部分。 /* * Part of the reclaimable slab consists of items that are in use, * and cannot be freed. Cap this estimate at the low watermark. */ available += global_page_state(NR_SLAB_RECLAIMABLE) - min(global_page_state(NR_SLAB_RECLAIMABLE) / 2, wmark_low);--类似pagecache,可回收slab缓存保留一部分不可释放。其余部分给available。 if (available < 0) available = 0; /* * Tagged format, for easy grepping and expansion. */ seq_printf(m, "MemTotal: %8lu kB " "MemFree: %8lu kB " "MemAvailable: %8lu kB " "Buffers: %8lu kB " "Cached: %8lu kB " "SwapCached: %8lu kB " "Active: %8lu kB " "Inactive: %8lu kB " "Active(anon): %8lu kB " "Inactive(anon): %8lu kB " "Active(file): %8lu kB " "Inactive(file): %8lu kB " "Unevictable: %8lu kB " "Mlocked: %8lu kB " #ifdef CONFIG_HIGHMEM "HighTotal: %8lu kB " "HighFree: %8lu kB " "LowTotal: %8lu kB " "LowFree: %8lu kB " #endif #ifndef CONFIG_MMU "MmapCopy: %8lu kB " #endif "SwapTotal: %8lu kB " "SwapFree: %8lu kB " "Dirty: %8lu kB " "Writeback: %8lu kB " "AnonPages: %8lu kB " "Mapped: %8lu kB " "Shmem: %8lu kB " "Slab: %8lu kB " "SReclaimable: %8lu kB " "SUnreclaim: %8lu kB " "KernelStack: %8lu kB " "PageTables: %8lu kB " #ifdef CONFIG_QUICKLIST "Quicklists: %8lu kB " #endif "NFS_Unstable: %8lu kB " "Bounce: %8lu kB " "WritebackTmp: %8lu kB " "CommitLimit: %8lu kB " "Committed_AS: %8lu kB " "VmallocTotal: %8lu kB " "VmallocUsed: %8lu kB " "VmallocChunk: %8lu kB " #ifdef CONFIG_MEMORY_FAILURE "HardwareCorrupted: %5lu kB " #endif #ifdef CONFIG_TRANSPARENT_HUGEPAGE "AnonHugePages: %8lu kB " #endif #ifdef CONFIG_CMA "CmaTotal: %8lu kB " "CmaFree: %8lu kB " #endif , K(i.totalram),-------------------------------------------------即totalram_pages大小 K(i.freeram),--------------------------------------------------即vm_stat[NR_FREE_PAGES] K(available),--------------------------------------------------等于freeram减去保留totalreserve_pages,以及一部分pagecache和可回收slab缓存。 K(i.bufferram),------------------------------------------------通过nr_blockdev_pages()获取。 K(cached),-----------------------------------------------------vm_stat[NR_FILE_PAGES]减去swap部分以及块设备缓存。 K(total_swapcache_pages()),------------------------------------swap交换占用的页面大小。 K(pages[LRU_ACTIVE_ANON] + pages[LRU_ACTIVE_FILE]),----------活跃页面大小 K(pages[LRU_INACTIVE_ANON] + pages[LRU_INACTIVE_FILE]),--------不活跃页面大小 K(pages[LRU_ACTIVE_ANON]), K(pages[LRU_INACTIVE_ANON]), K(pages[LRU_ACTIVE_FILE]), K(pages[LRU_INACTIVE_FILE]), K(pages[LRU_UNEVICTABLE]),-------------------------------------不能被pageout/swapout的内存页面 K(global_page_state(NR_MLOCK)), #ifdef CONFIG_HIGHMEM K(i.totalhigh), K(i.freehigh), K(i.totalram-i.totalhigh), K(i.freeram-i.freehigh), #endif #ifndef CONFIG_MMU K((unsigned long) atomic_long_read(&mmap_pages_allocated)), #endif K(i.totalswap),------------------------------------------------总swap空间大小 K(i.freeswap),-------------------------------------------------空闲swap空间大小 K(global_page_state(NR_FILE_DIRTY)),---------------------------等待被写回磁盘文件大小 K(global_page_state(NR_WRITEBACK)),----------------------------正在被回写文件的大小 K(global_page_state(NR_ANON_PAGES)),---------------------------映射的匿名页面 K(global_page_state(NR_FILE_MAPPED)),--------------------------映射的文件页面 K(i.sharedram),------------------------------------------------即vm_stat[NR_SHMEM] K(global_page_state(NR_SLAB_RECLAIMABLE) + global_page_state(NR_SLAB_UNRECLAIMABLE)),-------------slab缓存包括可回收和不可回收两部分,vm_stat[NR_SLAB_RECLAIMABLE]+vm_stat[NR_SLAB_UNRECLAIMABLE]。 K(global_page_state(NR_SLAB_RECLAIMABLE)), K(global_page_state(NR_SLAB_UNRECLAIMABLE)), global_page_state(NR_KERNEL_STACK) * THREAD_SIZE / 1024,-------vm_stat[NR_KERNEL_STACK]大小 K(global_page_state(NR_PAGETABLE)),----------------------------pagetables所占大小 #ifdef CONFIG_QUICKLIST K(quicklist_total_size()), #endif K(global_page_state(NR_UNSTABLE_NFS)), K(global_page_state(NR_BOUNCE)), K(global_page_state(NR_WRITEBACK_TEMP)), K(vm_commit_limit()), K(committed), (unsigned long)VMALLOC_TOTAL >> 10,----------------------------vmalloc虚拟空间的大小 0ul, // used to be vmalloc 'used' 0ul // used to be vmalloc 'largest_chunk' #ifdef CONFIG_MEMORY_FAILURE , atomic_long_read(&num_poisoned_pages) << (PAGE_SHIFT - 10) #endif #ifdef CONFIG_TRANSPARENT_HUGEPAGE , K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) * HPAGE_PMD_NR) #endif #ifdef CONFIG_CMA , K(totalcma_pages) , K(global_page_state(NR_FREE_CMA_PAGES)) #endif ); hugetlb_report_meminfo(m); arch_report_meminfo(m); return 0; #undef K } void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; val->sharedram = global_page_state(NR_SHMEM); 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; } void si_swapinfo(struct sysinfo *val) { unsigned int type; unsigned long nr_to_be_unused = 0; spin_lock(&swap_lock); for (type = 0; type < nr_swapfiles; type++) { struct swap_info_struct *si = swap_info[type]; if ((si->flags & SWP_USED) && !(si->flags & SWP_WRITEOK)) nr_to_be_unused += si->inuse_pages; } val->freeswap = atomic_long_read(&nr_swap_pages) + nr_to_be_unused; val->totalswap = total_swap_pages + nr_to_be_unused; spin_unlock(&swap_lock); }
参考文档:《/PROC/MEMINFO之谜》
1.2 free
free命令用来显示内存的使用情况。
free -s 2 -c 2 -w -t -h
含义为-s 每2秒显示一次,-c 共2次,-w buff/cache分开显示,-t 显示total,-h 可读性更高。
结果如下:
total used free shared buffers cache available Mem: 7.7G 1.4G 3.8G 534M 295M 2.1G 5.4G Swap: 7.5G 0B 7.5G Total: 15G 1.4G 11G total used free shared buffers cache available Mem: 7.7G 1.4G 3.8G 537M 295M 2.1G 5.4G Swap: 7.5G 0B 7.5G Total: 15G 1.4G 11G
Mem一行指的是RAM的使用情况,Swap一行是交换分区的使用情况。
free命令是procps-ng包的一部分,主体在free.c中。这些参数的获取在meminfo()中进行。
int main(int argc, char **argv) { ... do { meminfo(); /* Translation Hint: You can use 9 character words in * the header, and the words need to be right align to * beginning of a number. */ if (flags & FREE_WIDE) { printf(_(" total used free shared buffers cache available")); } else { printf(_(" total used free shared buff/cache available")); } printf(" "); printf("%-7s", _("Mem:")); printf(" %11s", scale_size(kb_main_total, flags, args)); printf(" %11s", scale_size(kb_main_used, flags, args)); printf(" %11s", scale_size(kb_main_free, flags, args)); printf(" %11s", scale_size(kb_main_shared, flags, args)); if (flags & FREE_WIDE) { printf(" %11s", scale_size(kb_main_buffers, flags, args)); printf(" %11s", scale_size(kb_main_cached, flags, args)); } else { printf(" %11s", scale_size(kb_main_buffers+kb_main_cached, flags, args)); } printf(" %11s", scale_size(kb_main_available, flags, args)); printf(" "); ... printf("%-7s", _("Swap:")); printf(" %11s", scale_size(kb_swap_total, flags, args)); printf(" %11s", scale_size(kb_swap_used, flags, args)); printf(" %11s", scale_size(kb_swap_free, flags, args)); printf(" "); if (flags & FREE_TOTAL) { printf("%-7s", _("Total:")); printf(" %11s", scale_size(kb_main_total + kb_swap_total, flags, args)); printf(" %11s", scale_size(kb_main_used + kb_swap_used, flags, args)); printf(" %11s", scale_size(kb_main_free + kb_swap_free, flags, args)); printf(" "); } fflush(stdout); if (flags & FREE_REPEATCOUNT) { args.repeat_counter--; if (args.repeat_counter < 1) exit(EXIT_SUCCESS); } if (flags & FREE_REPEAT) { printf(" "); usleep(args.repeat_interval); } } while ((flags & FREE_REPEAT)); exit(EXIT_SUCCESS); }
解析部分在sysinfo.c中。通过解析/proc/meminfo信息,计算出free的各项值。
/proc/meminfo和free的对应关系如下:
free | /proc/meminfo |
total | =MemTotal |
used | =MemTotal - MemFree - (Cached + SReclaimable) - Buffers |
free | =MemFree |
shared | =Shmem |
buffers | =Buffers |
cache | =Cached + SReclaimable |
available | =MemAvailable |
void meminfo(void){ char namebuf[32]; /* big enough to hold any row name */ int linux_version_code = procps_linux_version(); mem_table_struct findme = { namebuf, NULL}; mem_table_struct *found; char *head; char *tail; static const mem_table_struct mem_table[] = { {"Active", &kb_active}, // important {"Active(file)", &kb_active_file}, {"AnonPages", &kb_anon_pages}, {"Bounce", &kb_bounce}, {"Buffers", &kb_main_buffers}, // important {"Cached", &kb_page_cache}, // important {"CommitLimit", &kb_commit_limit}, {"Committed_AS", &kb_committed_as}, {"Dirty", &kb_dirty}, // kB version of vmstat nr_dirty {"HighFree", &kb_high_free}, {"HighTotal", &kb_high_total}, {"Inact_clean", &kb_inact_clean}, {"Inact_dirty", &kb_inact_dirty}, {"Inact_laundry",&kb_inact_laundry}, {"Inact_target", &kb_inact_target}, {"Inactive", &kb_inactive}, // important {"Inactive(file)",&kb_inactive_file}, {"LowFree", &kb_low_free}, {"LowTotal", &kb_low_total}, {"Mapped", &kb_mapped}, // kB version of vmstat nr_mapped {"MemAvailable", &kb_main_available}, // important {"MemFree", &kb_main_free}, // important {"MemTotal", &kb_main_total}, // important {"NFS_Unstable", &kb_nfs_unstable}, {"PageTables", &kb_pagetables}, // kB version of vmstat nr_page_table_pages {"ReverseMaps", &nr_reversemaps}, // same as vmstat nr_page_table_pages {"SReclaimable", &kb_slab_reclaimable}, // "slab reclaimable" (dentry and inode structures) {"SUnreclaim", &kb_slab_unreclaimable}, {"Shmem", &kb_main_shared}, // kernel 2.6.32 and later {"Slab", &kb_slab}, // kB version of vmstat nr_slab {"SwapCached", &kb_swap_cached}, {"SwapFree", &kb_swap_free}, // important {"SwapTotal", &kb_swap_total}, // important {"VmallocChunk", &kb_vmalloc_chunk}, {"VmallocTotal", &kb_vmalloc_total}, {"VmallocUsed", &kb_vmalloc_used}, {"Writeback", &kb_writeback}, // kB version of vmstat nr_writeback }; const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct); unsigned long watermark_low; signed long mem_available, mem_used; FILE_TO_BUF(MEMINFO_FILE,meminfo_fd); kb_inactive = ~0UL; kb_low_total = kb_main_available = 0; head = buf; for(;;){ tail = strchr(head, ':'); if(!tail) break; *tail = '