将所有进程的已交换内存拉出交换


8

在不写入磁盘的情况下,如何快速将所有进程的已交换内存从交换中拉出?

关于这个问题的背景很琐碎,因为系统性的问题是其他各方正在处理,这个问题是必须的。但是,现在,我遇到一个问题,即当负载和IO等待非常高时,我经常不得不释放OpenVZ节点上的交换空间。

交换通常主要由少数几个在单个容器上运行的MySQL和蛤processes进程消耗。重新启动这些服务可以释放交换并解决节点上的问题,但是出于明显的原因,这是不希望的。

我正在寻找一种方法,可以在节点过载时快速从那些进程中释放掉交换,并且需要比当前方法更快的操作:

unswap(){ [[ $1 && $(ls /proc/$1/maps) ]]  && ((gcore -o /tmp/deleteme $1 &>/dev/null; rm -fv /tmp/deleteme.$1)&) 2>/dev/null  || echo "must provide valid pid";};unswap

此核心转储会强制访问所有ram,从而完成了将其退出交换的工作,但是我尚未找到避免将其写入文件的方法。另外,如果我可以隔离当前交换的地址范围并将该部分转储到/ dev / null中,则看起来该过程会更快,但是我还没有找到一种方法来做到这一点。

这是一个巨大的节点,因此通常的交换/交换方法非常耗时,并且再次,该节点的配置不受我的控制,因此解决根本原因并不属于这个问题。但是,如果能洞悉我如何迅速释放掉大部分交换而又不中断/重启任何事情,将不胜感激。

环境:CentOS 6.7 / OpenVZ

对于以后可能会迷失方向的任何人的更新:

使用Jlong的输入,我创建了以下函数:

unswap(){ (awk -F'[ \t-]+' '/^[a-f0-9]*-[a-f0-9]* /{recent="0x"$1" 0x"$2}/Swap:/&&$2>0{print recent}' /proc/$1/smaps | while read astart aend; do gdb --batch --pid $1 -ex "dump memory /dev/null $astart $aend" &>/dev/null; done&)2>/dev/null;};

这有点慢,但是完全按照这里的要求执行。通过仅在交换中找到最大的地址范围,并为很小的区域省略迭代,可能可以提高速度,但是前提是合理的。

工作示例:

#Find the process with the highest swap use
[~]# grep VmSwap /proc/*/status 2>/dev/null | sort -nk2 | tail -n1 | while read line; do fp=$(echo $line | cut -d: -f1); echo $line" "$(stat --format="%U" $fp)" "$(grep -oP "(?<=NameS).*" $fp); done | column -t
/proc/6225/status:VmSwap:   230700  kB  root  mysqld

#Dump the swapped address ranges and observe the swap use of the proc over time
[~]# unswap(){ (awk -F'[ t-]+' '/^[a-f0-9]*-[a-f0-9]* /{recent="0x"$1" 0x"$2}/Swap:/&&$2>0{print recent}' /proc/$1/smaps | while read astart aend; do gdb --batch --pid $1 -ex "dump memory /dev/null $astart $aend" &>/dev/null; done&)2>/dev/null;}; unswap 6225; while true; do grep VmSwap /proc/6225/status; sleep 1; done
VmSwap:   230700 kB
VmSwap:   230700 kB
VmSwap:   230676 kB
VmSwap:   229824 kB
VmSwap:   227564 kB
... 36 lines omitted for brevity ... 
VmSwap:     9564 kB
VmSwap:     3212 kB
VmSwap:     1876 kB
VmSwap:       44 kB
VmSwap:        0 kB

批量转储仅大块交换内存的最终解决方案:

unswap(){ (awk -F'[ \t-]+' '/^[a-f0-9]*-[a-f0-9]* /{recent="0x"$1" 0x"$2}/Swap:/&&$2>1000{print recent}' /proc/$1/smaps | while read astart aend; do gdb --batch --pid $1 -ex "dump memory /dev/null $astart $aend" &>/dev/null; done&)2>/dev/null;}; grep VmSwap /proc/*/status 2>/dev/null | sort -nk2 | tail -n20 | cut -d/ -f3 | while read line; do unswap $line; done;echo "Dumps Free(m)"; rcount=10; while [[ $rcount -gt 0 ]]; do rcount=$(ps fauxww | grep "dump memory" | grep -v grep | wc -l); echo "$rcount        $(free -m | awk '/Swap/{print $4}')"; sleep 1; done 

我尚未确定此方法是否对流程或系统的运行状况造成任何风险,尤其是在同时循环多个流程时。如果有人了解这可能会对流程或系统产生任何潜在影响,请随时发表评论。


我认为,gdb如果要交换的进程有很多交换的片段,那么“最终解决方案”可能会启动大量的并行实例。该脚本将为gdb前20个最大进程的每个交换(大)片段启动并行实例。我认为一个人至少应该添加| tail -n20后的awk传递结果之前while循环来限制最大并联机器人流程,以400
米克Rantalainen

Answers:


8

您可以通过使用GDB的“转储内存”命令并将其写入/ dev / null来获得相同的结果。

您只需要在/ proc / $ PID / smaps中找到需要取消交换的区域。/ proc / $ PID / smaps中的示例:

02205000-05222000 rw-p 00000000 00:00 0 
Size:              49268 kB
Rss:               15792 kB
Pss:                9854 kB
Shared_Clean:          0 kB
Shared_Dirty:      11876 kB
Private_Clean:         0 kB
Private_Dirty:      3916 kB
Referenced:          564 kB
Anonymous:         15792 kB
AnonHugePages:         0 kB
Swap:              33276 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB

然后使用--batch模式执行gdb命令,以便可以在函数中使用它:

[root@nunya ~]# swapon -s ; gdb --batch --pid 33795 -ex "dump memory /dev/null 0x02205000 0x05222000" ;swapon -s
Filename                Type        Size    Used    Priority
/dev/sda2                               partition   7811068 7808096 -1

[Thread debugging using libthread_db enabled]

Filename                Type        Size    Used    Priority
/dev/sda2                               partition   7811068 7796012 -1

好主意,我稍后对其进行了改进,然后几年来其他人对其进行了进一步改进,它成为了github.com/wiedemannc/deswappify-auto
kubanczyk
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.