如何找出Linux中哪些进程正在使用交换空间?

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

378735 次浏览

我认为你可以通过运行top并寻找使用大量内存的活动进程来得到一个很好的猜测。通过编程实现这一点比较困难——只要看看关于Linux OOM杀手启发式的无休止的争论就知道了。

交换是在活跃的中使用的内存比安装的内存多的函数,所以通常很难将其归咎于单个进程。如果这是一个持续的问题,最好的解决方案是安装更多的内存,或进行其他系统更改。

我不知道如何确切地找到哪个进程正在使用交换空间的任何直接答案,但是,这个链接可能是有帮助的。另一个好例子是在这里

另外,使用像htop这样的好工具来查看哪些进程使用了大量内存,以及总体上使用了多少交换空间。

运行,然后按Op输入。现在进程应该根据它们的交换使用情况进行排序。

这是一个更新,因为我原来的答案没有提供一个确切的答案,在评论中指出的问题。从htop常见问题解答:

不可能得到a的已使用交换空间的确切大小 的过程。Top通过制作SWAP = VIRT - RES来伪造这一信息,但是 这不是一个很好的指标,因为其他东西,如显存 也依赖VIRT(例如:top表示我的X进程正在使用 81M的交换,但它也报告我的系统作为一个整体只使用2M 的交换。因此,我不会在htop中添加类似的Swap列 因为我不知道一个可靠的方法来获得这些信息(实际上, 我认为不可能得到一个确切的数字,因为共享 页)。< / p >

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

对于前者,你可以运行top并按swap排序(按'Op'),对于后者,你可以运行vmstat并寻找'so'的非零项。

我找到的最好的脚本在这一页:http://northernmost.org/blog/find-out-what-is-using-your-swap/

这里是脚本的一个变体,不需要根:

#!/bin/bash
# Get current swap usage for all running processes
# Erik Ljungstrom 27/05/2011
# Modified by Mikko Rantalainen 2012-08-09
# Pipe the output to "sort -nk3" to get sorted output
# Modified by Marc Methot 2014-09-18
# removed the need for sudo


SUM=0
OVERALL=0
for DIR in `find /proc/ -maxdepth 1 -type d -regex "^/proc/[0-9]+"`
do
PID=`echo $DIR | cut -d / -f 3`
PROGNAME=`ps -p $PID -o comm --no-headers`
for SWAP in `grep VmSwap $DIR/status 2>/dev/null | awk '{ print $2 }'`
do
let SUM=$SUM+$SWAP
done
if (( $SUM > 0 )); then
echo "PID=$PID swapped $SUM KB ($PROGNAME)"
fi
let OVERALL=$OVERALL+$SUM
SUM=0
done
echo "Overall swap used: $OVERALL KB"

在MacOSX上,你也可以运行top命令,但需要输入“o”,然后输入“vsize”,然后输入ENTER。

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

这是脚本的另一个变体,但意味着提供更可读的输出(你需要以根用户身份运行这个脚本才能得到准确的结果):

#!/bin/bash


# find-out-what-is-using-your-swap.sh
# -- Get current swap usage for all running processes
# --
# -- rev.0.3, 2012-09-03, Jan Smid          - alignment and intendation, sorting
# -- rev.0.2, 2012-08-09, Mikko Rantalainen - pipe the output to "sort -nk3" to get sorted output
# -- rev.0.1, 2011-05-27, Erik Ljungstrom   - initial version




SCRIPT_NAME=`basename $0`;
SORT="kb";                 # {pid|kB|name} as first parameter, [default: kb]
[ "$1" != "" ] && { SORT="$1"; }


[ ! -x `which mktemp` ] && { echo "ERROR: mktemp is not available!"; exit; }
MKTEMP=`which mktemp`;
TMP=`${MKTEMP} -d`;
[ ! -d "${TMP}" ] && { echo "ERROR: unable to create temp dir!"; exit; }


>${TMP}/${SCRIPT_NAME}.pid;
>${TMP}/${SCRIPT_NAME}.kb;
>${TMP}/${SCRIPT_NAME}.name;


SUM=0;
OVERALL=0;
echo "${OVERALL}" > ${TMP}/${SCRIPT_NAME}.overal;


for DIR in `find /proc/ -maxdepth 1 -type d -regex "^/proc/[0-9]+"`;
do
PID=`echo $DIR | cut -d / -f 3`
PROGNAME=`ps -p $PID -o comm --no-headers`


for SWAP in `grep Swap $DIR/smaps 2>/dev/null| awk '{ print $2 }'`
do
let SUM=$SUM+$SWAP
done


if (( $SUM > 0 ));
then
echo -n ".";
echo -e "${PID}\t${SUM}\t${PROGNAME}" >> ${TMP}/${SCRIPT_NAME}.pid;
echo -e "${SUM}\t${PID}\t${PROGNAME}" >> ${TMP}/${SCRIPT_NAME}.kb;
echo -e "${PROGNAME}\t${SUM}\t${PID}" >> ${TMP}/${SCRIPT_NAME}.name;
fi
let OVERALL=$OVERALL+$SUM
SUM=0
done
echo "${OVERALL}" > ${TMP}/${SCRIPT_NAME}.overal;
echo;
echo "Overall swap used: ${OVERALL} kB";
echo "========================================";
case "${SORT}" in
name )
echo -e "name\tkB\tpid";
echo "========================================";
cat ${TMP}/${SCRIPT_NAME}.name|sort -r;
;;


kb )
echo -e "kB\tpid\tname";
echo "========================================";
cat ${TMP}/${SCRIPT_NAME}.kb|sort -rh;
;;


pid | * )
echo -e "pid\tkB\tname";
echo "========================================";
cat ${TMP}/${SCRIPT_NAME}.pid|sort -rh;
;;
esac
rm -fR "${TMP}/";

使用< >强smem < / >强

smem -s swap -r

下面是一个链接,它告诉你如何安装它以及如何使用它:http://www.cyberciti.biz/faq/linux-which-process-is-using-swap/

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

另一种在shell中避免循环的脚本变体:

#!/bin/bash
grep VmSwap /proc/[0-9]*/status | awk -F':' -v sort="$1" '
{
split($1,pid,"/") # Split first field on /
split($3,swp," ") # Split third field on space
cmdlinefile = "/proc/"pid[3]"/cmdline" # Build the cmdline filepath
getline pname[pid[3]] < cmdlinefile # Get the command line from pid
swap[pid[3]] = sprintf("%6i %s",swp[1],swp[2]) # Store the swap used (with unit to avoid rebuilding at print)
sum+=swp[1] # Sum the swap
}
END {
OFS="\t" # Change the output separator to tabulation
print "Pid","Swap used","Command line" # Print header
if(sort) {
getline max_pid < "/proc/sys/kernel/pid_max"
for(p=1;p<=max_pid;p++) {
if(p in pname) print p,swap[p],pname[p] # print the values
}
} else {
for(p in pname) { # Loop over all pids found
print p,swap[p],pname[p] # print the values
}
}
print "Total swap used:",sum # print the sum
}'

标准用法是script.sh以随机顺序获取每个程序的使用情况(直到awk如何存储其哈希值)或script.sh 1以pid对输出进行排序。

我希望我已经对代码进行了足够的注释,以说明它的功能。

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

 { 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数值排序的。

还有两种变体:

因为tophtop不能安装在小型系统上,所以总是可以浏览/proc

即使在小型系统上,你也会发现shell

一个的变体!(不仅仅是bash)

这与lolotux脚本完全相同,但没有任何到grepawkps的分支。这是快多了!

由于是性能最差的之一,我们做了一些工作来确保这个脚本在和其他一些条件下能够正常运行。然后,(感谢Stéphane Chazelas,)再次变得更快!

#!/bin/sh
# Get current swap usage for all running processes
# Felix Hauri 2016-08-05
# Rewritted without fork. Inspired by first stuff from
# Erik Ljungstrom 27/05/2011
# Modified by Mikko Rantalainen 2012-08-09
# Pipe the output to "sort -nk3" to get sorted output
# Modified by Marc Methot 2014-09-18
# removed the need for sudo


OVERALL=0
for FILE in /proc/[0-9]*/status ;do
SUM=0
while read FIELD VALUE;do
case $FIELD in
Pid )    PID=$VALUE         ;;
Name )   PROGNAME="$VALUE"  ;;
VmSwap ) SUM=${VALUE%% *} ; break ;;
esac
done <$FILE
[ $SUM -gt 0 ] &&
printf "PID: %9d  swapped: %11d KB (%s)\n" $PID $SUM "$PROGNAME"
OVERALL=$((OVERALL+SUM))
done
printf "Total swapped memory: %14u KB\n" $OVERALL

别忘了双引号"$PROGNAME" !看到Stéphane查泽拉斯的评论:

read FIELD PROGNAME < <(
perl -ne 'BEGIN{$0="/*/*/../../*/*"} print if /^Name/' /proc/self/status
)
echo $FIELD "$PROGNAME"

不要在敏感系统上尝试没有双引号的echo $PROGNAME,并在此之前准备好杀死当前shell !

版本

由于这变成了没那么简单脚本,是时候通过使用更有效的语言来编写一个专用的工具了。

#!/usr/bin/perl -w


use strict;
use Getopt::Std;
my ($tot,$mtot)=(0,0);
my %procs;


my %opts;
getopt('', \%opts);


sub sortres {
return $a <=> $b                                          if $opts{'p'};
return $procs{$a}->{'cmd'} cmp $procs{$b}->{'cmd'}        if $opts{'c'};
return $procs{$a}->{'mswap'} <=> $procs{$b}->{'mswap'}    if $opts{'m'};
return $procs{$a}->{'swap'} <=> $procs{$b}->{'swap'};
};


opendir my $dh,"/proc";


for my $pid (grep {/^\d+$/} readdir $dh) {
if (open my $fh,"</proc/$pid/status") {
my ($sum,$nam)=(0,"");
while (<$fh>) {
$sum+=$1 if /^VmSwap:\s+(\d+)\s/;
$nam=$1 if /^Name:\s+(\S+)/;
}
if ($sum) {
$tot+=$sum;
$procs{$pid}->{'swap'}=$sum;
$procs{$pid}->{'cmd'}=$nam;
close $fh;
if (open my $fh,"</proc/$pid/smaps") {
$sum=0;
while (<$fh>) {
$sum+=$1 if /^Swap:\s+(\d+)\s/;
};
};
$mtot+=$sum;
$procs{$pid}->{'mswap'}=$sum;
} else { close $fh; };
};
};
map {
printf "PID: %9d  swapped: %11d (%11d) KB (%s)\n",
$_, $procs{$_}->{'swap'}, $procs{$_}->{'mswap'}, $procs{$_}->{'cmd'};
} sort sortres keys %procs;
printf "Total swapped memory: %14u (%11u) KB\n", $tot,$mtot;

能不能带着一个跑

-c  sort by command name
-p  sort by pid
-m  sort by swap values
by default, output is sorted by status's vmsize

给出使用交换的进程的总数和百分比

smem -t -p

enter image description here

来源:https://www.cyberciti.biz/faq/linux-which-process-is-using-swap/

这里有一个版本,输出与@loolotux脚本相同,但更快(虽然可读性较差)。 在我的机器上,这个循环大约需要10秒,我的版本需要0.019秒,这对我来说很重要,因为我想把它变成一个cgi页面
    join -t / -1 3 -2 3 \
<(grep VmSwap /proc/*/status  |egrep -v '/proc/self|thread-self' | sort -k3,3 --field-separator=/ ) \
<(grep -H  '' --binary-files=text /proc/*/cmdline |tr '\0' ' '|cut -c 1-200|egrep -v '/proc/self|/thread-self'|sort -k3,3 --field-separator=/ ) \
| cut -d/ -f1,4,7- \
| sed 's/status//; s/cmdline//' \
| sort -h -k3,3 --field-separator=:\
| tee >(awk -F: '{s+=$3} END {printf "\nTotal Swap Usage = %.0f kB\n",s}') /dev/null

自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交换空间。

你可以使用Procpath(作者在这里),来简化从/proc/$PID/status解析VmSwap

$ 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

你也可以像这样绘制感兴趣的进程随时间变化的VmSwap。在这里,我正在记录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"

CPU使用率,% vs Swap, kB

@lolotux相同的答案,但有排序输出:

printf 'Computing swap usage...\n';
swap_usages="$(
SUM=0
OVERALL=0


for DIR in `find /proc/ -maxdepth 1 -type d -regex "^/proc/[0-9]+"`
do
PID="$(printf '%s' "$DIR" | cut -d / -f 3)"
PROGNAME=`ps -p $PID -o comm --no-headers`
for SWAP in `grep VmSwap $DIR/status 2>/dev/null | awk '{ print $2 }'`
do
let SUM=$SUM+$SWAP
done
if (( $SUM > 0 )); then
printf "$SUM KB ($PROGNAME) swapped PID=$PID\\n"
fi
let OVERALL=$OVERALL+$SUM
SUM=0
break
done
printf '9999999999 Overall swap used: %s KB\n' "$OVERALL"
)"


printf '%s' "$swap_usages" | sort -nk1

示例输出:

Computing swap usage...
2064 KB (systemd) swapped PID=1
59620 KB (xfdesktop) swapped PID=21405
64484 KB (nemo) swapped PID=763627
66740 KB (teamviewerd) swapped PID=1618
68244 KB (flameshot) swapped PID=84209
763136 KB (plugin_host) swapped PID=1881345
1412480 KB (java) swapped PID=43402
3864548 KB (sublime_text) swapped PID=1881327
9999999999 Overall swap used: 2064 KB

这是我的一句话:

cat /proc/*/status | grep -E 'VmSwap:|Name:' | grep VmSwap -B1 | cut -d':' -f2 | grep -v '\-\-' | grep -o -E '[a-zA-Z0-9]+.*$' | cut -d' ' -f1 | xargs -n2 echo | sort -k2 -n

这一行的步骤是:

  • 获取所有进程的/proc/process/status中的所有数据
  • 为每个字段选择vmswwap和Name
  • 删除没有vmswwap字段的进程
  • 删除字段名(vmwap:和Name:)
  • 删除上一步添加的行
  • 删除行开头的空格
  • 删除每个进程名的第二部分和"kB"之后的交换使用编号
  • 取名称和编号(进程名和交换使用),并将它们依次放在一行中
  • 根据交换使用情况对行进行排序

我使用这个,如果你只有/proc,没有其他有用的。只需要设置nr为你想看到的顶部交换器的数量,它会告诉你进程名,交换占用空间(MB),它是ps -ef的完整进程线:

nr=10;for pid in $(for file in /proc/ __abc0 //g';ps -ef|awk "$2==$pid {print}"