在Linux下,如何发现哪个进程使用交换空间更多?


当前回答

您可以使用Procpath(作者在这里),以简化从/proc/$PID/status解析vmswwap。

$ procpath record -f stat,cmdline,status -r 1 -d db.sqlite
$ sqlite3 -column db.sqlite \
  'SELECT status_name, status_vmswap FROM record ORDER BY status_vmswap DESC LIMIT 5'
Web Content  192136       
okular       186872       
thunderbird  183692       
Web Content  143404       
MainThread   86300

您还可以像这样绘制感兴趣的进程的vmswwap。在这里,我正在记录Firefox进程树,同时打开几十个选项卡,并启动一个占用大量内存的应用程序,试图导致它进行交换(这对Firefox来说并不令人信服,但您的情况可能不同)。

$ procpath record -f stat,cmdline,status -i 1 -d db2.sqlite \
  '$..children[?(@.stat.pid == 6029)]'
# interrupt by Ctrl+C
$ procpath plot -d db2.sqlite -q cpu --custom-value-expr status_vmswap \
  --title "CPU usage, % vs Swap, kB"

其他回答

top命令还包含一个字段,用于显示进程的页面错误数量。页面错误最多的进程是交换次数最多的进程。 对于长时间运行的守护进程,可能会在开始时产生大量的页面错误,并且这个数字在以后不会增加。所以我们需要观察页面故障是否在增加。

不完全清楚您的意思是要找到交换出最多页的进程还是导致交换出最多页的进程。

对于前者,您可以运行top并按swap排序(按'Op'),对于后者,您可以运行vmstat并查找'so'的非零条目。

Iotop是一个非常有用的工具。它提供了每个进程/线程的I/O和交换使用情况的实时统计数据。默认情况下,它显示每个线程,但你可以执行iotop -P来获取每个进程的信息。默认情况下,这是不可用的。您可能需要通过rpm/apt安装。

自2015年内核补丁添加SwapPss (https://lore.kernel.org/patchwork/patch/570506/)以来,人们终于可以得到成比例的交换计数,这意味着如果一个进程交换了很多,然后它分叉,两个分叉的进程将分别报告交换50%。如果其中一个发生分叉,每个进程被计算为交换页面的33%,所以如果你把所有的交换使用量计算在一起,你得到的是真实的交换使用量,而不是数值乘以进程数。

简而言之:

(cd /proc; for pid in [0-9]*; do printf "%5s %6s %s\n" "$pid" "$(awk 'BEGIN{sum=0} /SwapPss:/{sum+=$2} END{print sum}' $pid/smaps)" "$(cat $pid/comm)"; done | sort -k2n,2 -k1n,1)

第一列是pid,第二列是KiB中的交换使用情况,其余一行是正在执行的命令。相同的交换计数按pid排序。

上面可能会发出这样的行

awk: cmd. line:1: fatal: cannot open file `15407/smaps' for reading (No such file or directory)

这仅仅意味着pid为15407的进程在/proc/的列表中看到它和读取进程smaps文件之间结束。如果这对您来说很重要,只需在末尾添加2>/dev/null即可。请注意,您可能还会丢失任何其他可能的诊断。

在现实世界的示例案例中,这改变了其他报告在一台服务器上运行的每个apache子服务器使用约40 MB交换空间的工具,而每个子服务器实际使用7-3630 KB交换空间。

我在网上改编了一个不同的脚本来写这句长单句:

 { date;for f in /proc/[0-9]*/status; do 
   awk '{k[$1]=$2} END { if (k["VmSwap:"]) print k["Pid:"],k["Name:"],k["VmSwap:"];}' $f 2>/dev/null; 
   done | sort -n ; }

然后我将其扔进cronjob并将输出重定向到日志文件。这里的信息与在smaps文件中积累Swap:条目相同,但如果你想确定,你可以使用:

{ date;for m in /proc/*/smaps;do 
  awk '/^Swap/ {s+=$2} END { if (s) print FILENAME,s }' $m 2>/dev/null;
  done | tr -dc ' [0-9]\n' |sort -k 1n; }

这个版本的输出有两列:pid,交换量。在上面的版本中,tr删除非数值组件。在这两种情况下,输出都是按照pid数值排序的。