從交換中拉出所有程序的交換記憶體
如何在不寫入磁碟的情況下快速將程序的所有交換記憶體從交換中拉出?
這個問題的背景是微不足道的,因為需要該問題的系統性問題正在由其他各方處理。但是,現在,我遇到了一個問題,即我經常不得不在 OpenVZ 節點上釋放交換空間,而負載和 IO 等待非常高。
交換通常主要由執行在單個容器上的少數 MySQL 和 clamd 程序使用。重新啟動這些服務可以釋放交換並解決節點上的問題,但由於顯而易見的原因是不可取的。
我正在尋找一種在節點超載並且需要比我目前方法更快的方法時從這些程序中快速釋放交換的方法:
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,那麼這個過程似乎會更快,但我還沒有找到一種方法來做到這一點。
這是一個巨大的節點,因此通常的 swapoff/swapon 方法非常耗時,而且節點的配置不在我的控制之下,因此修復根本原因不是這個問題的一部分。但是,任何有關如何在不殺死/重新啟動任何東西的情況下快速釋放大部分交換的見解都將不勝感激。
環境: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 的“轉儲記憶體”命令並將其寫入 /dev/null 來獲得相同的結果。
您只需要在 /proc/ 中找到區域 $ PID/smaps that need to be unswapped. example from /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