Comment puis-je obtenir la quantité de mémoire disponible de manière portable dans les distributions?

12

Les fichiers / outils standard qui signalent la mémoire semblent avoir différents formats sur différentes distributions Linux. Par exemple, sur Arch et Ubuntu.

  • Cambre

    $ free
                  total        used        free      shared  buff/cache   available
    Mem:        8169312     3870392     2648348       97884     1650572     4110336
    Swap:      16777212      389588    16387624
    
    
    $ head /proc/meminfo 
    MemTotal:        8169312 kB
    MemFree:         2625668 kB
    MemAvailable:    4088520 kB
    Buffers:          239688 kB
    Cached:          1224520 kB
    SwapCached:        17452 kB
    Active:          4074548 kB
    Inactive:        1035716 kB
    Active(anon):    3247948 kB
    Inactive(anon):   497684 kB
    
  • Ubuntu

    $ free
                 total       used       free     shared    buffers     cached
    Mem:      80642828   69076080   11566748    3063796     150688   58358264
    -/+ buffers/cache:   10567128   70075700
    Swap:     20971516    5828472   15143044
    
    
    $ head /proc/meminfo 
    MemTotal:       80642828 kB
    MemFree:        11565936 kB
    Buffers:          150688 kB
    Cached:         58358264 kB
    SwapCached:      2173912 kB
    Active:         27305364 kB
    Inactive:       40004480 kB
    Active(anon):    7584320 kB
    Inactive(anon):  4280400 kB
    Active(file):   19721044 kB
    

Alors, comment puis-je porter de manière portable (sur des distributions Linux uniquement) et obtenir de manière fiable la quantité de mémoire (hors swap) disponible pour mon logiciel à un moment donné? Vraisemblablement, c'est ce qui est indiqué comme «disponible» et «MemAvailable» dans la sortie de freeet cat /proc/meminfodans Arch, mais comment puis-je obtenir les mêmes dans Ubuntu ou une autre distribution?

terdon
la source

Réponses:

18

MemAvailableest inclus /proc/meminfodepuis la version 3.14 du noyau; il a été ajouté par commit 34e431b0a . C'est le facteur déterminant dans les variations de sortie que vous montrez. Le message de validation indique comment estimer la mémoire disponible sans MemAvailable:

À l' heure actuelle, la quantité de mémoire disponible pour une nouvelle charge de travail, sans pousser le système de swap, peut être estimée à partir MemFree, Active(file), Inactive(file), et SReclaimable, ainsi que les « filigranes de bas » de /proc/zoneinfo.

Les filigranes bas sont le niveau en dessous duquel le système va permuter. Donc , en l'absence de MemAvailablevous pouvez au moins additionner les valeurs données pour MemFree, Active(file), Inactive(file)et SReclaimable(selon sont présents dans /proc/meminfo) et soustraire les bas de filigranes /proc/zoneinfo. Ce dernier répertorie également le nombre de pages libres par zone, ce qui pourrait être utile à titre de comparaison ...

L'algorithme complet est donné dans le patch meminfo.cet semble assez facile à adapter:

  • additionner les filigranes bas dans toutes les zones;
  • prenez la mémoire libre identifiée ( MemFree);
  • soustraire le filigrane bas (nous devons éviter de le toucher pour éviter de permuter);
  • ajouter la quantité de mémoire que nous pouvons utiliser à partir du cache de pages (somme de Active(file)et Inactive(file)): c'est la quantité de mémoire utilisée par le cache de pages, moins la moitié du cache de pages, ou le filigrane bas, le plus petit des deux;
  • ajoutez la quantité de mémoire que nous pouvons récupérer ( SReclaimable), en suivant le même algorithme.

Donc, en rassemblant tout cela, vous pouvez obtenir la mémoire disponible pour un nouveau processus avec:

awk -v low=$(grep low /proc/zoneinfo | awk '{k+=$2}END{print k}') \
 '{a[$1]=$2}
  END{ 
   print a["MemFree:"]+a["Active(file):"]+a["Inactive(file):"]+a["SReclaimable:"]-(12*low); 
  }' /proc/meminfo 
Stephen Kitt
la source
Ah, bien, donc au moins il devrait être portable sur la même version du noyau. C'est quelque chose. Je teste votre suggestion awk -v low=$(grep low /proc/zoneinfo | awk '{k+=$2}END{print k}') '{a[$1]=$2}END{m=a["MemFree:"]+a["Active(file):"]+a["Inactive(file):"]+a["SReclaimable:"]; print a["MemAvailable:"],m-low}' /proc/meminfoqui devrait me donner le même numéro imprimé deux fois. Cependant, le deuxième nombre (ma compréhension de l'algorithme que vous suggérez) est supérieur à celui MemAvailableindiqué dans /proc/meminfo. Qu'est-ce que je fais mal?
terdon
2
/proc/zoneinfocompte les pages, dont la taille est principalement de 4 Ko amd64; vous manquez également la sécurité supplémentaire ajoutée au cache de pages et à la mémoire récupérable. Simplifiant ce dernier, nous pouvons soustraire le filigrane bas trois fois, donc m-12*low(3 × 4KB) donne le résultat correct sur mon système. (Cette simplification sous-estime la mémoire disponible si le cache de pages ou la mémoire récupérable est inférieure à deux fois le faible filigrane, mais vous ne voudriez pas utiliser beaucoup de mémoire dans cette situation de toute façon, ce qui semble être un compromis raisonnable.)
Stephen Kitt
1
@StephenKitt comment feriez-vous pour le calculer pour les noyaux plus anciens qui n'ont ni (file)entrée ni SReclaimableentrée? Sur une ancienne boîte centos avec noyau v 2.6.18-348.16.1.el5xen (per uname -r), voici la sortie que j'obtiens: pastebin.com/iFWiM1kX . Votre calcul tire seulement la MemFreepartie
Mitch
@Mitch Je ne sais pas, je ne suis pas sûr que les informations disponibles à partir de votre ancien noyau soient suffisantes pour déterminer avec précision la mémoire disponible (avant l'échange).
Stephen Kitt
Merci à tous ceux qui ont contribué à ce fil, c'est une excellente référence. Le calcul de MemAvailable a été légèrement ajusté dans Linux 4.5. Cependant, le nouveau calcul MemAvailable doit toujours être légèrement supérieur (ou peut-être le même que) l'ancien, il doit donc être sûr d'utiliser l'ancien calcul dans tous les cas. gitlab.com/procps-ng/procps/issues/42
sourcejedi
7

Alors que la réponse de Stephen est parfaitement suffisante et fait preuve de prudence, j'ai décidé de rédiger la logique complète, y compris les comparaisons minimales. Les informations sont d'abord lues dans / proc / meminfo et stockées dans une variable afin que les détails de la mémoire soient cohérents.

LOW_WATERMARK=$(awk '$1 == "low" {LOW_WATERMARK += $2} END {print LOW_WATERMARK * 4096}' /proc/zoneinfo)

MEMINFO=$(</proc/meminfo)

MEMINFO_MEMFREE=$(echo "${MEMINFO}" | awk '$1 == "MemFree:" {print $2 * 1024}')
MEMINFO_FILE=$(echo "${MEMINFO}" | awk '{MEMINFO[$1]=$2} END {print (MEMINFO["Active(file):"] + MEMINFO["Inactive(file):"]) * 1024}')
MEMINFO_SRECLAIMABLE=$(echo "${MEMINFO}" | awk '$1 == "SReclaimable:" {print $2 * 1024}')

MEMINFO_MEMAVAILABLE=$((
  MEMINFO_MEMFREE - LOW_WATERMARK
  + MEMINFO_FILE - ((MEMINFO_FILE/2) < LOW_WATERMARK ? (MEMINFO_FILE/2) : LOW_WATERMARK)
  + MEMINFO_SRECLAIMABLE - ((MEMINFO_SRECLAIMABLE/2) < LOW_WATERMARK ? (MEMINFO_SRECLAIMABLE/2) : LOW_WATERMARK)
))

if [[ "${MEMINFO_MEMAVAILABLE}" -le 0 ]]
then
  MEMINFO_MEMAVAILABLE=0
fi

Le résultat stocké dans la variable est en octets.

crashmaxed
la source
Alors que cette réponse implémente le calcul dans le commit 34e431b0a, la réponse de Stephen Kitt a fourni une estimation plus précise sur 2 machines sur 5 que j'ai testées. Sur les 5 machines, les deux réponses ont donné des estimations plus importantes que MemAvailable lu directement depuis / proc / meminfo. Un moyen plus sûr consiste probablement à obtenir le plus petit entre les 2 et à multiplier par 0,95 environ.
toddwz