Puxando toda a memória trocada de um processo para fora do swap

7

Como alguém iria rapidamente puxar toda a memória trocada de um processo para fora do swap sem gravar no disco?

O contexto sobre esta questão é trivial, já que a questão sistêmica que requer a questão está sendo tratada por outras partes. No entanto, neste momento, tenho um problema em que frequentemente tenho que liberar espaço de troca em um nó OpenVZ, enquanto o carregamento e a espera de IO são extremamente altos.

A troca é geralmente consumida principalmente por um pequeno número de processos MySQL e clamd sendo executados em contêineres individuais. Reiniciar esses serviços libera a troca e resolve o problema no nó, mas é indesejável por razões óbvias.

Estou procurando uma maneira de liberar rapidamente a troca desses processos enquanto o nó está sobrecarregado e precisa de algo mais rápido do que o meu método atual:

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

Este core dump força todo o RAM a ser acessado e, portanto, faz o trabalho de retirá-lo do swap, mas eu ainda não encontrei uma maneira de evitar que ele seja gravado. Além disso, parece que o processo seria mais rápido se eu pudesse isolar os intervalos de endereços que estão atualmente trocados e apenas descarregar essa parte para / dev / null, mas ainda não encontrei uma maneira de fazer isso.

Este é um nó enorme, então o método usual swapoff / swapon é proibitivamente demorado, e novamente, a configuração do nó não está sob meu controle, então consertar a causa raiz não faz parte desta questão. No entanto, qualquer insight sobre como eu poderia liberar uma porção significativa de swap rapidamente sem matar / reiniciar qualquer coisa seria apreciado.

Ambiente: CentOS 6.7 / OpenVZ

Atualização para qualquer um que possa se deparar com isso mais tarde:

Usando a entrada de Jlong, criei a seguinte função:

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;};

É um pouco lento, mas faz exatamente o que foi solicitado aqui. Provavelmente poderia melhorar a velocidade encontrando apenas os maiores intervalos de endereços em swap e omitindo as iterações para as áreas trivialmente pequenas, mas a premissa é sólida.

Exemplo de trabalho:

#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

Solução final para o despejo em massa apenas dos grandes blocos de memória trocados:

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 

Ainda estou para determinar se esse método representa algum risco para a integridade do processo ou do sistema, especialmente quando em loop em vários processos simultaneamente. Se alguém tiver conhecimento de qualquer efeito potencial que isso possa ter nos processos ou no sistema, sinta-se à vontade para comentar.

    
por Brandon DuPree 09.01.2016 / 03:31

1 resposta

6

Você pode obter o mesmo resultado usando o comando 'dump memory' do GDB e gravar em / dev / null.

Você só precisa encontrar as regiões em / proc / $ PID / smaps que precisam ser desativadas. exemplo de / 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

e depois use o modo --batch para executar o comando gdb para que você possa usá-lo em sua função:

[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
    
por 09.01.2016 / 22:35