Allouer plus de mémoire que disponible
Le
Rémi Moyen
Salut,
J'ai une question sur la manière dont se comporte un programme sous
Linux, en C++, quand on essaye d'allouer plus de mémoire que
disponible sur le système. À la base, l'opérateur new lance dans ce
cas-là une exception et c'est à moi de la rattraper et de la traiter,
très bien.
Mais mon problème est de savoir si, par derrière, le noyau essaye
d'allouer toute la mémoire avant de se rendre compte qu'il n'en a pas
assez de disponible, ou est-ce qu'il fait d'abord une vérification
avant de se lancer là-dedans ?
Dans le cas où la mémoire est sur de la RAM, ça ne change probablement
pas grand chose (en perception par l'utilisateur), mais si il y a du
swap, est-ce que le noyau va faire swapper tout ce qu'il peut pendant
une heure avant de se rendre compte qu'il lui manque 42 octets ?
Si c'est le cas, alors j'imagine que j'ai intérêt à implémenter
quelques checks rapides sur la mémoire disponible avant de commencer à
allouer des blocs de 1 Go (ça m'arrive), sinon mes utilisateurs
risquent de ne pas trop aimer devoir attendre longtemps, et swapper
tout les autres programmes, avant de s'entendre dire qu'il n'y a pas
assez de mémoire pour faire ce qu'ils veulent ! Et dans ce cas-là
encore, y'a-t-il une manière plus ou moins "standard" de vérifier "à
la main" la mémoire disponible ?
Et accessoirement, j'imagine que différents OS peuvent se comporter
différemment de ce point de vue, donc est-ce pareil pour SunOS ?
IRIX ? (les deux autres systèmes que je suis susceptible d'utiliser)
Merci d'avance !
--
Rémi Moyen
J'ai une question sur la manière dont se comporte un programme sous
Linux, en C++, quand on essaye d'allouer plus de mémoire que
disponible sur le système. À la base, l'opérateur new lance dans ce
cas-là une exception et c'est à moi de la rattraper et de la traiter,
très bien.
Mais mon problème est de savoir si, par derrière, le noyau essaye
d'allouer toute la mémoire avant de se rendre compte qu'il n'en a pas
assez de disponible, ou est-ce qu'il fait d'abord une vérification
avant de se lancer là-dedans ?
Dans le cas où la mémoire est sur de la RAM, ça ne change probablement
pas grand chose (en perception par l'utilisateur), mais si il y a du
swap, est-ce que le noyau va faire swapper tout ce qu'il peut pendant
une heure avant de se rendre compte qu'il lui manque 42 octets ?
Si c'est le cas, alors j'imagine que j'ai intérêt à implémenter
quelques checks rapides sur la mémoire disponible avant de commencer à
allouer des blocs de 1 Go (ça m'arrive), sinon mes utilisateurs
risquent de ne pas trop aimer devoir attendre longtemps, et swapper
tout les autres programmes, avant de s'entendre dire qu'il n'y a pas
assez de mémoire pour faire ce qu'ils veulent ! Et dans ce cas-là
encore, y'a-t-il une manière plus ou moins "standard" de vérifier "à
la main" la mémoire disponible ?
Et accessoirement, j'imagine que différents OS peuvent se comporter
différemment de ce point de vue, donc est-ce pareil pour SunOS ?
IRIX ? (les deux autres systèmes que je suis susceptible d'utiliser)
Merci d'avance !
--
Rémi Moyen

Poser une question


C'est le fameux problème de l'"overcommit". Probablement tous les
systèmes Unix modernes, et certainement Linux et FreeBSD font comme les
banquiers qui prêtent de l'argent qu'ils n'ont pas, ils allouent de la
mémoire qui n'est pas disponible, et on voit *aprés* si dès fois il y a
un problème. Il paraît que faire comme ça est indispensable pour
préserver les performances. La conclusion est que si on veut s'assurer
d'avoir rééllement la mémoire disponible, il faut aller écrire dans
chaque page en trappant le signal SEGV. Autrement dit, ça doit
probablement dire que ce qu'on attend de l'opérateur new en C++ ne
fonctionne pas du tout. Il n'y a aucun garantie que la mémoire sera
rééllement disponible sans effectuer des opérations de plus bas niveau
que celles que le C++ fournit en standard.
--
Michel TALON
C'est le fameux problème de l'"overcommit". Probablement tous les
systèmes Unix modernes, et certainement Linux et FreeBSD font comme les
banquiers qui prêtent de l'argent qu'ils n'ont pas, ils allouent de la
mémoire qui n'est pas disponible, et on voit *aprés* si dès fois il y a
un problème. Il paraît que faire comme ça est indispensable pour
préserver les performances. La conclusion est que si on veut s'assurer
d'avoir rééllement la mémoire disponible, il faut aller écrire dans
chaque page en trappant le signal SEGV. Autrement dit, ça doit
probablement dire que ce qu'on attend de l'opérateur new en C++ ne
fonctionne pas du tout. Il n'y a aucun garantie que la mémoire sera
rééllement disponible sans effectuer des opérations de plus bas niveau
que celles que le C++ fournit en standard.
Tu peux lire l'avis d'experts ici:
http://leaf.dragonflybsd.org/mailar...00239.html
--
Michel TALON
Hmmm... Je crois que je me suis mal exprimé, ou alors que je ne
comprends pas exactement ta réponse. Si je te comprends bien, tu dis
que dans certains cas, 'new' pourrait réussir alors qu'en réalité, le
jour où j'essaierais d'écrire dans ma mémoire, ça ne marchera pas
parce qu'en réalité le noyau m'a alloué des pages qui ne sont pas
vraiment disponibles ? Si c'est le cas, c'est en effet plutôt (!)
génant. Ceci dit, dans mon cas (j'ai testé), mes 'new' me renvoient
bien une exception quand y'a plus de mémoire, donc ce problème n'a pas
l'air d'arriver. Enfin, pas jusqu'à présent...
Mais ma question était plutôt de savoir si le noyau allait passer du
temps à essayer d'allouer avant de me dire qu'il ne peut pas ou si il
était capable de me dire 'non' avant d'avoir tout essayé ? Ta réponse
me laisse supposer que c'est plutôt la première alternative que la
deuxième, c'est bien ça ?
J'ai lu rapidement et j'avoue ne pas avoir tout compris, mais il me
semble qu'une des conclusions, ça serait que c'est à mon programme de
ne pas demander plus de mémoire que ce qui est raisonnable (et qu'un
des problèmes est de définir ce "raisonnable" proprement), et que je
ne devrais jamais me reposer sur le noyau pour me dire si oui ou non
j'ai assez de mémoire. Ai-je bien compris ?
Dans ce cas, j'ai l'impression que ça veut dire qu'il faut plus ou
moins que j'implémente un micro-garbage collector pour savoir combien
de mémoire j'utilise ?? Ça me semble bizarre, j'ai dû mal
comprendre...
Merci pour ta réponse, en tout cas !
--
Rémi Moyen
La différence, c'est que le gestionnaire de mémoire doit écrire dans
chaque page allouée lui-même. Mais si tu appelle directement mmap ou
brk ou sbrk, sans écrire dans les pages allouées, effectivement tu
peux obtenir l'exception plus tard, quand tu accéderas à ces pages.
Quand il n'y a plus de mémoire disponible, le noyau commence à tuer
des processus un peu au hasard pour faire de la place. Alors il doit
forcémener passer un peu de temps pour essayer d'allouer... Mais ça,
c'est seulement quand on a rempli toute la mémoire RAM et swap
disponible, et seulement quand on a effectivement besoin de la place,
pas quand on "l'alloue". Sinon le noyau alloue la mémoire sans la
réserver. La seule chose qu'il fait, c'est qu'il mappe toute la
mémoire allouée sur une page mise à zéro en lecture seule. C'est
quand on essaye d'écrire dans cette page que le système va essayer
d'allouer effectivement une page de mémoire et s'il n'en reste plus de
libre en RAM, il va swapper, et s'il n'en reste plus de libre en swap
il va envoyer des signaux (ce qui peut tuer des processus).
Oui.
Oui. Tu peux avoir une enveloppe fixe, ou évaluer la mémoire
nécessaire en fonction de la taille des données. Tu as de toutes
façon la limite due à l'espace adressable (moins la place pour le
programme, les piles, les bibliothèques, etc). Finalement, c'est une
question d'exploitation: c'est aux opérateurs d'ordonancer les tâches
pour éviter de lancer en même temps deux programmes qui nécessitent
plus que la mémoire physique disponible dans l'ordinateur (et même,
rester de préférence en dessous de la RAM, car si on commence à
swapper, on va rallentire énormément les traitements).
Si on spécifiait pour chaque programme les limites d'espace mémoire
(et temps CPU) nécessaires, on pourrait aussi écrire un ordonanceur
automatique, ce qui se fait dans les grosses exploitations.
--
__Pascal Bourguignon__ http://www.informatimago.com/
"By filing this bug report you have challenged the honor of my
family. Prepare to die!"
OK, je comprends ça. Dans mon cas où je passe par un 'new' (qui doit
être défini quelque part dans la stdlibc++, je suppose), est-ce que je
peux supposer que 'new' s'occuppe d'écrire dans chaque page pour
vérifier qu'elle est là, ou pas ? Est-ce que quelqu'un sait où est-ce
que je pourrais savoir ça (à part lire le code source de la libstdc++,
mais bon...) ?
D'accord. Donc, si je demande plus de mémoire que disponible, suivant
le comportement de 'new', il va se passer:
- soit 'new' n'écrit pas dans les pages réservées, l'appel est donc
très rapide, mais peut retourner sans échec et ça se passera mal quand
j'essaierais ensuite d'accéder pour de vrai à la mémoire (comment se
manifeste ce 'mal' ? Le noyau tuera des processus au hasard pour cause
de OOM ? Ou quelque chose de plus subtil et de récupérable ?) ;
- soit 'new' écrit dans chaque page pour vérifier sa disponibilité,
l'appel est donc potentiellement lent (parce qu'il faut aller dans
chaque page, potentiellement swapper, etc.), mais si il retourne sans
échec je suis certain que ma mémoire est dispo (bien que peut-être
partiellement sur le swap et donc lente à accéder, mais c'est un peu
un autre problème).
C'est correct ?
Pffou... ça ne m'arrange pas du tout, ça. Y'a des fonctions classiques
pour faire ça facilement (en C++, toujours) ? Genre un appel système
qui fixerait la taille maxi que je veux et qui ferait en sorte que
n'importe quel 'new' allouant au-dessus de cette limite lance une
exception ? Ça devrait être le comportement par défaut, me semble-t-
il...
Merci pour tes précisions, je cerne mieux le problème.
--
Rémi Moyen