Buscar

Resumo Software em Tempo Real

Prévia do material em texto

3.3 - Laço Principal com Tratadores de Interrupções 
Laço Principal com Tratadores de Interrupções, o executivo cíclico é uma forma 
simples e efetiva de resolver o problema de escalonamento quando todas as tarefas são 
periódicas e possuem um deadline relativo igual ao período. Para sistemas onde existem 
tarefas esporádicas, especialmente com deadlines relativos menores que o intervalo 
mínimo entre chegadas, um design mais eficiente para o sistema é não usar um executivo 
cíclico puro, mas sim um Laço Principal com Tratadores de Interrupções. Desta 
forma, todas as atividades que não são de tempo real, ou são periódicas com um deadline 
relativo igual ao período, são colocadas no laço principal que executa periodicamente. Se 
assume implicitamente que as tarefas no laço ou não possuem deadline ou possuem um 
deadline relativo maior ou igual ao período do laço. 
Também se assume implicitamente que o período de tais tarefas é igual ao período 
do laço, pois elas são executadas uma vez dentro do laço. É possível acomodar facilmente 
tarefas com períodos múltiplos inteiros do período do laço, basta colocar um contador 
que é incrementado a cada execução do laço e executar a tarefa em questão a cada X 
execuções do laço principal. Soluções mais complexas para o código do laço principal 
podem ser pensadas. 
Na solução baseada em laço principal com tratadores de interrupções, as tarefas 
de tempo real mais exigentes são implementadas como tratadores de interrupção. Todos 
os tratadores de interrupções têm prioridade sobre as tarefas do laço principal, salvo por 
aqueles momentos onde interrupções são desabilitadas. Isto pode ocorrer quando tarefas 
do laço principal precisam acessar variáveis globais compartilhadas com os tratadores de 
interrupções. Em qualquer caso, estas atividades mais urgentes executarão mais 
rapidamente, não precisando esperar pelas tarefas que agora formam o laço principal. 
Caso o laço principal seja periódico, é importante observar que o período do laço 
principal deve comportar não somente o tempo de execução no pior caso de todas as 
tarefas implementadas no laço, mas também o tempo de execução no pior caso dos 
tratadores de interrupções, considerando-se todas as interrupções que podem ocorrer 
durante um ciclo do laço. 
3.4 Microkernel Simples como Sistema Operacional Multitarefa 
Quando o kernel oferece apenas serviços básicos de gerência do processador e não 
inclui outros serviços, tais como sistema de arquivos ou gerência de memória 
sofisticada, ele é chamado de microkernel. A técnica conhecida como multiprogramação 
é empregada para criar a abstração thread, a partir da divisão do tempo do 
processador. Desta forma, acima do microkernel existem threads as quais executam o 
código de aplicação. 
O microkernel cria a ilusão de que as threads executam 
simultaneamente, enquanto na verdade elas se revezam no uso do processador, sob o 
controle do microkernel. 
Tarefas de tempo real são implementadas como threads, cada thread 
representando um fluxo de execução distinto, o qual é composto basicamente pelo 
conteúdo dos registradores do processador. 
3.4.1 Chamadas de Sistema 
Threads solicitam serviços ao microkernel através de chamadas de 
sistema. Tipicamente, chamadas de sistema são implementadas como interrupções de 
software cujo tratador faz parte do microkernel. 
Chamadas de sistema são sinalizadas através da execução de uma instrução do 
tipo interrupção de software. Cada thread representa um fluxo de execução independente. 
Quando uma thread da aplicação requer um serviço do microkernel e faz uma 
chamada de sistema na forma de uma interrupção de software, neste momento, o 
microkernel entra em execução, atendendo esta chamada de sistema. Se ela pode ser 
atendida imediatamente, isto é feito e a thread retoma sua execução. 
Na multiprogramação, quando uma thread não pode continuar a executar pois está 
esperando por algum evento, o que o microkernel faz é colocar outra thread para 
executar. A maioria dos sistemas inclui uma thread ociosa, a qual executa quando não 
existe nenhuma outra thread para executar, apenas para evitar que o microkernel fique 
sem threads para executar. O microkernel não faz nada até que ocorra um evento. 
3.4.2 Estados de uma Thread 
Threads podem ser criadas e destruídas dinamicamente, através de chamadas de 
sistema realizadas por threads já existentes. É claro que a primeira thread precisa ser 
criada na inicialização do microkernel, pois neste momento ainda não existe ninguém 
para fazer chamadas de sistema. No caso de um microkernel também é possível que todas 
as threads sejam criadas automaticamente, apenas na inicialização do sistema. 
A thread executa instruções de máquina até o ponto onde algum serviço é 
requerido do microkernel através de uma chamada de sistema. Normalmente tais 
atividades são demoradas se comparadas com a velocidade do processador e a thread em 
questão ficará bloqueada, liberando o processador para ser usado por outra 
thread. Mesmo no caso de bloqueio, após algum tempo, a ação solicitada pela thread é 
concluída. 
Neste momento a thread que estava bloqueada é liberada e poderá retornar para a 
fila de aptos onde novamente irá disputar o tempo do processador com as demais threads 
na fila. A forma tradicional de uma thread terminar é a mesma solicitar isto ao 
microkernel através de uma chamada de sistema. Entretanto, existe a possibilidade de 
uma thread ser destruída por outra thread através de chamada de sistema. Também é 
possível a destruição de uma thread caso a mesma cometa um erro crítico que gere uma 
interrupção de proteção. 
Quando uma thread torna-se apta, cabe ao escalonador decidir se a thread em 
execução prossegue com o processador, ou se a mesma deve ceder lugar para a thread 
recém tornada apta, por uma questão de prioridades entre as threads, por exemplo. 
3.4.3 Chaveamento de Contexto entre Threads 
Na multiprogramação, threads que estão executando são por vezes interrompidas 
e mais tarde continuadas. Ela é suspensa, o tratador de interrupção é executado, talvez 
uma outra thread de alta prioridade seja liberada pela chegada de um pacote de dados pela 
rede e passe a executar. Somente no futuro a thread interrompida será novamente 
escolhida pelo escalonador e colocada para retomar sua execução. 
A operação de suspender a thread em execução e colocar uma outra thread para 
executar é chamada de troca de contexto. Para que uma thread possa passar a 
executar, ocupando o processador, é necessário recarregar o seu contexto. Quando a 
thread é interrompida, o conteúdo dos registradores precisa ser salvo. No futuro, quando 
a thread retoma sua execução, este mesmo conteúdo é recolocado nos registradores, de 
tal forma que a thread não perceba que sua execução foi temporariamente interrompida, a 
não ser por um lapso de tempo no relógio de tempo real. 
O microkernel precisa manter as informações relativas ao contexto de execução 
de cada thread, de forma a efetuar as cargas e os salvamentos de contexto quando 
necessário. Para isto o microkernel mantém uma estrutura de dados chamada de Bloco 
Descritor de Thread (TCB - Thread Control Block). As informações específicas de cada 
thread são mantidas no seu respectivo TCB, e são constituídas basicamente pelos 
conteúdos dos registradores quando a thread não estiver executando, além de informações 
complementares tais como um número Identificador Da Thread (TID – Thread 
Identification) e a prioridade da thread usada pelo escalonador para definir a ordem de 
execução. Tipicamente as filas de threads esperando por recursos são representadas 
dentro do microkernel como listas encadeadas dos respectivos TCBs. 
Somente quando uma thread nova é criada, um contexto inicial de execução é 
definido para ela pelo microkernel. Depois disto, a thread executa, altera os 
registradores, seu contexto de execução é sempre salvo e posteriormentereposto. 
3.4.4 Design do Microkernel 
O microkernel é um programa de computador, normalmente escrito na linguagem 
C, com algumas pequenas partes em linguagem de montagem. Quando o computador é 
energizado ou reinicializado o microkernel começa a executar e pode inicializar as suas 
estruturas de dados. A partir deste momento, não é o microkernel que executa, mas as 
threads da aplicação. O microkernel não faz nada até que ocorra um evento, ele reage ao 
evento, e depois volta a não fazer nada. Os eventos que acionam o microkernel são as 
interrupções. No caso de uma chamada de sistema, a primeira coisa a fazer é «salvar o 
contexto» da thread executando, pois quando ela voltar a executar no futuro, ela deverá 
encontrar os mesmos valores nos registradores do processador, com exceção de algum 
resultado da própria chamada de sistema retornado em registrador. Isto é feito copiando 
tais valores para os campos apropriados no TCB da thread em questão. Em seguida, o 
microkernel deve «identificar a chamada» feita. Algumas chamadas de sistema tem um 
«retorno imediato», tais como «obtem a hora atual» ou «obtem a versão do microkernel». 
Uma vez concluída a chamada, o «escalonador» é chamado para decidir que 
thread executará em seguida. 
O microkernel «carrega o contexto» da thread escolhida a partir do seu TCB para 
os registradores do processador. Várias chamadas de sistema não podem ser concluídas 
imediatamente, levando a thread chamadora a um estado de bloqueado. No caso de «não 
haver retorno imediato» é necessário «retirar a thread chamadora da fila de aptos» e 
«inserir esta thread chamadora na fila do periférico em questão». A manutenção da fila 
do periférico é tipicamente feita pelo devicedriver daquele periférico. O mesmo também 
é responsável por “enviar comandos” ao controlador do periférico, quando isto for 
necessário. Uma vez que o device-driver concluiu sua execução, não há nada mais a fazer, 
senão esperar pelo periférico trabalhar. Neste momento, o “escalonador” é chamado, ele 
escolhe uma nova thread para executar. No caso de uma exceção, a resposta do 
microkernel vai depender da seriedade da violação de proteção que ocorreu. 
No caso da «chamada de sistema ter sido concluída», é necessário «retirar a 
respectiva thread da fila do periférico». Esta thread passa do estado de bloqueada para o 
estado de apta e, portanto, é necessário «inserir a thread na fila de aptos». Como 
sempre, uma vez concluídas as ações do device-driver, o «escalonador» é chamado para 
escolher a próxima thread e o microkernel «carrega o contexto» desta thread.

Continue navegando