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.
gdb
instances parallèles si le processus à échanger contient beaucoup de fragments échangés. Le script lancera unegdb
instance parallèle pour chaque (gros) fragment échangé pour les 20 plus gros processus. Je pense qu'il faut au moins ajouter| tail -n20
après leawk
avant de passer les résultats enwhile
boucle pour limiter les processus parallèles maximum à 400.