理解Linux上的内存使用


本文是为那些经常疑惑的人准备的,“为什么一个简单的KDE文本编辑器要占用25M内存?”导致大多数人认为许多Linux应用程序,特别是KDE或GNOME程序都象ps报告一样臃肿,虽然这可能是也可能不是真的,依赖于具体的程序,它通常不是真的,一些程序比它们看起来消耗更多的内存。

ps工具能为一个进程输出许多块有关的信息,象进程ID,当前运行状态,资源利用情况等。其中可能输出VSZ(代表虚拟设置大小)和RSS(驻留设置大小),它们经常被世界各地的计算机爱好者用来查看进程占用了多少内存。

例如:下面是在我电脑上用ps aux命令为KEit的输出:

USER  PID  %CPU  %MEM  VSZ  RSS  TTY STAT START TIME COMMAND
dbunker 3468  0.0     2.7    25400  14452  ?   S    20:19 0:00  kdeinit:kedit
   
 

按照ps的输出,KEdit占用了大约25M的虚拟大小内存空间,大约14M驻留大小空间(上面报告中的两个数字都用k为单位),看起来大部分人都喜欢随意选择其中一个数字来表示某个进程的真实内存占用情况。我现在暂时先不解释VSZ和RSS之间的不同之处。不用说,前面那种认识是错误的!想要知道为什么,必须先学习Linux是如何在程序中控制共享库的。

在Linux上的大部分主要程序使用共享库有助于确定功能,例如:一个KDE文件编辑程序将使用几个KDE共享库(为了允许与其他的KDE组件进行交互),几个X库(为了允许它显示、拷贝和粘贴图像)和几个常用系统库(为了允许它执行基本的操作)。大部分这些库,特别是象libc这样常用的库,是被许多Linux程序使用的,正是由于有这些共享,Linux可以使用一个巨大的诀窍:它将只载入单个共享库的拷贝到内存中,使用这一个拷贝就可以供每个引用它的程序使用。

许多工具不再关心这个非常通用的技巧,这可能是个好现象也可能是个坏现象;它们只是简单地报告某个进程使用了多少内存,而不管是否是与其他进程共享了部分内存,两个程序使用一个很大的共享库,并且它的大小倾向于它们内存使用的总和,共享库被计入了双倍的大小,如果你不清楚这一点将使你产生误解。

不幸的是,关于进程内存使用的准确表示法不是那么容易获得,不仅需要你理解系统是如何真实地工作的,而且还需要解决你想处理的一些困难问题,一个共享库应该为那个使用它的进程内存使用进行计数吗?如果一个共享库被许多进程使用,在这些进程间它的内存使用是平均分布的吗?或者刚好可以忽略?没有一个确定的及快速的规则,依赖于你面对的位置你可能会有不同的答案。这下容易看出来为什么ps不会尽力尝试报告一个正确的内存使用总量,而是给出一个模糊的数字。

看一个进程的内存映像足以说明,让我们来看一看那个庞大的Kedit进程的位置,要查看Kedit的内存象什么样子,我们将使用pmap程序(使用-d标志)


Address Kbytes Mode Offset Device Mapping08048000 40 r-x– 0000000000000000 0fe:00000 kdeinit08052000 4 rw— 0000000000009000 0fe:00000 kdeinit08053000 1164 rw— 0000000008053000 000:00000 [ anon ]40000000 84 r-x– 0000000000000000 0fe:00000 ld-2.3.5.so40015000 8 rw— 0000000000014000 0fe:00000 ld-2.3.5.so40017000 4 rw— 0000000040017000 000:00000 [ anon ]40018000 4 r-x– 0000000000000000 0fe:00000 kedit.so40019000 4 rw— 0000000000000000 0fe:00000 kedit.so40027000 252 r-x– 0000000000000000 0fe:00000 libkparts.so.2.1.040066000 20 rw— 000000000003e000 0fe:00000 libkparts.so.2.1.04006b000 3108 r-x– 0000000000000000 0fe:00000 libkio.so.4.2.040374000 116 rw— 0000000000309000 0fe:00000 libkio.so.4.2.040391000 8 rw— 0000000040391000 000:00000 [ anon ]40393000 2644 r-x– 0000000000000000 0fe:00000 libkdeui.so.4.2.040628000 164 rw— 0000000000295000 0fe:00000 libkdeui.so.4.2.040651000 4 rw— 0000000040651000 000:00000 [ anon ]40652000 100 r-x– 0000000000000000 0fe:00000 libkdesu.so.4.2.04066b000 4 rw— 0000000000019000 0fe:00000 libkdesu.so.4.2.04066c000 68 r-x– 0000000000000000 0fe:00000 libkwalletclient.so.1.0.04067d000 4 rw— 0000000000011000 0fe:00000 libkwalletclient.so.1.0.04067e000 4 rw— 000000004067e000 000:00000 [ anon ]4067f000 2148 r-x– 0000000000000000 0fe:00000 libkdecore.so.4.2.040898000 64 rw— 0000000000219000 0fe:00000 libkdecore.so.4.2.0408a8000 8 rw— 00000000408a8000 000:00000 [ anon ]…. (trimmed) …mapped: 25404K writeable/private: 2432K shared: 0K

我剪掉了许多输出内容,剩下的与展示出来的类似,即使没有完整的输出,我们也可以看到一些非常有趣的内容,一个重要的内容就是注意到每一个共享库都列出了两次,一次为它的代码段一次为它的数据段,代码段具有“r-x-”样式,而数据段具有“rw--”样式,我们关心的只有字节数、样式和映像栏,剩下的对于讨论都是不重要的。

如果你仔细检查输出内容,你会发现最大字节数的行通常是包含共享库(以lib开头的行就是共享库)的代码段行,如果你找出了在进程之间所有的共享部分,它们以“writeable/private” 结束,显示在输出的底端,这可以理解为进程的消耗增量,因此,运行Kedit(假设所有共享库都已经被载入了)实例大约要占用2M,这和ps报告的14或25M完全不是一回事。

这意味着什么?

这个故事的寓意是Linux进程内存使用是一个复杂的事情,你不能仅通过运行ps来了解,当你处理一个创建了大量子进程的程序时特别真实,如Apache,ps可能报告每个Apache进程使用10M内存,实际上每个Apache进程只消耗了1M内存,在调整Apache的MaxClients参数(它决定了你的服务器能同时处理的请求数量,)设置时这个信息变得非常重要。

同时,它也适应于桌面软件,如果你运行了KDE,但是几乎全部使用Gnome应用程序,那么你将为多余的(但是不同的)共享库付出巨大的代价,因此请尽量保持要么全部运行KDE应用程序要么全部运行Gnome应用程序,这样Linux就可以使用更多的内存来做其他事情(如文件缓存,它可以极大地提高文件的访问速度)。

本文作者:
« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3