디스크에 쓰지 않고 프로세스의 모든 스왑 메모리를 스왑에서 빠르게 가져오는 방법은 무엇입니까?
이 문제를 요구하는 시스템적 문제는 다른 당사자가 해결하고 있기 때문에 이 문제의 맥락은 사소한 것입니다. 그러나 이제 문제가 생겼습니다. 로드 및 IO 대기 시간이 매우 높을 때 OpenVZ 노드에서 스왑 공간을 확보해야 하는 경우가 많습니다.
스왑은 일반적으로 단일 컨테이너에서 실행되는 소수의 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
이 코어 덤프는 모든 메모리에 대한 액세스를 강제하여 스왑에서 가져오는 작업을 수행하지만 파일에 쓰지 않는 방법을 찾지 못했습니다. 또한 현재 교환된 주소 범위를 분리하고 해당 부분을 /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
특히 동시에 여러 프로세스를 반복할 때 이 방법이 프로세스나 시스템의 상태에 위험을 초래하는지 아직 확인하지 못했습니다. 이것이 프로세스나 시스템에 미칠 수 있는 잠재적인 영향을 알고 있는 사람이 있으면 언제든지 의견을 제시해 주세요.
답변1
GDB의 "dump memory" 명령을 사용하고 이를 /dev/null에 쓰면 동일한 결과를 얻을 수 있습니다.
당신이 해야 할 일은 /proc/$PID/smaps에서 unswap이 필요한 영역을 찾는 것뿐입니다. /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