本文解释 top(1) 中某些与内存使用量有关的统计量的意义,以澄清一些相当普遍的对于这些统计量的误解。
这是一个坑,待填……
每进程的统计量主要是 SIZE 和 RES 两个。
SIZE 表示的是进程向系统申请的内存的数量,RES 表示的是进程实际使用到的数量。
误解:xx 程序在 top(1) 里 SIZE 涨到 xxxM 了,吃掉了好多内存!
澄清:一个程序真正占用了多少内存,是根据 RES 而不是根据 SIZE 来看。
RES 和 SIZE 的差别可以通过下面的两个程序来体现。
#include <sys/cdefs.h> #include <sys/types.h> #include <sysexits.h> #include <stdlib.h> #include <unistd.h> int main(void) { void *ptr; ptr = malloc(1024 * 1024 * 200); for (;;) { sleep(1); } return (EX_OK); }
#include <sys/cdefs.h> #include <sys/types.h> #include <sysexits.h> #include <stdlib.h> #include <unistd.h> int main(void) { char *ptr; off_t off; ptr = malloc(1024 * 1024 * 200); for (off = 0; off < 1024 * 1024 * 200; off += 4096) { ptr[off] = '0'; } for (;;) { sleep(1); } return (EX_OK); }
在 top(1) 中,可以看到第一个程序运行的时候,这个进程的 RES 比 SIZE 至少小了 200M(根据运行的平台不同,这个差值具体是多少可能有一些差别)。而第二个程序运行的时候,这个进程的 RES 基本上和 SIZE 相同,都超过 200M。实际上这个现象就是由于第二个程序访问了分配的内存,而第一个仅仅分配了内存,并没有访问这些内存,这个差别造成了内存使用的不同。
误解:Apache 用 prefork MPM 每个进程的 RES 都在 xxM 以上,吃掉了好多内存!
澄清:累加所有进程的 RES 并不等于整个系统所用的内存。
下面第三个程序说明这种情况。
#include <sys/cdefs.h> #include <sys/types.h> #include <sysexits.h> #include <stdlib.h> #include <unistd.h> int main(void) { char *ptr; off_t off; ptr = malloc(1024 * 1024 * 200); for (off = 0; off < 1024 * 1024 * 200; off += 4096) { ptr[off] = '0'; } fork(); fork(); for (;;) { sleep(1); } return (EX_OK); }
运行这个程序之后应该有四个 RES 超过 200M 的进程。但实际上系统总共耗费在这个程序上的内存大约也只有 200M 多一点,这是因为这个程序在两次 fork(2) 的时候没有大规模地写入数据,所以大部分内存仍然是共享的。然而这个状态将会由于新内容的写入而被破坏,第四个程序说明了这种情况。
#include <sys/cdefs.h> #include <sys/types.h> #include <sysexits.h> #include <stdlib.h> #include <unistd.h> int main(void) { char *ptr; off_t off; ptr = malloc(1024 * 1024 * 200); for (off = 0; off < 1024 * 1024 * 200; off += 4096) { ptr[off] = '0'; } fork(); fork(); for (off = 0; off < 1024 * 1024 * 200; off += 4096) { ptr[off] = '0'; } for (;;) { sleep(1); } return (EX_OK); }
注意,内存少于 1G 的系统最好不要运行这个程序!
这个程序与第三个程序的差别主要在于 fork(2) 之后,每个进程都把 ptr 指向的内存写了一遍,虽然内容都一样,但是由于破坏了内存的共享,每个进程都要消耗掉 200M 以上的内存。
这里演示了两个极端情况,第三个程序代表的是四个进程完全共享 ptr 指向的内存,而第四个程序代表的是四个进程完全不共享 ptr 指向的内存。但是在实际运行的系统中,以 Apache 为例,运行了 prefork MPM 的 Apache 经常出现的状况是介于这两者之间的,所以单纯累加各个进程 RES 一点意义都没有。并且更糟糕的事情是,没有一个很简单的方法可以获得某一组特定的进程真正占用的内存到底是多少。
本文避免了论述 page fault、copy on write 这些概念,主要以一些简单的程序为例简要讨论了一些有关 top(1) 的误解,并澄清这些误解。