通过获取Linux中的 /proc/stat 文件中的内容可以获取系统内存的详细信息:
# cat /proc/meminfoMemTotal: 3880404 kBMemFree: 3182248 kBMemAvailable: 3396580 kBBuffers: 39588 kBCached: 355616 kBSwapCached: 0 kBActive: 318708 kBInactive: 252380 kBActive(anon): 176120 kBInactive(anon): 248 kBActive(file): 142588 kBInactive(file): 252132 kBUnevictable: 0 kBMlocked: 0 kBSwapTotal: 0 kBSwapFree: 0 kBDirty: 0 kBWriteback: 0 kBAnonPages: 175940 kBMapped: 54184 kBShmem: 488 kBSlab: 62040 kBSReclaimable: 48712 kBSUnreclaim: 13328 kBKernelStack: 2672 kBPageTables: 6080 kBNFS_Unstable: 0 kBBounce: 0 kBWritebackTmp: 0 kBCommitLimit: 1940200 kBCommitted_AS: 615340 kBVmallocTotal: 34359738367 kBVmallocUsed: 14160 kBVmallocChunk: 34359715580 kBHardwareCorrupted: 0 kBAnonHugePages: 36864 kBCmaTotal: 0 kBCmaFree: 0 kBHugePages_Total: 0HugePages_Free: 0HugePages_Rsvd: 0HugePages_Surp: 0Hugepagesize: 2048 kBDirectMap4k: 53120 kBDirectMap2M: 3092480 kBDirectMap1G: 3145728 kB
各字段含义见下表:
| 指标 | 作用 |
|---|---|
| MemTotal | 总内存大小 |
| MemFree | 空闲内存大小 |
| buffers/cached | 磁盘缓存的大小 |
| MemAvailable | 可用内存大小 |
| SwapTotal | 可用的swap空间的总的大小。 |
| SwapFree | 剩余swap空间的大小。 |
| Dirty | 需要写入磁盘的内存区大小。 |
| Writeback | 正在被写回磁盘的大小。 |
| AnonPages | 未映射页的内存大小。 |
| Mapped | 设备和文件等映射的大小。 |
| Slab | 内核数据结构slab的大小,可以减少申请和释放内存带来的消耗。 |
注Buffers 和 Cached的区别
Buffers 是指用来给块设备做的缓冲大小,他只记录文件系统的metadata以及 tracking in-flight pages. cached 是用来给文件做缓冲。
buffers 是值存储目录里面有什么内容,权限等等。 而cached直接用来记忆我们打开的文件,比如先后执行两次命令#man X ,你就可以明显的感觉到第二次的开打的速度快很多。 而buffers随时都在增加,比如先后两次使用ls /dev后,就会发现第二次执行的速度会较第一次快。 这就是buffers/chached的区别。
以内核态来讲,
buffers和cached是已经被使用的,可用内存就是MemAvailableMemUsed = MemTotal - MemFree
以用户态来讲,
buffers和cached可被分配。free = MemFree + Buffers + Cahched
本着监控应用对物理内存使用情况的目的采集,计算方法:
MemUsedPrec = 100*(MemTotal - MemFree - Buffers - Cahched)/MemTotal
黑洞
经过一番计算,发现 /proc/meminfo 中的数据无论如何无法与 free 中的内容对应,尤其是在 used 部分。经过一番信息检索,得出一个结论, free 命令中的数值是按照 /proc/meminfo 中的数据,根据一定算法计算所得,并且新版旧版的 free 所输出内容也不一致。因此按照 proc/meminfo 中的数据计算内存使用率是更加精确的。
进一步探索,会发现 Linux 存在一个内存黑洞,在某博主博客找到如下描述:
追踪Linux系统的内存使用一直是个难题,很多人试着把能想到的各种内存消耗都加在一起,kernel text、kernel modules、buffer、cache、slab、page table、process RSS…等等,却总是与物理内存的大小对不上,这是为什么呢?因为Linux kernel并没有滴水不漏地统计所有的内存分配,kernel动态分配的内存中就有一部分没有计入/proc/meminfo中。 —— 《/PROC/MEMINFO之谜》
综上原因,计算Linux内存使用率也就没有必要去细扣每个数值的含义了,大致了解如下内容即可:
- MemTotal:总内存大小
- MemFree: 空闲内存大小
- buffers/cached: 磁盘缓存的大小
- MemAvailable: 可用内存大小
而计算内存使用率只需按照这个方法计算:
实际可挪用的内存数: free+cache+buffer,实际可使用的内存数: used-cache-buffer (total-free-cache-buffer)。内存占用率:(total-available) / total * 100
C实现
同样的, 先定义一个结构体用于存放相关数据:
struct MEM_INFO{unsigned int total;unsigned int free;unsigned int buffers;unsigned int cached;unsigned int swap_cached;unsigned int swap_total;unsigned int swap_free;unsigned int available;};typedef struct MEM_INFO Mem_info;
之后定义函数,用于获取及计算内存数据:
void get_mem_occupy (Mem_info *o){FILE* fpMemInfo = fopen("/proc/meminfo", "r");if (NULL == fpMemInfo){return ;}int i = 0;int value;char name[1024];char line[1024];int nFiledNumber = 2;int nMemberNumber = 5;while (fgets(line, sizeof(line) - 1, fpMemInfo)){if (sscanf(line, "%s%u", name, &value) != nFiledNumber){continue;}if (0 == strcmp(name, "MemTotal:")){++i;o->total = value;}else if (0 == strcmp(name, "MemFree:")){++i;o->free = value;}else if (0 == strcmp(name, "MemAvailable:")){++i;o->available = value;}else if (0 == strcmp(name, "Buffers:")){++i;o->buffers = value;}else if (0 == strcmp(name, "Cached:")){++i;o->cached = value;}if (i == nMemberNumber){break;}}// system("free");// system("cat /proc/meminfo");// printf("MemTotal : %d\n",o->total);// printf("MemFree : %d\n",o->free);// printf("MemAvailable : %d\n",o->available);// printf("MemBuffers : %d\n",o->buffers);// printf("MemCached : %d\n",o->cached);// printf("MemSwapCached : %d\n",o->swap_cached);// printf("MemSwapTotal : %d\n",o->swap_total);// printf("MemSwapFree : %d\n",o->swap_free);fclose(fpMemInfo);}float cal_mem_occupy(Mem_info *o){return (100.0 * (o->total - o->available) / o->total);}
最后调用即可:
Mem_info omem;while(1){// printf("-------------------- Mem occupy -------------------\n");get_mem_occupy(&omem);printf("Mem Usage(%): %8.4f\n", cal_mem_occupy(&omem));printf("\n");}
