Contents

Linux过程的峰值内存使用

1. 概述

Linux 的设计旨在尽可能高效地使用所有可用的物理内存。但是,有时,系统资源限制会导致服务器端的突然行为。通常,这些限制会触发高 CPU 和高内存使用率。无论如何,我们可以通过观察进程的内存利用率来真正避免这种突然的行为。

在本教程中,我们将学习使用一些著名的 Linux 命令来实现一些技巧和窍门,以帮助我们识别进程的内存使用峰值。

2. 监控内存的传统命令

在大多数情况下,top */ htop / atop *等命令为我们提供了流程概览。在特定情况下,它们也可用于监视特定过程。在这里,我们专注于检查进程以确定其峰值内存利用率。

我们可以从调查顶部 结果开始,以查看过程概述。这给出了一个关于所有进程都在使用什么的想法。

假设我们需要对特定进程进行归零,并且我们知道它的进程 ID (PID):

$ top -p 7
top - 10:25:53 up 19 min,  0 users,  load average: 0.52, 0.58, 0.59
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s): 16.0 us,  6.2 sy,  0.0 ni, 77.1 id,  0.0 wa,  0.7 hi,  0.0 si,  0.0 st
MiB Mem :   3961.1 total,    665.1 free,   3072.0 used,    224.0 buff/cache
MiB Swap:  12288.0 total,  11806.3 free,    481.7 used.    758.5 avail Mem
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
    7 user1     20   0   18076   3632   3528 S   0.0   0.1   0:00.19 bash

现在我们来看看bash的用法:

$ top | grep bash
    7 user1     20   0   18076   3632   3528 S   0.0   0.1   0:00.19 bash
   73 root      20   0   18076   3604   3540 S   0.0   0.1   0:00.17 bash

htop与*top 类似,*但显示更多关于进程的数据。有命令栏,方便识别进程路径。

atop又是一个类似tophtop的命令。它的优点是在文件中记录输出的非常有用的功能。

考虑一个在特定时间窗口一次又一次发生的问题。为了保持记录,我们可以安排一个 cron 作业将输出写入文件,这样以后回放就变得可行了。如果我们需要将输出记录到文件中,我们将使用atop -w

$ atop -w filename

如果我们需要播放该文件的输出,我们将使用atop -r

$ atop -r filename
  PID      SYSCPU       USRCPU        VGROW        RGROW       RUID           EUID           ST       EXC        THR       S       CPUNR        CPU       CMD        1/1   73       0.17s        0.03s       481.1G        3604K       root           root           N-         -          1       S           0         0%       bash
    7       0.10s        0.09s       591.0G        3632K       user1          user1          N-         -          1       S           0         0%       bash
    1       0.15s        0.00s       376.9G         316K       root           root           N-         -          2       S           0         0%       init
   71       0.04s        0.00s       716.4G        2908K       root           root           N-         -          1       S           0         0%       sudo
   72       0.03s        0.00s       820.0G        2092K       root           root           N-         -          1       S           0         0%       su
  109       0.01s        0.00s         1.0T        2136K       root           root           N-         -          1       R           0         0%       atop
    6       0.00s        0.01s       376.9G         224K       root           root           N-         -          1       S           0         0%       init

这三个命令可以成为持续调查的最佳工具。对于我们的情况,我们可以在进程达到设定的限制之前识别出进程的峰值内存使用量。

3. grep 单行

/proc 虚拟文件系统 是一个目录,其中包含代表 Linux 内核当前状态的文件层次结构。它还包括有关任何当前正在运行的进程的信息。

这是一个确定具有进程 id (PID) 113 的此类进程的峰值内存使用量的单行程序:

$ grep ^VmPeak /proc/113/status 
VmPeak: 2252 kB

我们还可以查找“VmHWM:Peak resident set size”来衡量 RAM 使用情况。VmPeak 是最大总内存使用量,包括虚拟内存,而 VmHWM 是峰值 RAM 使用量

众所周知,/proc是一个虚拟文件系统,因此从它的文件中读取与从普通文件系统中读取是不同的。从/proc中删除有关进程的信息比从真正的文件系统中删除要快得多(此处涉及脏缓存刷新 )。

考虑到这一点,假设我们需要读取尚未缓冲的进程的下一行。在这种情况下,有关它的信息可能已被删除。我们可能不需要有关不再存在的流程的信息。解决方案是要么考虑文件丢失,要么缓冲整个文件,然后解析它。

4. GNU时间

让我们通过几个例子来了解给定上下文中的 GNU*time *。 假设我们想知道“ top ”进程的内存使用峰值。在这种情况下,“最大驻留集大小”告诉我们:

$ /usr/bin/time -v top | grep "Maximum resident set size"
Maximum resident set size (kbytes): 2252

GNU时间支持格式选项。除了内存使用之外,带有*%P选项的 GNU**时间*提供了不相关的统计信息 (%CPU),这取决于调度程序,因此变化很大:

$ /usr/bin/time -f "%P %M" top
2% 2248

在 bash 中,我们需要指定完整路径,例如/usr/bin/time,因为 bash 内置的time关键字不支持*-f*选项**:

$ /usr/bin/time -f '%M' top 
2248

我们还可以创建别名或调整环境以使用 GNU时间来获取平均和最大内存信息:

alias time="$(which time) -f '\t%E real,\t%U user,\t%S sys,\t%K amem,\t%M mmem'"
export TIME="$(which time) -f '\t%E real,\t%U user,\t%S sys,\t%K amem,\t%M mmem'"

存储在内存段 中的这些信息代表了进程的平均总(数据+堆栈+文本)内存使用(K)和最大驻留集大小(M)。

4.1. 论据和注意事项

让我们通过讨论一些已知问题来进行time命令事实检查。

第一个问题是某些 Linux 系统上的内存报告可能会浪费时间。**当我们使用/usr/bin/time -v ls确定“最大驻留集大小”时,我们可以看到大多数时候它返回 0。它总是返回 0,因为时间wait3(2) 系统中获取大部分信息称呼。

没有*wait3(2)调用的系统,使用time(2) 系统调用。但是,它提供的信息比wait3(2)*少得多。因此,此类系统将大部分资源报告为 0。

或者,尝试更占用 CPU 的命令可能会奏效。

第二个问题是在调用time -v时,输出**“bash: -v: command not found”*意味着 bash 拦截时间以使用自己的内置时间函数。使用/bin/time -v*解决了这个问题。

第三,GNU time有一个 bug。它报告实际内存使用量的 4 倍。Ubuntu 14.4 上的time 1.7-24和 Fedora 1.7-3 版本的time包包含对内存报告的修复。

4.2. 内置time与 GNUtime

如上所述,在 bash 中,我们需要指定 GNU time的完整路径,例如*/usr/bin/time*。或者,我们可以使用命令 time -l

请务必注意,该命令不是占位符。命令 timetime不同。命令 time -l调用 shell 来调用名为time的二进制文件,而不是内置函数。

5. Valgrind 单线

*Valgrind *是一个为用户空间二进制文件提供检测的框架。它附带了几个可用于分析和分析程序性能的工具。

Massif 是Valgrind工具之一,用于测量特定程序使用的堆内存。一个简单的valgrind massif one-liner 来描述“ top ”进程的峰值内存使用情况,如下所示:

$ valgrind --tool=massif --pages-as-heap=yes --massif-out-file=massif.out top; grep mem_heap_B massif.out | sed -e 's/mem_heap_B=\(.*\)/\1/' | sort -g | tail -n 1
==746== Massif, a heap profiler
==746== Copyright (C) 2003-2017, and GNU GPL'd, by Nicholas Nethercote
==746== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==746== Command: top
==746==
==746== error calling PR_SET_PTRACER, vgdb might block
top - 21:47:03 up 23 min,  0 users,  load average: 0.52, 0.58, 0.59
Tasks:   8 total,   1 running,   6 sleeping,   1 stopped,   0 zombie
%Cpu(s):  2.8 us,  1.9 sy,  0.0 ni, 95.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   3961.1 total,	408.3 free,   3328.8 used,	224.0 buff/cache
MiB Swap:  12288.0 total,  12014.8 free,	273.2 used.	501.7 avail Mem
  PID USER  	PR  NI	VIRT	RES	SHR S  %CPU  %MEM 	TIME+ COMMAND
	1 root  	20   0	8940	308	268 S   0.0   0.0   0:00.20 init
	6 root  	20   0	8940	220	180 S   0.0   0.0   0:00.01 init
	7 user1 	20   0   18080   3456   3348 S   0.0   0.1   0:00.27 bash
   72 root  	20   0   18924   2900   2800 S   0.0   0.1   0:00.10 sudo
   73 root  	20   0   18048   2080   2056 S   0.0   0.1   0:00.07 su
   74 root  	20   0   18076   3608   3516 S   0.0   0.1   0:00.41 bash
  366 root  	20   0   18444   1784   1324 T   0.0   0.0   0:00.01 top
  746 root  	20   0   70528  26284   3072 R   0.0   0.6   0:01.24 massif-amd64-li
==746==
15691776

输出 15691776 是“ top ”进程的内存使用峰值。

默认情况下,Massif 仅测量堆内存。但是,如果我们希望测量程序使用的所有内存,我们可以使用 — pages-as-heap=yes

Massif将分析数据输出到massif.out文件。ms_print 工具将此分析数据绘制成图表,以显示程序执行过程中的内存消耗。它还显示了有关在内存分配峰值点负责分配的站点的详细信息。我们可以使用以下命令绘制 massif.out文件中的数据:

ms_print massif.out

6. 其他现代工具:Heaptrack 和 Busybox

*Heaptrack *是一个同时具有 GUI 和文本界面的 KDE 工具。它以火焰图的形式提供峰值内存使用量。它比 Valgrind 做的检查更少,因此速度更快

我们可以通过跟踪*/proc/[pid]/smaps中的 PSS 数量或使用pmap来确定峰值内存。我们还可以将heaptrack*附加到已经运行的进程:

heaptrack --pid $(pid of <your application>)

heaptrack输出被写入*/tmp/heaptrack.APP.PID.gz*。

*Busybox *将许多常见 UNIX 实用程序的微小版本组合成一个小型可执行文件。它为我们通常在 GNU Coreutils、until-Linux 等中找到的大多数实用程序提供了极简主义的替代品。

Busybox预装在大多数 Linux 发行版中,包括 Debian 和 Ubuntu。它可以用来代替许多现代发行版中不存在的命令。同样,要了解峰值内存使用情况,我们可以使用带有*-v参数的busybox 时间*实现:

$ /usr/bin/time busybox time -v uname -r | grep "Maximum resident set size"
Maximum resident set size (kbytes): 1792

它的输出类似于 GNU时间输出。