Linux Virtual Memory Management

Se você usa Linux, alguma vez já notou uma lentidão extrema – a ponto de algumas vezes deixar o sistema irresponsivo – ao copiar arquivos grandes, de alguns gigabytes, para mídias lentas, como pendrives USB (especialmente aqueles “genéricos”, que oferecem baixa performance)? Se o seu computador é 64 bits e tem bastante memória RAM (8 GB ou mais), muito provavelmente já notou isso. Tanto é que até o Linus Torvalds já abordou esse problema [1], há alguns anos atrás; mesmo assim, ainda não há uma solução definitiva, mas existem tunings do subsistema de Virtual Memory do kernel do Linux que minimizam esse problema.

Antes de continuar, é preciso entender um pouco sobre alguns conceitos do gerenciamento de memória do Linux. Não vou entrar em muitos detalhes, pois este não é um artigo acadêmico, mas no final colocarei algumas referências para quem quiser se aprofundar mais. Um primeiro conceito que deve ficar claro é: o Linux trabalha por padrão com buffered I/O. De forma simplificada, isso significa que as operações de escrita simplesmente copiam os dados para a memória RAM [2], e depois, em background, o kernel vai fazendo a escrita em si (flush) no dispositivo destino. Dado isto, entra o segundo conceito: dirty memory, que é justamente essa informação que está temporariamente na memória RAM, esperando ser escrita em um dispositivo de armazenamento.

Acontece que, quando copiamos muitos dados para uma mídia lenta, há um problema de bufferbloat, em que a fila de I/O requests fica gigantesca. Isso acarreta toda a lentidão, já que outras operações de I/O são também afetadas, além de outros efeitos colaterais. Dentre as alternativas propostas para minimizar este problema, as mais simples envolvem ajustar os parâmetros relacionados à dirty memory do subsistema de Virtual Memory do kernel [3][4]. O objetivo é deixar a fila menor, definindo um threshold mais agressivo para o flush da dirty memory para o dispositivo (o daemon que controla isso é o pdflush, para quem quiser se aprofundar).

1. Procedimentos

Antes de experimentar os ajustes em questão, convém monitorar o seu sistema durante uma simulação, a fim de confirmar o problema. O sintoma mais óbvio é a lentidão, mas se você quiser ir mais a fundo, pode monitorar a quantidade de dirty memory com o seguinte comando:

  watch -n1 grep -e Dirty: /proc/meminfo

Enquanto monitora, copie um arquivo grande, de alguns gigabytes, para um pendrive (não vale aquele pendrive USB 3.0 de última geração, hein?). Observe a dirty memory aumentar assustadoramente, para alguns GBs, até que o pdflush entra em ação e começa a escrever os dados no dispositivo.

Percebeu que, aparentemente, a cópia rapidamente deu-se como concluída, mas na prática ela estava longe de terminar? Daí a importância de não remover o pendrive sem ejetar antes: podem haver muitos dados pendentes de escrita em background, e a operação de ejeção só vai concluir após eles estarem devidamente gravados.

Agora, vamos ao tuning: os parâmetros importantes aqui sãovm.dirty_background_bytesvm.dirty_background_ratiovm.dirty_bytes e vm.dirty_ratio. Na documentação do kernel você encontra uma explicação detalhada do que cada parâmetro faz [5], mas pelos nomes já dá para ter uma ideia. Só há um detalhe: os parâmetros com “bytes” e “ratio” são mutuamente exclusivos, ou seja, ajustando um, o outro automaticamente é desabilitado (ficando com valor 0). No meu sistema, os valores default são:

  vm.dirty_background_bytes = 0
  vm.dirty_background_ratio = 20
  vm.dirty_bytes = 0
  vm.dirty_ratio = 50

Perceba que até 50% da memória RAM total pode ser dirty! Vamos então ajustar os parâmetros para uma das recomendações, e ver o que acontece, repetindo a simulação:

  sysctl -w vm.dirty_background_bytes=16777216
  sysctl -w vm.dirty_bytes=50331648
  watch -n1 grep -e Dirty: /proc/meminfo
  (repetir cópia do arquivo)

Bem diferente, não é? Sem contar que agora o indicador de progresso da cópia é mais confiável.

Outra opção, que dá resultados similares mas trabalha com percentuais da memória RAM, ao invés de valores absolutos em bytes, é:

  sysctl -w vm.dirty_background_ratio=5
  sysctl -w vm.dirty_ratio=10

2. Conclusão

Tal situação de bufferbloat é mais usual em desktops, onde é comum realizar a cópia de arquivos grandes para pendrives lentos, mas é também possível de acontecer, com cenários alternativos, em servidores, sendo importante conhecer o problema, a solução, e o que está por trás dela. Claro que os valores sugeridos no tuning são, como disse, recomendações; o ideal para o seu caso pode variar.

O que acho mais interessante em solucionar esses problemas é que, quando queremos ir além do básico, além da receita de bolo, aprendemos muito. Espero que também tenha sido sua percepção, e se quiser ir ainda além, é só explorar as referências.

3. Referências

  1. https://yarchive.net/comp/linux/dirty_limits.html
  2. https://lwn.net/Articles/682582/
  3. https://unix.stackexchange.com/questions/107703/why-is-my-pc-freezing-while-im-copying-a-file-to-a-pendrive/107722#107722
  4. https://askubuntu.com/questions/397249/system-freezes-unresponsive-unusable-when-copying-large-file-to-usb
  5. https://www.kernel.org/doc/Documentation/sysctl/vm.txt

4. Sobre o autor

Dorian Bolivar é Engenheiro de Sistemas especialista em Linux, com mais de 15 anos de experiência na área. Trabalha gerenciando diariamente centenas de servidores Linux. Visite o website: https://www.dorianbolivar.com.

Rolar para cima