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


240

在Linux下,如何确定哪个进程在使用交换空间?


30
您接受的答案是错误的。考虑将其更改为lolotux的答案,这实际上是正确的。
jterrace 2012年

@jterrace是正确的,我的交换空间不如顶部SWAP列中的值总和那么大。
akostadinov 2012年

1
iotop是一个非常有用的命令,它将显示每个进程/线程的io实时统计信息和交换使用情况
sunil 2015年

@jterrace,请考虑说明其当前接受的答案是错误的。六年后,我们其余的人都不知道您是在指大卫·霍姆(David Holm)的答案(截至目前为止是目前接受的答案)还是其他答案。(好吧,我看到您还说过大卫·霍尔姆的回答是错误的,因为他的回答是评论……所以我想您可能是他的意思。)
唐·哈奇

Answers:


106

运行顶部,然后按OpEnter。现在,应按进程的交换使用情况对其进行排序。

这是更新,因为我的原始答案并未提供评论中所指出问题的确切答案。从htop常见问题解答

无法获得进程已使用交换空间的确切大小。Top通过使SWAP = VIRT-RES来伪造此信息,但这并不是一个很好的指标,因为其他内容(例如视频内存)也在VIRT上计数(例如:top说我的X进程正在使用81M交换,但是报告我的系统总体上仅使用2M交换。因此,我不会在htop中添加类似的Swap列,因为我不知道获取此信息的可靠方法(实际上,我认为不可能获得确切的数字,因为共享页面)。


137
从文档中,最上面的SWAP列似乎只是显示如果整个过程都被交换掉了,那么有必要进行多少交换,而不是目前实际交换了多少个过程。经过短时间的搜索,我无法判断出目前每个进程中有多少交换掉了。因此,htop的作者拒绝放入这样的列(我确实看到了CNSWAP和NSWAP列,但它们似乎在我的机器上没有任何作用):htop.sourceforge.net/index.php?page=faq
yukondude

6
@yukondude是正确的,顶部的SWAP列只是VIRT-RES,在这种情况下,此信息是无用的。对于映射的视频RAM的共享内存等没有补偿。同样,该进程可能还没有引用所有内存。在这种情况下,操作系统无需将完整的二进制文件从磁盘读取到内存中,因此RES的值不包括内存的这一部分。
巴特2012年

如果可以的话,我会更多地投票。这节省了我的培根!
atrain

值得庆幸的是,这是@jterrace的注释:)(尽管您必须读它们:S ...不确定是指什么火车,我希望它是yukondude)
AJP 2014年

11
关于评论不再起作用:似乎top的较新版本不再将'O'设置为选择排序字段的关键。什么时候使用?键,您可以看到实际的程序名称和版本,procps-ng是最新版本。这是Debian,Fedora和openSUSE的一个分支:gitorious.org/procps。如果仍要在SWAP列上进行排序:使用'f'键查看字段,使用箭头键转到SWAP并使用's'设置排序,然后使用'q'。
Pieter VN 2015年

294

我发现的最佳脚本在此页面上: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"


4
虽然好笑,但我却得到了Overall swap used: 260672 KB免费节目的738932使用...
Doncho Gunchev 2012年

23
相同的输出速度快十倍:for file in /proc/*/status ; do awk '/Tgid|VmSwap|Name/{printf $2 " " $3}END{ print ""}' $file; done | grep kB | sort -k 3 -n对于Debian / RH 6x +,Arch,Ubuntu(RH 5x具有VmSize)()。像@dgunchev一样,它提供的总交换量比少得多free。@Tensibai在Arch上不起作用;您的awk可能缺少某些内容。
tuk0z

1
请看一下我的无分支版本脚本!
F. Hauri

3
作者撰写了有关如何使用它的后续文章topNorthernmost.org/blog/swap-usage-5-years-later
Jack Valmadre 17-02-22

53

这是脚本的另一种变体,但其目的是提供更具可读性的输出(您需要以root用户身份运行此命令以获得准确的结果):

#!/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}/";

2
非常好的脚本。它提供的信息与lolotux的信息相同,但可读性更好。
菲利普·温德勒

出色的输出。谢谢。
布莱恩·克莱恩

2
我唯一更改的是使用args而不是commps命令中使用,因为我有很多名称相同但参数不同的进程(一堆python gunicorn进程)。即:ps -p $PID -o args --no-headers
mgalgs 2014年

1
旁注grep VmSwap $DIR/status 2>/dev/null | awk '{ print $2 }'可以简化为awk ' /VmSwap/ { print $2 }'
Tensibai

12

我确实注意到该线程已经很旧了,但是,就像我刚才那样,如果您偶然发现了它,那么另一个答案是:使用smem。

这是一个告诉您如何安装和使用方法的链接:

http://www.cyberciti.biz/faq/linux-which-process-is-using-swap/


这个好。这是该文章的改编版本,显示了按交换使用排序并添加PID的proc:$用于/ proc / * / status中的文件;awk'/ ^ Pid | VmSwap | Name / {printf $ 2“” $ 3} END {print“”}'$ file; 完成| 排序-k 3 -n -r | 少
Stan Brajewski '17

您应该使用glob / proc / [1-9] * / status排除几个特殊的/ proc条目,并且可以将args排序为-rnk3
dland

10

您是否要查找交换掉最多页面的进程或导致交换掉大部分页面的进程,还不是很清楚。

对于第一个,您可以top通过交换运行和排序(按“ Op”),对于第二个,您可以运行vmstat并寻找“ so”的非零条目。


6

top命令还包含一个字段,以显示进程的页面错误数。具有最大页面错误的过程将是交换最多的过程。对于长时间运行的守护程序,可能是它们在开始时会引起大量的页面错误,而以后不会增加。因此,我们需要观察页面错误是否正在增加。


6

另一个避免在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对输出进行排序。

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


1
请注意,bash以排序方式(词汇而非数字)扩展目录。随机顺序取决于如何awk存储其数组(哈希表)以及如何for p in pname检索它们。
Stephane Chazelas

@StephaneChazelas好吧,这还不是词汇,这是一个ascii代码排序(/proc/1/status随后,/proc/1992/status并且/在9个ascii代码之上都有一个ascii代码。这也给人一种“随机顺序”的感觉。我同意awk哈希表,我参加了一个快捷方式在这里可以随意编辑答案,以保持在归属编辑历史。
Tensibai

1
/proc/1/status不会/proc/1992/status在C语言环境中按字节值排序的顺序。它在您的语言环境(或在en_GB.UTF-8GNU系统上的我的语言环境)中执行,因为/在排序规则算法的第一个实例中将忽略该属性(并s在之后排序9)。printf '/proc/%s/status\n' 1 1992 | LC_ALL=en_GB.UTF-8 sort与比较printf '/proc/%s/status\n' 1 1992 | LC_ALL=C sort。在以外的语言环境中C,排序顺序通常基于字节值。
Stephane Chazelas

@StephaneChazelas好点,虽然没有关于语言环境的信息。再次随意编辑以增加精度,以便功劳归您所有(至少在历史记录编辑中如此)。
Tensibai '16

2
做完了 这个答案比这里投票最多的答案要好得多。它确实值得更多投票。在“ 为什么使用shell循环处理文本被认为是不良做法”中讨论了该问题和其他答案这就是把我带到这里的原因。
Stephane Chazelas

6

还有两个变体:

因为在小型系统上可能安装tophtop可能未安装,所以/proc始终可以浏览。

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

一个 变体!(不仅限于bash)

这正是比相同lolotux剧本,但没有任何叉grepawkps。这要快得多!

并作为 是最穷的人之一 关于性能,做了一些工作以确保该脚本在 和其他一些。然后,(感谢StéphaneChazelas)再次变得更快了!

#!/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
rifs=`printf ': \t'`
for FILE in /proc/[0-9]*/status ;do
    SUM=0
    while IFS="$rifs" read FIELD VALUE ;do
        case $FIELD in
            Pid )    PID=$VALUE      ;;
            Name )   PROGNAME="$VALUE" ;;
            VmSwap ) SUM=$((SUM=${VALUE% *}))  ;;
        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éphaneChazelas的评论

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

假定进程名称不包含空格,制表符:,,反斜杠,通配符或控制字符。
Stephane Chazelas

@StephaneChazelas谢谢!我已经添加了[1-9]之前*的只算编号路径(没有self,也没有thread-self
F. Hauri

1
语法是已知的,但是进程名称不是。至少引用您的变量。(无论如何,您的脚本比loloxux的错误要少得多)。
Stephane Chazelas

1
Linux上的进程名称可以包含任何字节值,但0除外,但长度限制为15个字节。中的Name条目对/proc/*/status其中一些字节值进行编码。例如尝试perl -ne 'BEGIN{$0="\n\t\\"} print if /^Name/' /proc/self/status。因为它太短了,所以perl -ne 'BEGIN{$0="/*/*/../../*/*"} print if /^Name/' /proc/self/status当您忘记引用变量时,诸如此类的操作所造成的损害是有限的。
Stephane Chazelas '16

1
这(我只是尝试至少perl的版本)是巨大的比其他的答案更快。
大卫·加德纳

5

我在网上修改了一个不同的脚本来适应这一长线:

 { 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并将输出重定向到日志文件。此处的信息与累积Swap:smaps文件中的条目相同,但是如果您想确定的话,可以使用:

{ 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进行数字排序。


2
这很好,但是第一个按pid升序排序(sort -n)。更好的用法是按交换顺序以降序排序(使用最多的是在列表前面)。要获得它,请将“ sort -n”更改为“ sort -n -k 3 -r”
Stan Brajewski

3

在MacOSX上,您也要运行top命令,但需要先键入“ o”,然后键入“ vsize”,然后按Enter。


2

我想您可以通过运行top并使用大量内存来查找活动进程来很好地猜测。以编程方式执行此操作比较困难-只需看看关于Linux OOM杀手启发法的无休止的辩论即可。

交换是在活动中拥有更多内存的功能使用的大于已安装的,因此通常很难将其归咎于单个进程。如果这是一个持续存在的问题,最好的解决方案是安装更多内存或进行其他系统更改。



1

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

另外,使用诸如htop之类的好工具来查看哪些进程正在使用大量内存以及正在使用多少交换空间。


1

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


1

这是一个输出与@loolotux脚本相同的版本,但是速度更快(虽然可读性较低)。该循环在我的计算机上花费大约10秒,我的版本花费0.019 s,这对我来说很重要,因为我想将其放入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

1

自从2015年添加内核补丁SwapPsshttps://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中的swap用法,其余的行是正在执行的命令。相同的交换计数按pid排序。

上面可能会发出诸如

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

这只是意味着进程号为15407的进程在查看列表/proc/和读取进程smaps文件之间结束。如果这对您很重要,只需添加2>/dev/null到最后。请注意,您也可能会丢失任何其他可能的诊断信息。

在实际示例中,这会将其他工具报告为在一台服务器上运行的每个apache子级〜40 MB交换使用更改为每个子级实际使用的7-3630 KB的实际使用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.