Extraire toute la mémoire échangée d'un processus du swap


8

Comment procéder pour extraire rapidement toute la mémoire échangée d'un processus du swap sans écrire sur le disque?

Le contexte sur cette question est trivial, car la question systémique nécessitant la question est traitée par d'autres parties. Cependant, en ce moment, j'ai un problème où je dois fréquemment libérer de l'espace d'échange sur un nœud OpenVZ pendant que la charge et l'attente d'E / S sont extrêmement élevées.

Le swap est souvent principalement utilisé par une petite poignée de processus MySQL et clamd exécutés sur des conteneurs individuels. Le redémarrage de ces services libère l'échange et résout le problème sur le nœud, mais n'est pas souhaitable pour des raisons évidentes.

Je cherche un moyen de libérer rapidement le swap de ces processus alors que le nœud est surchargé et a besoin de quelque chose de plus rapide que ma méthode actuelle:

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

Ce vidage de mémoire force l'accès à tout le ram et fait donc le travail de le retirer du swap, mais je n'ai pas encore trouvé de moyen d'éviter son écriture dans un fichier. En outre, il semble que le processus serait plus rapide si je pouvais isoler les plages d'adresses actuellement échangées et simplement vider cette partie dans / dev / null, mais je n'ai pas encore trouvé de moyen de le faire.

C'est un énorme nœud, donc la méthode habituelle de swapoff / swapon prend beaucoup de temps, et encore une fois, la configuration du nœud n'est pas sous mon contrôle, donc la résolution de la cause première ne fait pas partie de cette question. Cependant, tout aperçu de la façon dont je pourrais libérer une partie importante du swap rapidement sans tuer / redémarrer quoi que ce soit serait apprécié.

Environnement: CentOS 6.7 / OpenVZ

Mise à jour pour tous ceux qui pourraient tomber dessus plus tard:

En utilisant l'entrée de Jlong, j'ai créé la fonction suivante:

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

C'est un peu lent, mais fait exactement ce qui était demandé ici sinon. Pourrait probablement améliorer la vitesse en ne trouvant que les plus grandes plages d'adresses dans le swap et en omettant les itérations pour les zones trivialement petites, mais la prémisse est solide.

Exemple de travail:

#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

Solution finale pour le dumping en vrac uniquement les gros morceaux de mémoire échangée:

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 

Je n'ai pas encore déterminé si cette méthode présente un risque pour la santé du processus ou du système, en particulier lorsqu'elle est bouclée sur plusieurs processus simultanément. Si quelqu'un a un aperçu de tout effet potentiel que cela pourrait avoir sur les processus ou le système, n'hésitez pas à commenter.


Je pense que la "solution finale" peut lancer un grand nombre d' gdbinstances parallèles si le processus à échanger contient beaucoup de fragments échangés. Le script lancera une gdbinstance parallèle pour chaque (gros) fragment échangé pour les 20 plus gros processus. Je pense qu'il faut au moins ajouter | tail -n20après le awkavant de passer les résultats en whileboucle pour limiter les processus parallèles maximum à 400.
Mikko Rantalainen

Réponses:


8

Vous pouvez obtenir le même résultat en utilisant la commande «dump memory» de GDB et en l'écrivant dans / dev / null.

Il vous suffit de trouver les régions dans / proc / $ PID / smaps qui doivent être non échangées. exemple 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

puis utilisez le mode --batch pour exécuter la commande gdb afin de pouvoir l'utiliser dans votre fonction:

[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

Bonne idée, je l'ai amélioré un peu plus tard, puis d'autres personnes l'ont encore amélioré au fil des ans, et il est devenu github.com/wiedemannc/deswappify-auto
kubanczyk
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.