O cache de writeback ('dirty') parece estar limitado a menos que dirty_background_ratio. O que está sendo limitado por? Como este limite é calculado?

1

Eu testei o Linux 4.18.16-200.fc28.x86_64 . Meu sistema tem 7,7G de RAM total, de acordo com free -h .

Eu tenho valores padrão para os vm.dirty* sysctl. dirty_background_ratio é 10 e dirty_ratio é 20. Com base em tudo que li, espero que o Linux comece a escrever sobre cache sujo quando atingir 10% de RAM: 0,77G. E as chamadas write () em buffer devem bloquear quando o cache sujo atingir 20% da RAM: 1.54G.

Eu corri dd if=/dev/zero of=~/test bs=1M count=2000 e observei o campo dirty em atop . Enquanto o comando dd estava em execução, o valor dirty estabilizou em torno de 0.5G. Isso é significativamente menor do que o limite de fundo sujo (0.77G)! Como isso pode ser? O que estou perdendo?

dirty_expire_centisecs é 3000, então não acho que isso possa ser a causa. Eu até tentei reduzir dirty_expire_centisecs para 100 e dirty_writeback_centisecs para 10, para ver se isso estava limitando dirty . Isso não alterou o resultado.

Inicialmente escrevi estas observações como parte desta investigação: Por que os problemas" USB-stick stall "foram reportados em 2013? Por que esse problema não foi resolvido pelo código existente de "limitação I / O suja"?

Eu entendo que a meio caminho entre os dois limites - 15% = 1.155G - as chamadas write () começam a ser estranguladas (atrasadas) em uma curva. Mas nenhum atraso é adicionado quando abaixo deste teto; os processos que geram páginas sujas são permitidos "free run".

Pelo que entendi, a limitação visa manter o cache sujo em algum lugar igual ou superior a 15% e evitar atingir o limite de 20%. Não fornece garantia para todas as situações. Mas estou testando um caso simples com um comando dd ; Eu acho que deve simplesmente ratelimit as chamadas write () para coincidir com a velocidade de gravação alcançada pelo dispositivo.

(Não há uma garantia simples, pois existem algumas exceções complexas. Por exemplo, o código de limitação limita o atraso que irá impor a um máximo de 200 ms. Mas não se o limite de destino do processo for menor que uma página por segundo, nesse caso, será aplicado um limite estrito.)

  • Documentação / sysctl / vm.txt - Linux v4.18
  • Aceleração suja de não-I / O - 2011 LWN.net.
  • (dirty_background_ratio + dirty_ratio)/2 dirty data in total ... is an amount of dirty data when we start to throttle processes -- Jan Kara, 2013

  • Users will notice that the applications will get throttled once crossing the global (background + dirty)/2=15% threshold, and then balanced around 17.5%. Before patch, the behavior is to just throttle it at 20% dirtyable memory

    -- commit 143dfe8611a6, "writeback: IO-less balance_dirty_pages()"

  • The memory-management subsystem will, by default, try to limit dirty pages to a maximum of 15% of the memory on the system. There is a "magical function" called balance_dirty_pages() that will, if need be, throttle processes dirtying a lot of pages in order to match the rate at which pages are being dirtied and the rate at which they can be cleaned." -- Writeback and control groups, 2015 LWN.net.

  • balance_dirty_pages () no Linux 4.18.16.
por sourcejedi 08.11.2018 / 00:27

1 resposta

1

Veja Documentation / sysctl / vm.txt :

dirty_ratio

Contains, as a percentage of total available memory that contains free pages and reclaimable pages, the number of pages at which a process which is generating disk writes will itself start writing out dirty data.

The total available memory is not equal to total system memory.

A memória disponível é calculada em global_dirtyable_memory () . É igual à quantidade de memória livre mais o cache da página. Não inclui páginas permutáveis (ou seja, alocações de memória anônima, memória que não é apoiada por um arquivo).

Esse comportamento se aplica desde Linux 3.14 (2014) . Antes dessa alteração, as páginas permutáveis eram incluídas no total global_dirtyable_memory ().

Exemplo de estatísticas durante a execução do comando dd :

$ while true; do grep -E '^(Dirty:|Writeback:|MemFree:|Cached:)' /proc/meminfo | tr '\n' ' '; echo; sleep 1; done
MemFree:         1793676 kB Cached:          1280812 kB Dirty:                 4 kB Writeback:             0 kB
MemFree:         1240728 kB Cached:          1826644 kB Dirty:            386128 kB Writeback:         67608 kB
MemFree:         1079700 kB Cached:          1983696 kB Dirty:            319812 kB Writeback:        143536 kB
MemFree:          937772 kB Cached:          2121424 kB Dirty:            312048 kB Writeback:        112520 kB
MemFree:          755776 kB Cached:          2298276 kB Dirty:            389828 kB Writeback:         68408 kB
...
MemFree:          136376 kB Cached:          2984308 kB Dirty:            485332 kB Writeback:         51300 kB
MemFree:          101340 kB Cached:          3028996 kB Dirty:            450176 kB Writeback:        119348 kB
MemFree:          122304 kB Cached:          3021836 kB Dirty:            552620 kB Writeback:          8484 kB
MemFree:          101016 kB Cached:          3053628 kB Dirty:            501128 kB Writeback:         61028 kB

A última linha mostra cerca de 3.150.000 KB de memória "disponível" e um total de 562.000 KB de dados sendo gravados ou aguardando writeback. Isso faz com que seja de 17,8%. Embora parecesse que a proporção flutuava mais do que isso, e muitas vezes estava mais perto de 15%. EDITAR : embora esses números pareçam mais próximos, não confie neste método. Ainda não é o cálculo correto e pode dar resultados muito errados. Veja o acompanhamento aqui .

Eu encontrei isso da maneira mais difícil:

Percebi que há um ponto de rastreio em balance_dirty_pages () , que pode ser usado para " analisando a dinâmica dos algoritmos de estrangulamento ". Então usei perf :

$ sudo perf list '*balance_dirty_pages'

List of pre-defined events (to be used in -e):

  writeback:balance_dirty_pages                      [Tracepoint event]
...
$ sudo perf record -e writeback:balance_dirty_pages dd if=/dev/zero of=~/test bs=1M count=2000
$ sudo perf script

Ele mostrou que dirty (medido em páginas de 4096 bytes) era menor do que eu esperava, porque setpoint era baixo. Eu segui o código; isso significava que deve haver um valor similarmente baixo para freerun na definição do ponto de rastreio, que é definido como (thresh + bg_thresh) / 2 ... e retornou para global_dirtyable_memory() .

    
por 10.11.2018 / 21:08