Aula #25 - Scheduling de I/O


O desempenho do sistema, muitas vezes depende muito da estratégia de otimização do scheduler de I/O. Muitos fatores influenciam o comportamento: tempos de acesso hardware, minimizar o desgaste de mídia de armazenamento, garantir a integridade dos dados, garantir acesso eficiente aos aplicativos que precisam fazer I/O, e ser capaz de priorizar tarefas importantes. 


O Linux oferece uma variedade de schedulers de I/O, cada um com parâmetros ajustáveis, bem como uma série de utilitários para configurar e analisar o desempenho de I/O.

O scheduler de I/O fornece a interface entre a camada de bloco genérico e os drivers de dispositivos de baixo nível. Tanto as camadas de VM (Memória Virtual) quanto de VFS (Virtual File System) apresentam pedidos de I/O para dispositivos de bloco; é responsabilidade da camada de scheduling de I/O priorizar e ordenar esses pedidos antes que eles sejam passados aos dispositivos de bloco.


Qualquer algoritmo de scheduling de I/O deve satisfazer determinados requisitos (as vezes conflitantes):

    Tempos de acesso ao Hardware deve ser minimizado; ou seja, os pedidos devem ser ordenados de acordo com a localização física no disco. Isto leva a um esquema de elevador onde os pedidos são inseridos na fila em ordem física.
    
    Os pedidos deverão ser fundidos na medida do possível para reduzir o número de pedidos e aumentar o tamanho de cada pedido individual também para minimizar o tempo de acesso ao disco.
    
    Os pedidos devem ser satisfeitos com o valor mais baixo de latência possível; e em alguns casos pode ser importante conhecer os prazos máximos para que as requisições sejam atendidas.
    
    As operações de escrita normalmente podem ser dadas como concluídas quando atingirem o cache de disco, e antes de terem sido de fato escritas no disco. As operações de leitura, no entanto precisam que o processo aguarde a conclusão da requisição. Favorecer leitura sobre escrita resulta em melhor paralelismo e melhora a capacidade de resposta do sistema.
    
    Os processos devem compartilhar a largura de banda de I/O de forma justa, ou pelo menos organizada; mesmo que isso tenha um leve impacto na performance geral.


Uma vez que essas demandas podem ser conflitantes, diferentes schedulers de I/O serão adequados para diferentes demandas; por exemplo, um grande servidor de banco de dados contra um sistema desktop. Além disso, hardware diferente pode precisar de estratégia diferente. A fim de proporcionar flexibilidade, o kernel do Linux tem um esquema de orientação a objetos, em que os ponteiros para as diversas funções necessárias são fornecidos em uma estrutura de dados. É possível escolher qual estrutura será utilizada modificando a linha de comando do kernel, como em:



linux ...  elevator=[cfq|deadline|noop]


Pelo menos um dos algoritmos de scheduler de I/O deve ser compilado no kernel. As opções atuais são:


  •     Completely Fair Queueing (CFQ) ou filas completamente justas
  •     Deadline Scheduling ou scheduling por prazo
  •     noop (Um esquema simples)


A opção padrão é definida durante a compilação; distribuições modernas escolhem como padrão o CFQ ou Deadline.


A introdução gradual de discos SSD (Solid State Drive ou discos de estado sólido, ou discos sem partes móveis) que usam memória flash ao invés de discos rígidos rotativos tem implicações importantes para scheduling de I/O.


Tais dispositivos não precisam de um esquema de elevador e beneficiam de nivelamento de desgaste para espalhar I/O ao longo das células de memória para maximizar a vida útil.


Pode-se examinar o arquivo /sys/block//queue/rotational para ver se o dispositivo é um SSD ou não, como em:


$ cat /sys/block/sda/queue/rotational
1




Cada um dos schedulers de I/O expõe parâmetros que podem ser utilizados para afinar o comportamento durante a execução atual do sistema. Os parâmetros são acessados através do sistema de arquivos virtual montado no /sys.


Além disso, é possível utilizar diferentes schedulers de I/O para dispositivos diferentes. A escolha pode ser feita facilmente através da linha de comando. Por exemplo:


$ cat /sys/block/sda/queue/scheduler
noop deadline [cfq]




$ echo noop > /sys/block/sda/queue/scheduler
$ cat /sys/block/sda/queue/scheduler
[noop] deadline cfq




Os ajustes específicos variam de acordo com o scheduler escolhido, mas pode-se verificar quais estão disponíveis na pasta:

/sys/block/.../queue/iosched



Para o CFQ:

$ ls -l /sys/block/sda/queue/iosched
total 0
-rw-r--r-- 1 root root 4096 Jun 19 01:00 back_seek_max
-rw-r--r-- 1 root root 4096 Jun 19 01:00 back_seek_penalty
-rw-r--r-- 1 root root 4096 Jun 19 01:00 fifo_expire_async
-rw-r--r-- 1 root root 4096 Jun 19 01:00 fifo_expire_sync
-rw-r--r-- 1 root root 4096 Jun 19 01:00 group_idle
-rw-r--r-- 1 root root 4096 Jun 19 01:00 low_latency
-rw-r--r-- 1 root root 4096 Jun 19 01:00 quantum
-rw-r--r-- 1 root root 4096 Jun 19 01:00 slice_async
-rw-r--r-- 1 root root 4096 Jun 19 01:00 slice_async_rq
-rw-r--r-- 1 root root 4096 Jun 19 01:00 slice_idle
-rw-r--r-- 1 root root 4096 Jun 19 01:00 slice_sync
-rw-r--r-- 1 root root 4096 Jun 19 01:00 target_latency



Vamos apresentar alguns desses parâmetros em breve.

CFQ (Completely Fair Queue)

O método CFQ (Completely Fair Queue ou fila completamente justa) tem o objetivo de distribuir igualmente largura de banda de I/O entre todos os processos que fazem requisições.

Teoricamente cada processo tem a sua própria fila de I/O, na prática, o número de filas é fixo (em 64) e um processo baseado tem tabelas hash usa o ID do processo para determinar qual fila utilizar.

Os pedidos que estão aguardando para serem atendidos são atendidos no estilo round robin (ou anel sequencial) e são atendidos na ordem FIFO (First In First Out ou primeiro a entrar primeiro a sair). Para evitar excesso de operações de consulta aleatórias, uma rodada inteira é reservada para enviar pedidos de consulta antes dos pedidos reais de I/O serem emitidos para o dispositivo.

HZ


HZ é uma medida usada pelo kernel como uma forma de medir tempo, que corresponde ao número de jiffies por segundo. Apenas aluns detalhes: HZ/2 equivale a 0.5 segundo e 5 * HZ equivale a 5 segundos etc.

    quantum
    Tamanho máximo da fila que deve ser atendida em uma rodada de serviço.  (Padrão = 4);
    
    queued
    Tamanho mínimo de alocação por fila.  (Padrão = 8)
    
    fifo_expire_sync
    Timeout do FIFO para requisições síncronas.  (Padrão = HZ/2)
    
    fifo_expire_async
    Timeout do FIFO para solicitações assíncronas.  (Padrão = 5  *  HZ)
    
    fifo_batch_expire
    Taxa em que os FIFOsexpiram.  (Padrão = HZ/8)
    
    back_seek_max
    Tamanho máximo dos seeks para trás em KB. (Padrão = 16K)
    
    back_seek_penalty
    Penalidade para seeks para trás.  (Padrão = 2)




 O scheduler de I/O Deadline reordena agressivamente solicitações com os objetivos simultâneos de melhorar o desempenho global e prevenir grandes latências para pedidos individuais; ou seja, limita o fenômeno chamado starvation que acontece quando processos ficam parados, aguardando que operações de I/O sejam concluídas.


O kernel associa um prazo a cada solicitação de I/O. Pedidos de leitura tem maior prioridade do que pedidos de escrita.


Cinco filas de I/O separadas são mantidas:

    Duas listas ordenadas são mantidas, uma para leitura e outra para escrita e são organizadas pelo primeiro bloco de cada solicitação.

    Duas listas FIFO são mantidos, uma para leitura e outro para escrita. Estas listas são classificadas pelo momento em que as solicitações foram feitas.

    Uma quinta fila contém os pedidos que estão aguardando para serem enviados para o controlador de disco. Essa é chamada de fila de envio.


Exatamente como os pedidos navegam pelas cinco filas é onde está a arte do algoritmo.



A lista de parâmetros para tuning do scheduler  Deadline são:


    read_expire:
    Quanto tempo (em milissegundos) no máximo uma solicitação de leitura leva para ser concluída. (Padrão = HZ/2 = 500 )
    
    write_expire:
    Quanto tempo (em milissegundos) no máximo uma solicitação de escrita leva para ser concluída. (Padrão = 5 * HZ = 5000)
    
    writes_starved:
    Quantas solicitações de leitura devem receber preferência sobre uma solicitação de escrita. (Padrão = 2 )
    
    fifo_batch:
    Quantas solicitações devem ser movidas da lista sorted scheduler para a fila de dispatch quando os prazos ainda não expiraram. (Padrão = 16)
    
    front_merges:
    Combinar com a anterior é mais comum do que combinar com a próxima, porque solicitações de grandes áreas contínuas costumam combinar com o próximo bloco. Configurar este parâmetro para 0 desliga o recurso combinar com a próxima, o que pode melhorar o desempenho se você souber que este recurso raramente será necessário. (Padrão = 1 )

Mais vistos no mês:

As melhores distribuições Linux para 2017

Teste de Performance de Rede com Iperf

TuxMath - Tux, do Comando da Matemática. Ensino e diversão a crianças.

Aula #14 - Os sistemas de arquivos ext2/ext3/ext4

Modelo Firewall Completo em Iptables para pequena rede/office

DHCP - Guia Completo

OPNsense - Firewall Open Source

SSD no linux

Administração de sistema e Deploys: Ansible, Chef, Fabric, Puppet ou Salt?

Oracle Linux 7.0 Server com Xfce - Instalação e configurações iniciais