La section 9.6 "Surcompromis et OOM" dans le document mentionné par @dunxd est particulièrement explicite sur les dangers de permettre le surcompromis. Cependant, le 80
semblait également intéressant pour moi, donc j'ai réalisé quelques tests.
Ce que j'ai découvert, c'est que le surcompromis_ratio
affecte la RAM totale disponible pour TOUS les processus. Les processus racine ne semblent pas être traités différemment des processus utilisateur normaux.
Fixer le ratio à 100
ou moins devrait fournir la sémantique classique où les valeurs de retour de malloc/sbrk
sont fiables. Fixer des ratios inférieurs à 100
pourrait être un moyen de réserver plus de RAM pour des activités non processus comme le mise en cache, etc.
Ainsi, sur mon ordinateur avec 24 GiB de RAM, sans swap activé, 9 GiB en cours d'utilisation, avec top
montrant
Mem: 24683652k total, 9207532k utilisés, 15476120k libres, 19668k tampons
Swap: 0k total, 0k utilisés, 0k libres, 241804k mis en cache
Voici quelques paramètres du surcompromis_ratio
et combien de RAM mon programme consommateur de RAM a pu allouer (touchant chaque page) - dans chaque cas, le programme s'est arrêté correctement une fois que malloc
a échoué.
50 ~680 MiB
60 ~2900 MiB
70 ~5200 MiB
100 ~12000 MiB
Exécuter plusieurs à la fois, même avec certains en tant qu'utilisateur racine, n'a pas changé la quantité totale qu'ils ont consommée ensemble. Il est intéressant de constater qu'il était incapable de consommer les 3+ Go restants ou plus; le libre
n'a pas chuté bien en dessous de ce qui est affiché ici :
Mem: 24683652k total, 20968212k utilisés, 3715440k libres, 20828k tampons
Les expériences étaient chaotiques - tout ce qui utilise malloc au moment où toute la RAM est utilisée a tendance à planter, car de nombreux programmeurs sont terribles en matière de vérification des échecs de malloc en C, certaines bibliothèques de collection populaires l'ignorent complètement, et le C++ et divers autres langages sont encore pires.
La plupart des premières implémentations de RAM imaginaire que j'ai vues visaient à traiter un cas très spécifique, où un seul gros processus - disons 51%+ de la mémoire disponible - devait fork()
pour exécuter un programme de support, généralement beaucoup plus petit. Les systèmes d'exploitation avec des sémantiques de copie sur écriture permettraient le fork()
, mais avec la condition que si le processus bifurqué tentait effectivement de modifier trop de pages mémoire (chacune devant alors être instanciée en tant que nouvelle page indépendante du processus initial gigantesque) il finirait par être tué. Le processus parent n'était en danger que s'il allouait plus de mémoire, et pouvait gérer le manque, dans certains cas en attendant un peu pour que d'autres processus meurent, puis en continuant. Le processus enfant se remplaçait généralement lui-même par un programme (typiquement plus petit) via exec()
et était alors libéré de la condition.
Le concept de surcompromis de Linux est une approche extrême qui permet à la fois la fork()
de se produire ainsi que permet aux processus individuels de s'alimenter massivement. Les décès causés par l'OOM-killer se produisent de manière asynchrone, même pour les programmes qui gèrent de manière responsable l'allocation mémoire. Je déteste personnellement le surcompromis à l'échelle du système en général et l'oom-killer en particulier - cela favorise une approche insouciante de la gestion de la mémoire qui infecte les bibliothèques et à travers elles chaque application qui les utilise.
Je suggère de régler le ratio à 100, et d'avoir également une partition de swap qui ne sera généralement utilisée que par des processus énormes - qui utilisent souvent une fraction minuscule de la partie d'eux-mêmes qui est stockée dans le swap, et protégeant ainsi la grande majorité des processus du mauvais fonctionnement de l'OOM-killer. Cela devrait protéger votre serveur web des arrêts aléatoires, et s'il a été écrit pour gérer de manière responsable malloc
, même de se tuer lui-même (mais ne pariez pas là-dessus).
Cela signifie que j'utilise cela dans /etc/sysctl.d/10-no-overcommit.conf
vm.overcommit_memory = 2
vm.overcommit_ratio = 100