free 命令显示 swap 信息异常处理
free 结果异常
在执行一个占用很多内存的操作后, free 命令显示的结果中, swap
的 used
远远超过 swap total
的值, free
的值也大于 total, 如下所示:
# free -k
total used free shared buffers cached
Mem: 132110132 104895792 27214340 44 0 824716
-/+ buffers/cache: 104071076 28039056
Swap: 31439200 18014398509479232 31441952
系统环境:
CentOS release 6.6 (Final)
128G mem
Linux cz 2.6.32-573.3.1.el6.x86_64
为什么出现这么大的值
通过查看 free 命令的源代码说明: https://github.com/mmalecki/procps/
free.c
文件关于 swap 信息的代码:
22 #define S(X) ( ((unsigned long long)(X) << 10) >> shift)
...
...
38 int shift = 10;
...
...
100 printf(
101 "%-7s %10Lu %10Lu %10Lu\n", "Swap:",
102 S(kb_swap_total),
103 S(kb_swap_used),
104 S(kb_swap_free)
105 );
追踪 kb_swap_used
到 proc/sysinfo.c
文件的代码:
41 #define MEMINFO_FILE "/proc/meminfo"
...
589 FILE_TO_BUF(MEMINFO_FILE,meminfo_fd);
...
621 kb_swap_used = kb_swap_total - kb_swap_free;
622 kb_main_used = kb_main_total - kb_main_free;
从代码来看 free 命令是通过读取 /proc/meminfo
的信息来显示内存及 swap 的使用, 通过 free -m
函数可以看到 free 大于 total 的总量, 在这里的话 X 即为负数 -2752
, 在宏定义函数 S 中, 将 X 强制转换为64位的无符号整形, 表达式 (unsigned long long) (X)
等效于 2^64 - 2752
, 计算出结果后左移 10 位再右移 10(shift 值) 位, 得出结果 18014398509479232
.
为什么会发生 swap free 大于 swap total 的现象
在 kernel-2.6.32-573.7.1
版本之前, 函数 get_swap_page 在加锁的过程中去掉了自旋锁 swap_lock
, 这可能会引起 nr_swap_pages
检测异常使得 /proc/meminfo
记录失效的 swapfree
数值, 由此可能引起 swapfree
大于 swaptotal
的现象. 详见: RHBA-2015-1827.html
bug 说明
* A previous change in the get_swap_page() locking removed the use of the
swap_lock spinlock. This could cause nr_swap_pages corruption and invalid
SwapFree information in the /proc/meminfo file, where the size of SwapFree could
exceed the size of SwapTotal. This update uses an atomic variable for
nr_swap_pages, and the size of SwapFree in /proc/meminfo is now correct.
(BZ#1259362)
如何让 free 显示的结果正常
上述问题的原因在于 kernel 方面的 bug 而引起, 所以要永久杜绝该现象可以升级内核到 2.6.32-573.7.1
版本, 重启后即可生效;
如果不升级 kernel
的话, 只是简单的 swapoff/swapon
是不能让结果正常显示的, 因为 swap 的使用未见变化, 基于此参考链接: http://www.linuxatemyram.com/play.html
我们手工测试占用部分 swap 空间就可以使 free 显示正常. 示例代码中的内存分配可适当调大, 结果显示如下:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define N 1024*1024*1000
int main(int argc, char** argv) {
int max = -1;
int mb = 0;
char* buffer;
if(argc > 1)
max = atoi(argv[1]);
while((buffer=malloc(N)) != NULL && mb != max) {
memset(buffer, 0, N);
mb += 1000;
printf("Allocated %d MB\n", mb);
sleep(2);
}
return 0;
}
结果如下:
# free -m
total used free shared buffers cached
Mem: 129013 127216 1797 1 0 2051
-/+ buffers/cache: 125164 3849
Swap: 30702 17592186044413 30705
# free -m
total used free shared buffers cached
Mem: 129013 128218 795 1 0 2051
-/+ buffers/cache: 126166 2847
Swap: 30702 17592186044413 30705
# free -m
total used free shared buffers cached
Mem: 129013 128637 376 1 0 2048
-/+ buffers/cache: 126588 2425
Swap: 30702 90 30611
总结
在内存急速被占尽的情况下, 由于旧版本 kernel
的函数处理可能会引起该问题的发生, 进而造成 /proc/meminfo
数据信息的异常, 最后导致 free 命令的错误. 如果要杜绝该现象, 需要将 kernel
升级到 kernel-2.6.32-573.7.1
版本. 另外也应该尽量避免运行急速消耗内存的进程.