[gull] Taille ideal de la partition swap?

Marc SCHAEFER schaefer at alphanet.ch
Mon Oct 16 19:19:57 CEST 2006


On Sun, Oct 15, 2006 at 03:24:18PM +0200, Yoan BLANC wrote:
> Je me souviens d'un truc disant, deux fois la taille de la RAM, mais 
> c'est peut-être surévalué au vu des éléments trouvés ça et là ou c'est 
> plus proche de la taille de la RAM voire inférieur.

Initialement, Linux (le kernel) n'avait pas de demandes de ce type.
C'était les *BSD qui proposaient cette règle de calcul.

Si je me souviens bien, cependant, tout a changé avec le kernel 2.4. Dès
ce moment-là, le sous-système de gestion de la mémoire pouvait non
seulement ralentir le système en cas de swap insuffisant (probablement
en jettant des pages READ/EXECUTE, comme du `text' -- le code des
programmes trop vite, de manière à augmenter le buffer cache), mais
aussi provoquer la mort d'un processus qui a `trop' alloué de mémoire --
j'élabore sur cela dans un moment.

En 2.6, j'ai pour le moment totalement renoncé à comprendre ce qui se
produit, je m'y consacrerai quand j'aurai besoin de cette version,
jusqu'ici le 2.4 fonctionne relativement bien pour moi.

L'allocation de mémoire sur les systèmes Linux `actuels' est assez simple:
on dit `ok, c'est bon' (appel système brk(2), de mémoire, je viens de
remarquer que je n'ai pas les pages man 2 sur mon Ubuntu), tant que ce
qui est demandé peut tenir dans l'espace mémoire privé du processus (de
2 à 3 GB suivant le mapping mémoire choisi: s'il y a plus de 1 GB de
mémoire dans la machine, environ, disons 2 GB par processus sur x86 pour
simplifier).

Cela ne signifie pas du tout qu'il y a assez de mémoire logique (RAM +
swap), voire physique (RAM) pour allouer effectivement les données.

Un petit exemple:

   #include <stdlib.h>
   #include <stdio.h>
   
   int main(int argc, char **argv) {
      unsigned long int bytes_allocated = 0;
      unsigned char *addr;
   
      while ((addr = malloc(4096))) {
         bytes_allocated += 4096;
   #ifdef TOUCH
         addr[0] = 1; /* touch */
   #endif /* TOUCH */
      }
   
      printf("allocated: %lu bytes.\n", bytes_allocated);
   
      return 0;
   }

En 2.4, ce programme devrait, si TOUCH n'est pas défini, allouer jusqu'à
2 ou 3 GB de mémoire puis finir par une erreur (ENOMEM), par défaut:
l'address space du processus a été totalement mangé.

   Le programme ne fonctionnera peut-être pas comme prévu, tout dépend
   comment la libc gère l'allocation, je n'ai pas essayé récemment!
   A vos risques et périls.

Si l'on définit TOUCH, dans ce cas, ce programme va manger toute la
mémoire libre, attaquer les buffers, déplacer en swap des données, etc,
puis il finira probablement par coincer le système, se faire tuer, ou
tuer un gros processus autre (j'y reviens).

Le problème est que l'allocation *réelle* de la page qui était de trop
(la dernière bouchée, le dernier petit chocolat alors que les dents
du fond baignent déjà) ne fonctionne pas.

Cette allocation réelle correspond en fait au premier accès en écriture
à la page.

Mais le processus qui a voulu allouer une page n'est *pas forcément*
celui qui a demandé beaucoup de mémoire.  De plus, il est trop tard pour
`retourner une erreur' ou autre: il faut envoyer un signal POSIX (cf kill
-l) pour indiquer cette condition, qui peut être éventuellement traitée:
par exemple OpenOffice semble supporter cette condition parfois
correctement. Sinon, le processus est terminé.

Les premiers `OOM-killer' (Out Of Memory Killer) de Linux tuaient le processus
qui demandait la page. Souvent, c'était le serveur X, et pas le méchant
processus `mange-tout'.  D'autres stratégies ont été développées, que
l'on peut activer et désactiver par compilation du kernel. La stratégie
pré-compilée par votre distribution fait en général bons offices.

Mais pourquoi, finalement, séparer l'allocation de mémoire de
l'allocation réelle de la page ?  La raison est simple: de nombreux
programmes allouent beaucoup plus de place que nécessaire. Si l'on
n'utilisait pas cette stratégie d'overcommit, il faudrait acheter
beaucoup plus de mémoire/swap que réellement nécessaire.

Mais comment résoudre ce problème ?

   1. limiter les dégâts: certaines applications sont connues pour se
      planter magistralement en allouant toute la mémoire: il suffit
      alors de limiter la mémoire allouable ou allouée, en mémoire logique,
      voire mémoire physique, via ulimit: cf ulimit -a, via un wrapper
      de lancement de l'application

      on peut aussi limiter globalement (et pas par application ou
      utilisateur/login) pour le système, ou faire le contraire (limiter
      et élever la limite pour certaines applications)

   2. mettre plus de mémoire logique (plus de RAM, SWAP == 2 x RAM
      jusqu'à 2 GB de SWAP sur x86).

   3. supprimer l'overcommit (au moins partiellement)

Aha. Donc, on peut revenir à un comportement plus `sain' -- mais plus
dispendieux, de préallocation réelle des pages.

En 2.4 on cherche

   find /proc -name '*overcomm*'

et on lit la manpage (man 5 proc) qui concerne cela.

Malheureusement -- à ma connaissance -- supprimer *complètement*
l'overcommit est impossible en 2.4. On peut simplement donner des
indications au système dans le sens voulu.  En 2.4, on a donc intérêt
d'ajouter également une stratégie reposant sur 1.

D'après ce que j'ai rapidement vu, en 2.6, il semblerait que l'on puisse
définir des paramètres plus complexes, comme un % d'overcommit. Je
laisse des personnes plus expérimentées compléter pour cette nouvelle
version.

PS: dans le cas présent, si le système a dû lancer l'OOM killer (d'après
    le kernel log c'est ce qui s'est passé), c'est que 512 MB de mémoire
    n'est *pas* suffisant pour les applications lancées. Un peu de swap
    permettrait de retarder le problème, mais un véritable
    dimensionnement de l'application est nécessaire.

Une astuce pour bien dimensionner la mémoire sur un serveur x86:

   - mettre 2 GB de swap

   - laisser le système en fonction quelques jours

   - regarder (sar, vmstat 5 dans un screen, etc) si le swap est actif
     en permanence ou souvent.

S'il y a des pages dans les swap, même beaucoup, style 200-500MB mais
que ces pages ne sont jamais cherchées du swap (swap-in, si dans vmstat
5), ou ne sont jamais créées, sinon au début du fonctionnement du
système, pas besoin de rajouter de la RAM.

-- 
Je lis les messages bien formatés. N'abusez pas du Cc:. Texte == efficace.
Citer n'est pas concaténer. Editez vos messages, ça gagne du temps.
Marc se met au blog `-o ro': http://www.alphanet.ch/schaefer_chronique.html



More information about the gull mailing list