Os sistemas embebidos comuns apresentam elevado nível de interação com o mundo exterior, possuindo diversos dispositivos de I/O, tais como, sensores, atuadores, discos, interfaces de comunicação, etc. Estes dispositivos participam nas diferentes operações, que, na maioria dos casos, apresentam requisitos de tempo real rigo- rosos. Os eventos físicos originados pelos dispositivos periféricos são direcionados para o sistema computacional através das interrupções.
Uma das principais tarefas do sistema operativo consiste na gestão eficaz do fluxo de execução, que deve ser conseguida na presença de tarefas executadas de forma síncrona e atendimento às interrupções, cuja ocorrência é assíncrona. Nos sistemas computacionais de uso geral, as tarefas não tem requisitos temporais rigorosos ao contrário das interrupções que, por norma, são associadas às operações restringi- das temporalmente, exigindo eficácia e baixas latências. Sendo assim, nos siste- mas operativos tradicionais, o escalonamento e a sincronização destes dois tipos de atividades (tarefas e interrupções) são abordados recorrendo aos mecanismos e as estratégias independentes e semanticamente e sintaticamente distintos. As tarefas, nestes sistemas, são geridas pelo kernel e escalonadas conforme a sua prio- ridade, enquanto as interrupções são geridas pelo hardware dedicado (controlador das interrupções) e escalonadas também conforme a sua prioridade. Tanto o es- calonamento de tarefas como interrupções baseia-se nas prioridades, no entanto, devido à natureza distinta da implementação, trata-se de dois espaços de prio- ridades independentes, sendo que a precedência é sempre dada às interrupções. Esta arquitetura tradicional endereça de forma eficaz o requisito de baixas latên- cias para eventos de I/O mas origina um conjunto de potenciais problemas que a tornam inadequada no contexto de sistemas de tempo real. Um dos potenciais problemas é a inversão das prioridades, que pode surgir no sistema de tempo real na presença de uma tarefa de alta prioridade que tem exigências temporais mais rigorosas do que algumas interrupções do mesmo sistema. Visto que, a arquitetura tradicional não permite atribuir a esta tarefa uma prioridade superior do que às interrupções de baixa prioridade, esta será inutilmente perturbada pelas interrup- ções relacionadas com as tarefas de baixa prioridade, comprometendo as garantias temporais do sistema. Um outro problema consiste na elevada complexidade e certas inconsistências do kernel causados pelas naturezas distintas da implemen- tação da sincronização de tarefas e interrupções. Endereçamento destes problemas serviu de motivação para vários estudos, que propuseram a unificação de espaço de
prioridades e mecanismos de sincronização de tarefas e interrupções como sendo solução gratificante para os sistemas de tempo real (Leyva-del Foyo, 2006), (Hofer et al., 2009), (Scheler et al., 2009).
Nos sistemas tradicionais, o sinal enviado pelo controlador das interrupções com o intuito de interromper CPU é denominado IRQ (interrupt request), enquanto as rotinas que este desencadeia são denominadas ISRs (interrupt service routines). As interrupções são a maior fonte do jitter e imprevisibilidade nos sistemas computa- cionais. A razão para tal é a natureza assíncrona dos eventos externos e o requisito de baixas latências que é atingido comprometendo determinismo. Um outro pro- blema surge quando a computação associada à interrupção é extensa. Visto que a maioria dos sistemas implementam ISRs como atividades não preemptivas, uma ISR demorada vai comprometer o atendimento das restantes interrupções que po- dem ocorrer durante a sua execução. Este problema é solucionado por maioria dos sistemas modernos atendendo a interrupção em duas etapas denominadas “top-
half” e “bottom-half”. Onde a primeira etapa é efetivamente a rotina que atende
a interrupção e prepara a segunda etapa para ser executada a seguir, dependendo da disponibilidade do CPU. Dependendo do sistema operativo, “bottom-half” pode ser implementado de diferentes maneiras, mas a principal ideia é que esta rotina já pode ser interrompida e se aproxima mais à tarefa. No trabalho (Leyva-del Foyo, 2006) a integração dos mecanismos de sincronização e espaços de prioridade é atingida convertendo as interrupções em eventos de sincronização (“top-half” ) e implementando o corpo da ISR na forma de uma tarefa (denominada IST) igual às outras, escalonadas pelo kernel (“bottom-half” ). Essa forma de abstração elimina as diferenças entre as interrupções e as tarefas. Sendo assim, ponto de vista do sistema, existem apenas tarefas (algumas dos quais desencadeadas pelas interrup- ções), o que simplifica e torna mais consistente o kernel, tornando obsoleto certo conjunto das suas funcionalidades e mecanismos originado pela anterior heteroge- neidade. O trabalho (Leyva-del Foyo, 2006) compara o benefício de unificação do espaço de prioridades contra a desvantagem do aumento do overhead associado a atendimento às interrupções. Com base nos casos de utilização, que apresentam imprevisibilidade devido a inversão das prioridades no espaço de prioridades não integrado, foi demonstrado e provado matematicamente (para um subconjunto de casos) que a utilização do espaço de prioridades integrado, para além de tornar o sistema mais previsível, proporciona o aumento do limite de utilização deste. Por outro lado, o jitter associado ao processamento da etapa “top-half” do aten- dimento a interrupção permanece e não pode ser eliminado recorrendo às técnicas
em software que primeiro devem atender a interrupção antes de concluir se esta deve ser processada agora ou não. Para que esta fonte do jitter seja eliminada por completo os IRQs devem ser avaliados (“top-half” ) numa entidade externa ao CPU.
Nos trabalhos associados ao projeto SLOTH (Hofer et al., 2009) a abordagem para integração do espaço de prioridades já é radicalmente oposta. Em vez de transfor- mar interrupções em tarefas, todas as tarefas são implementadas como sendo ISRs tradicionais. A motivação dos autores consiste na delegação da responsabilidade pelo escalonamento tanto das tarefas como das interrupções ao controlador das in- terrupções existente nos dispositivos computacionais modernos. Foi estabelecido um conjunto dos requisitos que o controlador das interrupções deve cumprir para poder substituir quase por completo o escalonador em software. Estes requisitos são:
• Possibilidade de desencadear interrupção a partir de uma ISR ou tarefa (por
software);
• Possibilidade de parcialmente desativar interrupções estipulando uma prio- ridade threshold;
• Possibilidade de acoplar timer em hardware ao trigger de uma interrupção específica para implementar desta forma uma tarefa temporizada;
• Suporte simultânea de um número suficiente de interrupções externas. Assim, os autores selecionaram a plataforma Infineon-TriCore de uso comum na indústria automóvel e cujo controlador das interrupções suporta até 255 fontes de interrupções diferentes e cumpre os requisitos enumerados (Infieon Technologies, 2002). A implementação de todas as tarefas e interrupções como sendo ISRs que partilham uma única pilha tem as limitações em termos das funcionalidades comparando com os kernels tradicionais, no entanto, mantendo esta semântica, os autores conseguiram uma solução extremamente concisa (menos de 200 source
lines of code), robusta e de fácil verificação. O sistema operativo por eles concebido
está em conformidade com a classe BCC1 da especificação OSEK (OSEK Group, 2005), podendo ser estendida, ao custo de um overhead suportável (Hofer et al., 2011), para estar em conformidade com a classe ECC1. Especificação OSEK é uma norma que regula sistemas operativos embebidos e que é adotada pela indústria automóvel. A solução usufrui das vantagens da integração do espaço de prioridades e explora as potencialidades do hardware, superando assim, em termos de níveis de
latências e determinismo, as soluções comercializadas que estão em conformidade com a mesma norma.
Como já foi referido, quando a integração do espaço de prioridades vai pelo caminho de transformação das interrupções em tarefas, o jitter e indeterminismo associados à interrupção especulativa do CPU, necessária para processamento “top-half” dos IRQs, não podem ser eliminados por completo recorrendo as técnicas em software. Idealmente, a avaliação e escalonamento do IRQ deve ocorrer numa entidade inde- pendente do CPU e sem a sua interrupção. Para que seja possível integrar ISR no contexto do escalonador do kernel (que escalona tarefas) sem interromper CPU, o dito escalonador também deve encontrar se numa entidade desacoplada do CPU, ou seja, deve ser implementado como sendo coprocessador em hardware. Um exem- plo do microkernel em hardware com a integração do espaço de prioridades pode ser encontrado no projeto hthreads (Andrews et al., 2005). O módulo do micro-
kernel denominado CBIS (CPU Bypass Interrupt Scheduler ) tem a capacidade de
guardar em hardware a associação entre uma fonte de interrupção externa e uma tarefa do escalonador, o que é semelhante a assinatura do determinado handler ao determinado IRQ no sistema tradicional. Essa informação é posteriormente usada para poder traduzir um evento assíncrono da interrupção num evento assíncrono do escalonamento, direcionado para escalonador baseado em hardware sem passar pelo CPU. Isto possibilita o escalonamento das interrupções em paralelo com a execução da aplicação do sistema, eliminando assim por completo, a necessidade do processamento “top-half” (ponto de vista do CPU) e o respetivo overhead com- putacional. Ao mesmo tempo, a parte “bottom-half” do atendimento à interrupção é levada ao domínio do sistema operativo e pode usufruir das funcionalidades e flexibilidades que este proporciona às tarefas normais.
Visto que, o trabalho desenvolvido no âmbito desta dissertação implementa hard-
ware microkernel, é possível integrar o espaço de prioridades das tarefas e inter-
rupções seguindo abordagem apresentada no projeto hthreads. O modelo imple- mentado é em tudo parecido mas, no entanto, tem as suas particularidades. Os detalhes da implementação são apresentados na Secção 5.5.3.