Como posso obter a quantidade de memória disponível de maneira portável nas distribuições?

12

Os arquivos / ferramentas padrão que relatam memória parecem ter diferentes formatos em diferentes distribuições Linux. Por exemplo, no Arch e Ubuntu.

  • Arco

    $ 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
    

Então, como posso portável (somente nas distribuições Linux) e obter com segurança a quantidade de memória - excluindo swap - disponível para o meu software usar em um determinado momento? Presumivelmente, é isso que é mostrado como "disponível" e "MemAvailable" na saída do freee cat /proc/meminfono Arch, mas como faço para obter o mesmo no Ubuntu ou em outra distribuição?

terdon
fonte

Respostas:

18

MemAvailableestá incluído na /proc/meminfoversão 3.14 do kernel; foi adicionado pelo commit 34e431b0a . Esse é o fator determinante nas variações de saída que você mostra. A mensagem de confirmação indica como estimar a memória disponível sem MemAvailable:

Atualmente, a quantidade de memória que está disponível para uma nova carga de trabalho, sem empurrar o sistema em troca, pode ser estimada a partir MemFree, Active(file), Inactive(file), e SReclaimable, assim como os "baixos" marcas d'água a partir /proc/zoneinfo.

As marcas d'água baixas são o nível abaixo do qual o sistema será trocado. Assim, na ausência de MemAvailablepelo menos você pode somar os valores dados para MemFree, Active(file), Inactive(file)e SReclaimable(o que estão presentes em /proc/meminfo), e subtrair as marcas d'água baixa de /proc/zoneinfo. Este último também lista o número de páginas gratuitas por zona, que podem ser úteis como comparação ...

O algoritmo completo é fornecido no patch meminfo.ce parece razoavelmente fácil de adaptar:

  • somar as marcas d'água baixas em todas as zonas;
  • pegue a memória livre identificada ( MemFree);
  • subtrair a marca d'água baixa (precisamos evitar tocá-la para evitar trocas);
  • adicione a quantidade de memória que podemos usar do cache da página (soma Active(file)e Inactive(file)): é a quantidade de memória usada pelo cache da página, menos metade do cache da página ou marca d'água baixa, o que for menor;
  • adicione a quantidade de memória que podemos recuperar ( SReclaimable), seguindo o mesmo algoritmo.

Assim, juntando tudo isso, você pode obter a memória disponível para um novo processo com:

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
fonte
Ah, bom, então pelo menos deve ser portátil na mesma versão do kernel. Isso é algo. Estou testando sua sugestão com a 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/meminfoqual deve me dar o mesmo número impresso duas vezes. No entanto, o segundo número (minha compreensão do algoritmo você sugere) é maior do que o MemAvailablemostrado na /proc/meminfo. O que estou fazendo de errado?
terdon
2
/proc/zoneinfoconta páginas, com tamanho principalmente de 4KB amd64; você também está perdendo a segurança extra adicionada ao cache da página e à memória recuperável. Simplificando o último, podemos subtrair a marca d'água baixa três vezes, para que m-12*low(3 × 4KB) forneça o resultado correto no meu sistema. (Essa simplificação subestima a memória disponível se o cache da página ou a memória recuperável for menor que o dobro da marca d'água baixa, mas você não gostaria de usar muita memória nessa situação de qualquer maneira, para que isso pareça um compromisso razoável.)
Stephen Kitt
1
@StephenKitt, como você calcularia isso para kernels mais antigos que não têm entradas (file)ou SReclaimableentradas? Em uma caixa de centos mais antiga com o kernel v 2.6.18-348.16.1.el5xen (por uname -r), esta é a saída que recebo: pastebin.com/iFWiM1kX . O seu cálculo única puxa a MemFreeparte
Mitch
@ Mitch, eu não sei, não tenho certeza de que as informações disponíveis no seu kernel antigo sejam suficientes para determinar a memória disponível (antes da troca) com precisão.
Stephen Kitt
Obrigado a todos que contribuíram para este tópico, é uma ótima referência. O cálculo do MemAvailable foi ligeiramente ajustado no Linux 4.5. No entanto, o novo cálculo MemAvailable deve sempre ser um pouco maior que (ou talvez o mesmo) do antigo, portanto, deve ser seguro usar o cálculo antigo em todos os casos. gitlab.com/procps-ng/procps/issues/42
sourcejedi
7

Enquanto a resposta de Stephen é perfeitamente suficiente e erra por precaução, decidi escrever a lógica completa, incluindo as comparações mínimas. As informações são lidas primeiro em / proc / meminfo e armazenadas em uma variável para que os detalhes da memória sejam consistentes.

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

O resultado armazenado na variável está em bytes.

crashmaxed
fonte
Embora essa resposta implemente o cálculo no commit 34e431b0a, a resposta de Stephen Kitt forneceu uma estimativa mais precisa de duas máquinas em cada cinco que testei. Nas cinco máquinas, ambas as respostas forneceram estimativas maiores do que a leitura MemAvailable diretamente de / proc / meminfo. Provavelmente, uma maneira mais segura é obter o menor entre os 2 e multiplicar por 0,95 ou mais.
toddwz 9/03