Comment résoudre ce problème de mémoire avec élégance?

10

J'ai un ordinateur portable Linux (test Debian) standard, avec une partition de swap.

Je fais beaucoup d'expériences avec. Certains d'entre eux sont vraiment gourmands en mémoire et la façon dont Linux se comporte par défaut est un problème pour moi ... Donnons un exemple stupide:

  1. Asseyez-vous devant l'ordinateur portable
  2. Ouvrez un terminal
  3. Tapez python, puisa = [0]*100000000

Il y a maintenant de grandes chances que vous n'ayez pas assez de RAM pour gérer cette grande liste. Linux remplira la RAM, puis l'échange et, quelques minutes plus tard, le tueur OOM sera déclenché et tuera des services (presque) aléatoires et, espérons-le, si vous appuyez sur Ctrl + C au bon moment python, et si le terminal avait encore le focus, l'ordinateur redeviendra réactif.

Je voudrais imposer certaines limites de mémoire pour éviter cet échange indésirable et refuser à un processus le droit d'allouer plus de mémoire que je n'en ai (en RAM). Si la demande de mémoire est inférieure à une certaine limite ou demandée par root, il suffit de tuer le processus le plus gourmand en mémoire de tout utilisateur, sauf root.

ulimit -Sv [mem] J'entends dans le dos!

Ho Ho! "Utiliser cgroupsvia cgexec!" quelqu'un dit au premier rang!

Oui, vous avez raison: ce sont en effet de très bonnes solutions. Mais:

  • Ils ne s'appliquent pas à l'ensemble du système
  • Les limites sont fixées par processus
  • Les limites sont statiques, sans tenir compte de la quantité réelle de RAM libre (AFAIK)
  • Ici et , ils disent que ce n'est pas vraiment une bonne solution pour imposer des limites strictes.

Ce que j'aimerais, c'est que le noyau dise: "Vous appartenez à l'utilisateur foo (pas root), vous utilisez beaucoup de mémoire et nous allons manquer de mémoire. Désolé mec ... meurs maintenant!"

Ou: "Que diable faites-vous? Vous avez besoin de x Mo et il n'y a que y Mo disponibles. Oui, SWAP est vide, mais vous n'avez pas l'intention d'utiliser le SWAP pour faire votre sale boulot, n'est-ce pas? Non, je dit non! Pas de mémoire pour toi! Si tu insistes, tu vas mourir! "

Communauté
la source
2
Il existe déjà un algorithme décrit dans cet article qui aide le tueur OOM à choisir le processus correct. La modification /proc/sys/vm/overcommit_memoryaffecte le comportement du noyau en cas de mémoire faible.
jofel du
1
Oui, mais le overcommit_memoryfichier spécial utilise RAM + SWAP comme mémoire utilisable. Je vais encore échanger :)
1
Vous devez également expliquer en quoi il ne s'agit pas d'un doublon: unix.stackexchange.com/questions/34334/… qui vous contredit WRT cgroups et les utilisateurs individuels. PS. Si vous ne souhaitez pas échanger, désactivez l'échange .
goldilocks
1
Je veux échanger! Je veux l'hibernation, je veux que les octets inutilisés soient stockés! Mais je ne veux pas que les octets utilisés y soient stockés. À propos du lien, ulimitsc'est une mauvaise idée comme montré presque partout car c'est une limitation par processus ... Je fourche vous savez :) À propos cgroups, c'est certainement mieux mais il manque quelque chose de plus général: je parle de mon ordinateur portable mais je posséder un serveur de "calcul" que nous sommes trois à partager. Si j'applique de telles limites par utilisateur, je serai limité par le pire des cas, n'est-ce pas?
1
Les groupes de contrôle s'appliquent à tout processus que vous décidez - mettez tous les processus d'un utilisateur dans un groupe distinct et il devrait faire ce que vous voulez.
peterph

Réponses:

4

Quelqu'un a suggéré dans votre audition cgroups. Eh bien, essayez de chercher cette direction car elle peut vous fournir:

  • appliqué à un groupe de tâches que vous choisissez (donc pas à l'échelle du système mais ni par processus)
  • les limites sont fixées pour le groupe
  • les limites sont statiques
  • ils peuvent appliquer une limite stricte sur la mémoire et / ou la mémoire + swap

Quelque chose comme ça pourrait vous rapprocher de vos objectifs :

group limited {
  memory {
    memory.limit_in_bytes = 50M;
    memory.memsw.limit_in_bytes = 50M;
  }
}

Cela indique que les tâches sous ce groupe de contrôle peuvent utiliser au maximum 50 Mo de mémoire uniquement et 50 Mo de mémoire + échange, donc lorsque la mémoire est pleine, elle ne sera pas échangée, mais si la mémoire n'est pas pleine et que certaines données pourraient être mappées dans échange, cela pourrait être autorisé.

Voici un extrait de la documentation mémoire du cgroup :

En utilisant la limite memsw, vous pouvez éviter le MOO du système qui peut être causé par une pénurie de swap.

Huygens
la source
Toujours pas exactement ce à quoi je m'attendais. Mais la différence entre ce que j'attends et la réalité est souvent assez grande :) Dans ce cas, je voulais être sûr de ne rien manquer comme la overcommit_memoryvariable du noyau. Merci à tous.
0

Je rencontre souvent le même problème. Mon workflow général implique des calculs lourds dans MATLAB. Parfois, j'essaierai par inadvertance d'allouer une nouvelle variable qui dépasse la quantité de mémoire disponible. Le système se bloque et je dois généralement redémarrer la machine en dur pour me remettre au travail. : P

Dans mon cas, et il semble que dans le vôtre aussi, je n'étais pas tellement préoccupé par la limitation de la quantité de mémoire utilisée par MATLAB à une quantité statique - j'étais intéressé à ne pas avoir de machine gelée, et j'étais prêt à sacrifier mon processus MATLAB afin de préserver la réactivité du système.

Inspiré par une réponse à ce post , j'ai écrit le script suivant (je l'ai appelé watch_memory.sh):

#!/bin/bash

MONITOR=$(free | grep 'buffers/cache:')
MEM_USED=$(echo $MONITOR | awk '{ print $3 }')
MEM_FREE=$(echo $MONITOR | awk '{ print $4 }')

MEM_PERC=$(( 100*MEM_USED / (MEM_FREE+MEM_USED) ))

while :; do
    if [ "$MEM_PERC" -gt "95" ]
    then
        kill $1
        echo "$1 killed for using too much memory."
        exit
    fi
    sleep 1

    MONITOR=$(free | grep 'buffers/cache:')
    MEM_USED=$(echo $MONITOR | awk '{ print $3 }')
    MEM_FREE=$(echo $MONITOR | awk '{ print $4 }')
    MEM_PERC=$(( 100*MEM_USED / (MEM_FREE+MEM_USED) ))
done

Ce script vérifie chaque seconde le pourcentage de mémoire libre. Lorsque le système est épuisé, votre pid "bouc émissaire" (passé en argument au script) est tué.

Sans ajuster la priorité (gentillesse) du script, il a fallu environ 10 à 20 secondes pour que le bouc émissaire soit tué, mais cela a quand même fonctionné. L'exécution du script avec une priorité négative a entraîné un kill instantané après violation (11916 dans cet exemple est le pid que je veux tuer si je manque de mémoire):

sudo nice -n -5 bash watch_memory.sh 11916
Gordon Bean
la source