Regras do Contexto de Interrupção em Kernel Space

kernel-space
kernel-space

No que tange desenvolvimento em Kernel Space do Linux e concorrência1, existe um volume enorme de técnicas e procedimentos necessários para evitar as malditas condições de concorrência. Neste artigo apresentamos as particularidades ao lidar com concorrência em interrupções e três maneiras de compartilhar recursos entre interrupções e threads.

 

Aprenda mais sobre Linux Embarcado e Yocto Project com os vídeos das palestras do "Seminário Linux Embarcado 2015"

 

Diferentemente do restante do kernel, o contexto de interrupção é atômico2, ou seja, seu código é executado em um modo especial no processador em alta prioridade onde não existe processos, threads e nem escalonador3, assim, interrupções jamais poderão dormir4 nem ficar em longos períodos de espera. Elas existem para atender uma requisição proveniente de um hardware e as vezes de software, com baixa latência e curta duração.

 

Ao escrever código para rodar em interrupções deve-se saber que diversos mecanismos do kernel (mutex e cia, kmalloc, schedule, copy_to(from)_user, etc.) devem ser usados considerando duas ressalvas:

 

  • a interrupção não pode chamar nenhuma função que durma sob o risco de causar comportamento indefinido, uma vez que não existe processo para escalonar;
  • interrupções não podem aguardar por travas, sob pena de causar dead locks5, já que a interrupção não pode ser removida de contexto, o processador jamais vai ter a oportunidade de executar a thread que possui a trava que ocasionou o travamento da interrupção.

 

Em resumo, não é permitido chamar qualquer função que:

 

  • lide com user space: copy_to_user, copy_from_user, etc;
  • lide com paginação de memória: kmalloc(GFP_KERNEL), alloc_page, etc;
  • durma: down, wait_for_completion, sleep, etc.

 

Quando o código de uma interrupção necessita acessar uma estrutura de dados compartilhada, é importante gerenciar o acesso usando spin_lock_irqsave e spin_lock_irqrestore. Primeiramente porque a família spin_lock não dorme, suas esperas são implementadas com loops do tipo while(1) e podem ser usadas seguramente dentro de interrupções, e, segundo, especificamente as versões _irqsave e _irqrestore desligam (e religam depois) as interrupções, garantindo que nenhuma ocorra enquanto uma thread tiver acesso à trava. Este mecanismo que desliga as interrupções é importante, porque, já que o código de interrupção não pode esperar, ela não deve ocorrer de maneira alguma enquanto uma trava compartilhada estiver habilitada. Esta técnica posterga a execução de todas as interrupções do sistema, aumentando a latência das mesmas, e por isso deve ser mínimo o tempo em que a trava é mantida.

 

Depois, se a memória compartilhada entre threads e interrupções for de apenas uma variável, é possível usar a família de funções atomic_, definida no arquivo <asm/atomic.h>. Essas funções são seguras por executarem em contexto atômico (onde não pode ser interrompido) evitando qualquer problema de concorrência no uso compartilhado dessas variáveis.

 

Por fim, a última opção para lidar com memória compartilhada apresentada neste artigo serve para compartilhar stream de dados. Trata-se do uso da kfifo (<linux/kfifo.h>), uma FIFO que não precisa de controle de acesso quando operada sob único consumidor e único produtor, algo que é característico de algoritmos de FIFOs.

 

Vale lembrar que tasklets6 ocorrem dentro de interrupções (de software no caso), e todas as regras apresentadas neste artigo também se aplicam a eles.

 

 

Notas

 

1 Quando duas, ou mais threads ou interrupções, compartilham o acesso a um mesmo recurso, ocorre em sistemas multi-thread, interrupções e processadores multi-core.
2 Código que não pode ter sua execução interrompida.
3 Principal mecanismo de sistemas operacionais, responsável por gerenciar o tempo de processador dispendido em cada thread.
4 Perder o uso do processador temporariamente por solicitação de software, trocar de contexto.
5 Quando duas ou mais threads ou interrupções ficam impossibilitadas de rodar por estarem aguardando em uma trava que erroneamente jamais será libertada.
6 Mecanismo de agendar a execução de uma função no futuro. Útil para executar processamento postergado.

 

Referências

 

 Linux Device Drivers, 3rd Edition - O'Reilly Media - Capítulo 5 e 10.

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Felipe Lavratti
Engenheiro, desenvolve software para embarcados e Linux, evangelista dos processos de qualidade. Mais informações e redes sociais: http://flp.lv

Deixe um comentário

1 Comentário em "Regras do Contexto de Interrupção em Kernel Space"

avatar
 
  Notificações  
recentes antigos mais votados
Notificar
Matheus Oleiro
Visitante
Matheus Oleiro

Sugiro um post sobre alocação de memória, limites de memória alocada com um kmalloc, etc..
Capítulo 8, Linux Device Drivers, 3rd Edition – O’Reilly Media.