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


当前回答

与@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

其他回答

还有两种变体:

因为top或htop不能安装在小型系统上,所以浏览/proc总是可能的。

即使在小型系统上,你也会发现一个壳…

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

这与loolotux脚本完全相同,但没有任何到grep, awk或ps的分支。这要快得多!

由于bash是性能最差的shell之一,我们做了一些工作,以确保该脚本在dash、busybox和其他一些环境下能够正常运行。然后,(感谢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 Chazelas的评论:

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

不要尝试在合理的系统上不带双引号的回显$PROGNAME,并在此之前准备好杀死当前shell !

还有一个perl版本

当这变成一个不那么简单的脚本,时间来编写一个专用的工具,使用更有效的语言。

#!/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

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

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

It is not possible to get the exact size of used swap space of a process. Top fakes this information by making SWAP = VIRT - RES, but that is not a good metric, because other stuff such as video memory counts on VIRT as well (for example: top says my X process is using 81M of swap, but it also reports my system as a whole is using only 2M of swap. Therefore, I will not add a similar Swap column to htop because I don't know a reliable way to get this information (actually, I don't think it's possible to get an exact number, because of shared pages).

您可以使用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"

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

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

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