• Nenhum resultado encontrado

5.3 Blocos básicos propostos

5.3.2 Bloco recurso

Dois modelos atômicos foram desenvolvidos para representar um processador e o sistema operacional que executa nele: (i) bloco recurso que suporta preempções de tarefas, e (ii) bloco recurso que não suporta preempções de tarefas. A Figura 5.5 apresenta a estrutura desses blocos, e a Figura 5.6 mostra a especificação do bloco recurso que suporta preempções de tarefas. Este bloco básico é usado para modelar um processador com um sistema operacional em que preempções estão autorizadas. Devido às similaridades desse modelo com o modelo em que preempções não são suportadas, o último é apresentado no Apêndice A. O bloco recurso possui uma porta de entrada e uma de saída para cada um dos k blocos tarefa associados ao bloco recurso (linhas 3 e 4 da Figura 5.6): “ jobin1”, ... , “ jobink” são as portas de entrada, e “ jobout1”,

... , “ joboutk” são as portas de saída. As portas de entrada e saída são usadas, respectivamente,

para receber novas cargas de trabalho e notificar os blocos tarefa sobre a finalização de sua respectiva carga de trabalho. O estado do modelo é representado por uma tupla que determina (linha 5): (i) qual carga de trabalho está sendo executada no momento (current job), (ii) o estado da fila de prioridades com as cargas de trabalho prontas para executar (priorityqueue), (iii) o tempo restante para uma mudança de contexto (so_overhead), ou seja, o tempo restante para o sistema operacional chavear de uma tarefa para outra.

O modelo faz uso das seguintes funções auxiliares: Insert(l, w), que insere na fila de prioridades l a carga de trabalho w; Top(l), que retorna a carga de trabalho de maior prioridade na fila l; Pop(l), que retorna e remove da fila de prioridades l a carga de trabalho de maior prioridade; e max(a, b), que retorna o maior argumento entre a e b. As seguintes constantes são usadas no modelo: context_switch_delay, que representa o overhead de uma mudança de contexto pelo sistema operacional (recordar que esse tempo foi informado durante a etapa de caracterização); e max_priority, que representa a prioridade máxima de uma carga de trabalho. Conforme visto na subseção anterior, apenas a carga de trabalho que representa a tarefa de tratamento de interrupções interprocessador pode receber esta prioridade.

Sempre que o bloco recurso recebe um evento de entrada (linha 7), isso significa que novas cargas de trabalho chegaram ao recurso. Quando isso acontece, as novas cargas de trabalho são inseridas na fila de prioridades (linhas 9 e 10 da Figura 5.6). Caso o bloco recurso não esteja ocioso no momento da chegada (linha 11), o tempo restante para finalizar a carga de trabalho em execução, assim como o tempo restante para uma mudança de contexto são atualizados (linhas 11-14). Em seguida, verifica-se se as novas cargas de trabalho provocarão uma mudança de contexto, isto é, se chegou uma carga de trabalho com maior prioridade que a carga sendo atualmente executada (linha 15). Caso se verifique que haverá mudança de contexto, então o tempo para mudança de contexto é reiniciado (linha 16). Finalmente, a carga de trabalho de

5.3. BLOCOS BÁSICOS PROPOSTOS 90

1 Resourcep= (X ,Y, S, δint, δext, δcon, λ ,ta) 2 Job= {exec_time ∈ R+, priority ∈ N, id ∈ N}

3 X= {(p, v) | p ∈ IPorts, v ∈ Xp} tal que IPorts ∈ {“ jobin1”, ... ,“ jobini” , ... ,“ jobink”}, Xjobin

i

∈ Job

4 Y = {(p, v) | p ∈ OPorts, v ∈ Yp} tal que OPorts ∈ {“1”, ... , i , ... ,“k”}, Yi∈ { “done”} 5 S= {current_ job ∈ Job ∪ {“nil”}, priorityqueue ∈ { job ∈ Job}∗, so_overhead ∈ R+} 6

7 s0= δext(s, e, xb):

8 s0:= s

9 foreach x in xbdo

10 Insert(s0.priorityqueue, x.v) // insere as novas cargas de trabalho

11 if s.current job 6=“nil” then // se o bloco recurso não está ocioso

12 s0.current job.exec_time := s0.current job.exec_time − max(e − s0.so_overhead, 0)

// atualiza o tempo restante para terminar a carga de trabalho atual

13 s0.so_overhead := max(e − s0.so_overhead, 0) // atualiza o tempo restante para finalizar uma mudança de contexto

14 s0.priorityqueue := Insert(s0.priorityqueue, s0.current job)

15 if s.current job 6= Top(s0.priorityqueue) then // verifica se haverá mudança de contexto 16 s0.so_overhead := context_switch_delay // acrescenta o tempo para mudança de

contexto

17 s0.current job := Pop(s0.priorityqueue) // seleciona e remove a carga de trabalho com maior prioridade na fila

18

19 σ = ta(s):

20 if s.current job 6=“nil” then

21 σ := s.current job.exec_time + s.so_overhead

22 else

23 σ := +∞ // recurso ocioso

24

25 yb= λ (s):

26 if current job.id 6= max_priority then // verifica se a carga de trabalho finalizada representa a tarefa de tratamento de mensagens interprocessador

27 y.p := current job.id, y.v :=“done”, yb:= {y} // avisa o bloco tarefa que a carga de trabalho enviada terminou

28

29 s0= δint(s):

30 s0.current job :=“nil”, s0.priorityqueue := s.priorityqueue

31 if s0.priorityqueue 6= () then // se a fila não estiver vazia

32 s0.current job := Pop(s0.priorityqueue) // inicia a execução da próxima carga de trabalho

33 s0.so_overhead := context_switch_delay // acrescenta o tempo para mudança de contexto

34

35 s0= δcon(s, xb):

36 s0:= δextint(s), 0, xb) // a carga de trabalho atual precisa ser finalizada primeiro

Figura 5.6: Modelo atômico P-DEVS que representa um processador com sistema operacional em que preempções estão autorizadas (bloco recurso com preempção).

5.3. BLOCOS BÁSICOS PROPOSTOS 91

maior prioridade, considerando as cargas antigas e as novas, é colocada para executar (linha 17). A função de avanço do tempo verifica se o bloco recurso precisa executar uma carga de trabalho (linhas 19 e 20 da Figura 5.6). Caso precise, o tempo de execução é definido como sendo o tempo restante para executar a carga de trabalho mais o tempo restante para mudança de contexto (linha 21). Caso contrário, o bloco recurso fica ocioso, aguardando algum evento externo chegar (linhas 22 e 23).

A função de saída (linha 25 da Figura 5.6) notifica um bloco tarefa que sua respectiva carga de trabalho enviada foi finalizada. Caso a carga de trabalho finalizada tenha sido um serviço de tratamento de mensagens interprocessador, então o bloco tarefa não precisa ser avisado (linha 26). A verificação é feita checando se a carga de trabalho tem prioridade máxima, uma vez que uma carga de trabalho comum não pode assumir essa prioridade. Como o recurso possui uma porta de saída para cada bloco tarefa, a porta correta é selecionada a partir do identificador da carga de trabalho (linha 27).

Quando a função de transição interna é chamada (linha 29 da Figura 5.6), isso significa que o bloco recurso finalizou a execução de uma carga de trabalho. Nesse caso, a função verifica se a fila de cargas de trabalho está vazia (linha 31), caso não esteja, o bloco recurso é configurado para iniciar a execução da próxima carga de trabalho (linhas 32 e 33).