Organizacao de Escalonadores de Processo
Organizacao de Escalonadores de Processo
Organizacao de Escalonadores de Processo
FACULDADE DE CIÊNCIAS ‐ CAMPUS DE BAURU
BACHARELADO EM SISTEMAS DE INFORMAÇÃO
SISTEMAS OPERACIONAIS I
CARLOS EDUARDO CIRILO
DANILO BALZAQUE SILVEIRA NETTO JÚNIOR
JORGE HENRIQUE DE BARROS ASSUMPÇÃO
Organização de Escalonadores de Processo
Bauru
2007
CARLOS EDUARDO CIRILO
DANILO BALZAQUE SILVEIRA NETTO JÚNIOR
JORGE HENRIQUE DE BARROS ASSUMPÇÃO
Organização de Escalonadores de Processo
Monografia apresentada à disciplina Sistemas
Operacionais I do curso de Bacharelado em Sistemas de
Informação da Faculdade de Ciências – campus de Bauru
– da Universidade Estadual Paulista “Júlio de Mesquita
Filho”.
Bauru
2007
“Se um homem tem um talento e não tem capacidade de usá‐lo, ele fracassou. Se ele
tem um talento e usa somente a metade deste, ele fracassou parcialmente. Se ele tem
um talento e de certa forma aprende a usá‐lo em sua totalidade, ele triunfou
gloriosamente e obteve uma satisfação e um triunfo que poucos homens conhecerão“.
Thomas Wolfe
RESUMO
Figura 2.4. Utilização da UCP como uma função do num. de proc. na memória 50
Figura 2.10. Algoritmo de escalonamento com quatro classes de prioridade 61
Figura 2.13. Escalonador de processo‐padrão do Unix 75
Figura 2.14. Escalonador por fração justa 76
LISTA DE TABELAS
LISTA DE SIGLAS
BCP Bloco de Controle de Processo
E/S Entrada/Saída
FCFS First In First Serv (Primeiro a Entrar Primeiro a Ser Servido)
FIFO First In First Out (Primeiro a Entrar Primeiro a Sair)
FSS Fair Share Scheduling (Escalonamento por Fração Justa)
HRRN Highest Response Ratio Next (Próxima Taxa de Retorno Mais Alta)
I/O Input/Output
PEB Process Enviroment Block (Bloco de Ambiente de Processo)
RR Round Robin (Alternância Circular)
SJF Shortest Job First (Job Mais Curto Primeiro)
SPN Shortest Process Next (Próximo Processo Mais Curto)
SRT Shortest Remaining Time (Menor Tempo de Execução Restante)
SO Sistema Operacional
TEB Thread Enviroment Block (Bloco de Ambiente de Thread)
TLS Thread Local Storage (Armazenamento Local do Thread)
UCP Unidade Central de Processamento
SUMÁRIO
INTRODUÇÃO .............................................................................................................................. 11
1. O ESCALONAMENTO DE PROCESSOS ...................................................................................... 13
1.1 Breve Histórico .................................................................................................................. 13
1.2 O Processo: conceito, definições, estados e comportamento .......................................... 14
1.3 Quando escalonar ............................................................................................................. 23
1.4 Escalonamento preemptivo versus escalonamento não preemptivo .............................. 24
1.5 Filas de escalonamento ..................................................................................................... 25
1.6 Níveis de Escalonamento .................................................................................................. 27
1.7 Prioridades ........................................................................................................................ 31
1.8 Trocas de contexto ............................................................................................................ 32
2. ALGORITMOS DE ESCALONAMENTO ....................................................................................... 33
2.1 Categorias de algoritmos de escalonamento .................................................................... 33
2.2 Objetivos do Escalonamento ............................................................................................. 34
2.3 Critérios de Escalonamento .............................................................................................. 36
2.4 Escalonamento em Sistemas em Lote ............................................................................... 39
2.4.1 Escalonamento primeiro a entrar primeiro a sair (FIFO) ........................................... 39
2.4.2 Escalonamento por job mais curto primeiro (SJF) ..................................................... 42
2.4.3 Escalonamento por próxima taxa de resposta mais alta (HRRN)............................... 46
2.4.4 Escalonamento por menor tempo de execução restante (SRT) ................................ 47
2.4.5 Escalonamento em três níveis .................................................................................... 48
2.5 Escalonamento em sistemas interativos ........................................................................... 51
2.5.1 Escalonamento Round Robin ..................................................................................... 51
2.5.2 Escalonamento por Prioridades ................................................................................. 58
2.5.3 Escalonamento por Múltiplas Filas ............................................................................ 63
2.5.4 Escalonamento por múltiplas filas com realimentação ............................................. 66
2.5.5 Próximo processo mais curto (shortest process next ‐ SPN) ...................................... 71
2.5.6 Escolamento Garantido .............................................................................................. 71
2.5.7 Escalonamento por Loteria ........................................................................................ 72
2.5.8 Escalonamento por Fração Justa (fair share scheduling ‐ FSS) .................................. 73
2.6 Escalonamento em sistemas de tempo real ..................................................................... 77
2.6.1 Escalonamento por prazo .......................................................................................... 77
2.6.2 Escalonamento de Tempo Real .................................................................................. 78
2.6.3 Algoritmos de escalonamento de tempo real estáticos ............................................ 83
2.6.4 Algoritmos de escalonamento de tempo real dinâmicos .......................................... 84
2.7 Escalonamento de threads ................................................................................................ 85
2.8 Escalonamento de Multiprocessadores ............................................................................ 87
2.9 Política versus Mecanismo ................................................................................................ 88
2.10 Avaliação de Algoritmos .................................................................................................. 89
2.10.1 Modelagem determinista ......................................................................................... 89
2.10.2 Modelo de filas ......................................................................................................... 91
2.10.3 Simulações ................................................................................................................ 92
2.10.4 Implementação ........................................................................................................ 93
3. ESTUDOS DE CASO .................................................................................................................. 95
3.1 Escalonamento no UNIX .................................................................................................... 95
3.2 Escalonamento no Minix ................................................................................................. 100
3.2.1 Código e comentários .............................................................................................. 103
3.3 Escalonamento no Windows XP ...................................................................................... 116
3.3.1 Organização de processos e trheads ........................................................................ 116
3.3.2 Escalonamento de threads ....................................................................................... 118
3.3.3 Estados de threads ................................................................................................... 119
3.3.4 Algoritmo de escalonamento de thread .................................................................. 120
3.3.5 Determinação de prioridades de threads ................................................................ 121
3.3.6 Escalonamento em multiprocessadores .................................................................. 123
CONCLUSÕES ............................................................................................................................. 125
REFERÊNCIAS ............................................................................................................................. 126
INTRODUÇÃO
Sem software, um computador é basicamente um inútil amontoado de metal. Com
software, um computador pode armazenar, processar e recuperar informações, exibir
documentos multimídia, pesquisar na internet e envolver‐se em muitas outras importantes
atividades que justificam o seu valor (TANENBAUM; WOODHULL, 2000, p. 17). O software de
computador pode ser dividido, grosso modo, em dois tipos principais: software de sistema,
conjunto de programas generalizados que gerenciam os recursos do computador, como
processador central, as conexões de comunicação e os dispositivos periféricos; e software
aplicativo, programas que são escritos para ou pelos usuários para designar uma tarefa
específica ao computador que o usuário realmente deseja (LAUDON, J.; LAUDON, K., 2003, p.
196‐197). O software de sistema mais fundamental é o sistema operacional (SO), que controla
todos os recursos do computador e fornece a base sobre a qual os programas aplicativos
podem ser escritos (TANENBAUM; WOODHULL, 2000, p. 17).
Os primeiros sistemas de computação só permitiam que um programa fosse executado
de cada vez. Esse programa tinha controle completo do sistema e acesso a todos os recursos
do sistema (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 63). Os computadores modernos
são capazes de fazer várias coisas ao mesmo tempo, permitindo que múltiplos programas
sejam carregados na memória e executados de forma concorrente. Enquanto executa um
programa de usuário, um computador pode também ler os dados de um disco e mostrar um
texto na tela ou enviá‐lo para uma impressora. É comum um computador de mesa (desktop)
compilar um programa, enviar um arquivo a um impressora, exibir uma página Web,
apresentar um videoclipe digital e receber mensagens de correio eletrônico concorrentemente
(DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 66; TANEMBAUM, 2003, p. 53).
Uma série de capacidades do SO habilita o computador a administrar muitas tarefas e
usuários diferentes ao mesmo tempo. A multiprogramação permite que múltiplos programas
compartilhem os recursos de um sistema computacional ao mesmo tempo por meio do uso
simultâneo de uma Unidade Central de Processamento (UCP)1. Na verdade, apenas um
programa está usando a UCP em dado momento, mas as necessidades de entrada/saída (E/S)
de outros programas podem ser atendidos ao mesmo tempo dando aos usuários a ilusão de
paralelismo (LAUDON, J.; LAUDON, K., 2003, p. 197‐198). Algumas vezes, nesse contexto, fala‐
se de pseudoparalelismo ou paralelismo lógico para diferenciá‐lo do verdadeiro paralelismo
de hardware dos sistemas multiprocessadores, os quais possuem duas ou mais UCPs que
compartilham simultaneamente a mesma memória física (TANEMBAUM, 2003, p. 53). Com o
1
Os termos UCP e processador serão utilizados indistintamente neste trabalho.
uso de tempo compartilhado (timesharing) o SO permite que muitos usuários compartilhem
os recursos de processamento concomitantemente de forma que a UCP gaste um período pré‐
determinado de tempo com um programa antes de passar para outro. A cada um dos
diferentes usuários é alocada uma minúscula fatia de tempo de computador em um programa;
durante esse tempo, cada um fica livre para executar quaisquer operações requisitadas. Ao fim
desse período, outra minúscula fatia de tempo da UCP é alocada a outro usuário. Esse arranjo
permite que muitos usuários permaneçam conectados à UCP simultaneamente, cada um
recebendo apenas uma pequena quantidade de tempo de computação (LAUDON, J.; LAUDON,
K., 2003, p. 197‐198).
Essa situação, onde variados programas competem pelo uso da UCP ao mesmo tempo,
ocorre sempre que dois ou mais processos estão simultaneamente no estado pronto para
execução. Se apenas uma UCP estiver disponível, deverá ser adotada uma estratégia –
denominada política de escalonamento de processador (ou disciplina de escalonamento) –
para decidir quais processos executar em determinado instante (DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R., 2005, p. 209). A parte do SO responsável por essa escolha é chamada de
escalonador de processos (ou escalador de processos), e o algoritmo que ele utiliza para
realizar essa tarefa é o algoritmo de escalonamento (TANEMBAUM, 2003, p. 97).
Uma política de escalonamento deve tentar satisfazer alguns critérios de desempenho
conforme os objetivos do sistema a que está vinculada, como maximizar o número de
processos que terminam por unidade de tempo (rendimento), minimizar o tempo que cada
processo espera antes de executar (latência), evitar adiamento indefinido de processos
(deadlocks), assegurar que cada processo conclua antes de seu prazo estabelecido, ou
minimizar a utilização do processador. Alguns desses objetivos, como maximizar a utilização e
o rendimento do processador são complementares; outros conflitam entre si – um sistema
que garanta que os processos terminarão antes de seus prazos pode não atingir o maior
rendimento possível (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 209).
A organização dos principais algoritmos de escalonamento, bem como suas políticas e
implicações nos sistemas são o tema central de discussão do presente trabalho.
CAPÍTULO 1
O ESCALONAMENTO DE PROCESSOS2
1.1 Breve Histórico
Entre as décadas de 1950 e meados da década de 1960, quando os sistemas de
processamento em lote passaram a ser utilizados nos computadores de segunda geração, o
algoritmo de escalonamento era bastante simples. As entradas de dados eram feitas
basicamente por meio de fitas magnéticas que continham os dados transcritos de cartões
perfurados. A tarefa do algoritmo era apenas executar os próximos jobs3 na seqüência em que
apareciam na fita (TANENBAUM; WOODHULL, 2000, p. 69; TANEMBAUM, 2003, p. 97). Com o
surgimento dos sistemas de tempo compartilhado na terceira geração de computadores, entre
1965 e 1980, e o advento da multiprogramação, o algoritmo de escalonamento tornou‐se mais
complexo à medida em que vários usuários passaram a requisitar serviços da UCP de forma
concorrente. Em muitos casos, em máquinas que mesclavam serviços em lote e de tempo
compartilhado, o escalonador tinha a responsabilidade de decidir se um job em lote ou um
usuário interativo em um terminal deveria ser atendido. Assim, observando a escassez de
tempo de UCP e o diferencial que o escalonador impactava no desempenho observado e na
satisfação do usuário, grande enfoque foi dado no desenvolvimento de algoritmos de
escalonamento inteligentes e eficientes (TANEMBAUM, 2003, p. 97).
A ascensão dos computadores pessoais (PC) a partir da década de 1980 trouxe uma
nova mentalidade no que diz respeito ao escalonamento de processos. Em um PC, na maior
parte do tempo existe apenas um processo ativo, o que faz com que o escalonador não precise
trabalhar muito para perceber qual processo executar. Além disso, com os avanços
tecnológicos no decorrer dos anos, os PCs tornaram‐se tão velozes que dificilmente a UCP
chegará a ser um recurso escasso. Na verdade, a maioria dos programas para PCs são limitados
não pela taxa na qual a UCP é capaz de processá‐los, mas sim pela velocidade com que o
usuário pode entrar com os dados que a aplicação necessita. Logo, observou‐se que o
escalonamento não era tão importante em PCs simples (TANEMBAUM, 2003, p. 97).
2
Na literatura, os termos escalonamento de processos e escalonamento de processador são usados como
equivalentes.
3
Neste trabalho, os termos job e processo serão utilizados quase que indistintamente. Apesar da tendência de
utilizar o termo processo, boa parte da teoria e terminologia sobre sistemas operacionais foi desenvolvida em uma
época na qual a principal atividade dos sistemas operacionais era o processamento de jobs. Seria errôneo evitar o
uso de termos comumente aceitos que incluem a palavra job (como escalonamento de jobs) simplesmente porque
processo suplantou job.
Por outro lado, em servidores e estações de trabalho de alto desempenho em rede,
onde é comum haver múltiplos processos competindo pela UCP, o escalonamento é crucial
para o desempenho percebido do sistema como um todo. Além de escolher o processo
adequado para executar, o escalonador também deve preocupar‐se com a utilização eficiente
da UCP, visto que as tarefas envolvidas na alternância de processos, como o salvamento e
recuperação de contexto, são dispendiosas e se forem realizadas muitas vezes
deliberadamente podem demandar uma grande quantidade de tempo da UCP, afetando o
desempenho geral do sistema (TANEMBAUM, 2003, p. 98).
1.2 O Processo: conceito, definições, estados e comportamento
Um obstáculo à discussão de sistemas operacionais é que existe dificuldade em
denominar todas as atividades da UCP. Um sistema em lote (bach) executa jobs, enquanto que
um sistema de tempo compartilhado executa programas de usuário ou tarefas. Mesmo em um
sistema monousuário, como o Microsoft Windows ou o Macintosh OS, um usuário pode
executar vários programas de uma vez: um processador de textos, um navegador Web e um
pacote de e‐mail. Mesmo se o usuário só puder executar um programa de cada vez, o sistema
operacional poderá precisar dar suporte a suas próprias atividades internas programadas,
como gerência de memória. Em muitos aspectos, todas essas atividades são semelhantes, de
modo que podem ser todas denominadas de processos (SILBERSCHATZ, A.; GALIN, P.; GAGNE,
G., 2000, p. 63).
O termo processo no contexto de sistemas operacionais foi usado pela primeira vez
pelos projetistas do sistema Multics na década de 1960 (DALEY; DENNIS, 1967 apud DEITEL, H.
M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 66) 4. Desde aquela época o termo processo, de
certo modo usado intercambiavelmente como tarefa, ganhou muitas definições como: um
programa em execução; uma atividade assíncrona; o “espírito animado” de um procedimento;
o “locus de controle” de um procedimento em execução; aquilo que é manifestado pela
existência de uma estrutura de dados denominada “descritor de processo” ou “bloco de
controle de processo” (BCP) no sistema operacional; aquela entidade às quais os
processadores são designados; e a unidade “de despacho” (DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R., 2005, p. 66).
Cada processo é representado no sistema operacional por um bloco de controle de
processo (BCP), também chamado de bloco de controle de tarefa. A figura seguinte mostra um
BCP.
4
DALEY, R. C.; DENNIS, Jack B. “Virtual memory, processes, and sharing in Multics”, Proceedings of the ACM
Symposium on Operating System Principles, jan. 1967.
estado do
ponteiro
processo
número do processo
contador do programa
Registradores
limites de memória
lista de arquivos abertos
.
.
.
Figura 1.1. Bloco de controle de processo
O BCP contém muitas informações associadas a um processo específico, incluindo:
• Estado do processo;
• Contador do programa;
• Registradores de UCP;
• Informações de escalonamento de UCP;
• Informações de gerência de memória;
• Informações de contabilização;
• Informações de status de E/S.
O BCP atua simplesmente como um repositório de informações que podem variar de
processo a processo (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 65).
Os softwares de computador – inclusive, algumas vezes, o próprio sistema operacional
‐ são organizados em vários processos seqüenciais. Nesse sentido, o processo é apenas um
programa em execução acompanhado dos valores atuais do contador de programa, dos
registradores e das variáveis (TANEMBAUM, 2003, p. 55). Cada processo tem seu próprio
espaço de endereço, que normalmente consiste em uma região de texto, uma região de
dados e uma região de pilha. A região de texto armazena o código que o processador executa.
A região de dados armazena as variáveis e memória alocada dinamicamente, que o processo
usa durante a execução. A região de pilha armazena instruções e variáveis locais para
chamadas ativas ao procedimento. O conteúdo da pilha cresce à medida que um processo
emite chamadas aninhadas ao procedimento, e diminui quando o procedimento chamado
retorna (PETERSON; QUARTERMAN; SILBERSCHATZ, 1985 apud DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R., 2005, p. 67)5.
Silberschatz, A., Galin, P.; Gagne, G. (2000, p. 64) enfatizam que um programa por si só
não é um processo; um programa é uma entidade passiva, como o conteúdo de um arquivo
armazenado em disco, enquanto um processo é uma entidade ativa, com um contador de
programa especificando a próxima instrução a ser executada e um conjunto de recursos
associados. Embora dois processos possam ser associados ao mesmo programa, são
considerados duas seqüências separadas de execução. Por exemplo, vários usuários podem
estar executando cópias diferentes do programa de correio ou o mesmo usuário pode chamar
muitas cópias do programa editor. Cada uma dessas atividades é um processos separado e,
embora as seções de texto sejam equivalentes, as seções de dados variam. Também é comum
ter um processo que produza muitos processos durante sua execução.
Em suma, a idéia principal é que um processo constitui uma atividade. Ele possui
programa, entrada, saída e um estado (TANENBAUM, 2003, p. 54). Durante seu tempo de vida
um processo passa por uma série de estados de processo distintos. Vários eventos podem
fazer que um processo mude de estado. Diz‐se que um processo está executando (ou seja, no
estado de execução) se estiver executando em um processador. Diz‐se que um processo está
pronto (ou seja, em estado “de pronto”) quando poderia executar em um processador se
houvesse algum disponível. Diz‐se que um processo está bloqueado (ou seja, no estado
bloqueado) se estiver esperando que algum evento aconteça (tal como um evento de
conclusão de E/S, por exemplo) antes de poder prosseguir (DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R., 2005, p. 67).
Em um sistema monoprocessado (ou uniprocessado), ou seja, como uma única UCP,
apenas um processo pode ser executado por vez, mas diversos outros podem estar prontos e
outros ainda bloqueados. O SO mantém uma lista de prontos, de processos prontos, e uma
lista de bloqueados, de processos bloqueados. A lista de prontos é organizada por ordem de
prioridade, de modo que o processo seguinte a receber o processador será o primeiro da lista
(o processo de maior prioridade). A lista de bloqueados é tipicamente desordenada – os
processos não se tornam desbloqueados (ou seja, prontos) na ordem de prioridade; são
desbloqueados na ordem em que ocorrem os eventos pelos quais estão esperando. Em alguns
5
PETERSON, J. L.; QUARTERMNA, J. S.; SILBERSCHATZ, A. “4.2BSD and 4.3BSD as examples of the UNIX system”, ACM
Computing Surveys, v. 17, nº 4, dez. 1985, p. 388.
casoss é comum p
priorizar os processos em
m espera (DEITEL, H. M.; D
DEITEL, P. J.;; CHOFFNES, D. R.,
2005, p. 68).
Figura 1.2. TTransições de estado de pro
ocessos
O ato dee designar um processsador ao prrimeiro proccesso da lissta de pron
ntos é
denominado desspacho, e é realizado por
p uma enttidade do sistema operracional chaamada
escalonador (ou despachantte). Diz‐se qu
ue processoss que estão nos estadoss de pronto ou de
orque disputtam ativamente tempo d
ução estão acordados po
execu de processad
dor. O SO gerrencia
transsições de esttado para melhor
m servir aos processsos no sistem
ma. Para evitar que quaalquer
um d
dos processo
os monopolizze o sistema, acidental o
ou maliciosamente, o sisstema operacional
estab
belece um re
elógio de intterrupção em
m hardware
e (também denominado
d temporizad
dor de
intervvalo) que peermite que o
o processo eexecute duraante um inteervalo de tem
mpo específiico ou
quan
ntum. Se o prrocesso não devolver o p
processador voluntariam
mente antes q
que o intervaalo de
po expire, o temporizad
temp dor de interrvalo gera uma interrup
pção, fazend
do que o sisstema
operaacional obtenha o controle do proceessador. Entãão o SO mud
da o estado do processo
o, que
estavva anteriorm
mente em exxecução, paraa pronto e despacha
d o primeiro pro
ocesso da lissta de
pronttos, mudand
do o seu esttado de pro m execução (DEITEL, H. M.; DEITEL, P. J.;
onto para em
CHOFFNES, D. R., 2005, p. 68). Muitos algoritmos vêm sendo desenvolvidos na tentativa de
equilibrar essa competição, que exige eficiência para o sistema como um todo e igualdade
para processos individuais (TANENBAUM, 2003, p. 58). Se um processo em execução iniciar
uma operação de E/S antes do seu quantum expirar e, conseqüentemente, tiver de esperar
que a operação de E/S seja concluída antes de poder usar o processador novamente, o
processo em execução entregará voluntariamente o processador. Nesse caso, diz‐se que o
processo bloqueou a si mesmo, deixando em suspenso a conclusão da operação de E/S. Diz‐se
que processos no estado bloqueado estão adormecidos porque não podem executar mesmo
que um processador fique disponível (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p.
68).
Dessa forma, observa‐se quatro estados de transição possíveis. Quando um processo é
despachado, ele transita de pronto para em execução (despacho). Quando o quantum de um
processo expira, ele transita de em execução para pronto (preempção). Quando um processo é
bloqueado, ele transita de em execução para bloqueado (bloqueio). Por fim, quando um
processo acorda devido à conclusão de algum evento pelo qual está esperando, ele transita de
bloqueado para pronto (despertar). Nota‐se que o único estado de transição iniciado pelo
próprio processo de usuário é o bloqueio – as outras três transições são disparadas pelo
sistema operacional (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 69) como
esquematizado na figura abaixo.
Figura 1.3. Estados de um processo e seus disparadores
Assume‐se, então, que o sistema operacional designa um quantum a cada processo.
Alguns sistemas operacionais mais antigos, que executavam com processadores que não
dispunham de relógios de interrupção, empregavam multitarefa cooperativa, o que significa
que cada processo deve devolver voluntariamente o processador no qual está executando
antes que outro processo possa executar. Entretanto, multitarefa cooperativa é raramente
usada nos sistemas operacionais modernos porque permite que processos monopolizem o
processador, acidental ou maliciosamente, entrando em laço infinito ou simplesmente
recusando‐se a entregar o processador na hora certa, por exemplo (DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R., 2005, p. 69).
Quanto mais complexo for um sistema operacional, mais benefícios aos usuários são
esperados. Embora sua principal preocupação seja a execução de programas de usuário, ele
também precisa cuidar das várias tarefas de sistema que são mais bem executadas fora do
núcleo (kernel) propriamente dito. Um sistema, portanto, consiste em uma coleção de
processos: processos de sistema operacional que executam código do sistema e processos de
usuário que executam código de usuário. Todos esses processos podem executar ao mesmo
tempo, sendo a UCP (ou UCPs) multiplexada(s) entre eles (SILBERSCHATZ, A.; GALIN, P.;
GAGNE, G., 2000, p. 63).
Essa visão dá origem ao modelo mostrado na figura seguinte. Nele, o nível mais baixo
do sistema operacional é o escalonador, com diversos processos acima dele. Todo o
tratamento de interrupção e detalhes sobre a iniciação e o bloqueio de processos estão
“ocultos” no escalonador, que, na verdade não possui muito código. O restante do SO é bem
estruturado na forma de processos (TANENBAUM, 2003, p. 58).
Figura 1.4. A camada mais inferior de um sistema operacional estruturado por processos trata as interrupções e o
escalonamento. A camada superior abriga os processos seqüenciais
O SO deve assegurar que cada processo receba uma quantidade suficiente de tempo
de processador. Em qualquer sistema, o número de processos verdadeiramente executados
em concorrência é obrigatoriamente igual ao número de processadores mas, em geral, há um
número muito maior de processos do que de processadores em um sistema. Portanto, a
qualquer dado instante, alguns processos podem ser executados e outros não, de forma que a
UCP troca, a todo momento, de um processo para outro dando a impressão de que o conjunto
de processos esteja sendo executado (pseudo)paralelamente (TANENBAUM, 2003, p. 53;
DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 67). Esse mecanismo de trocas rápidas é
chamado de multiprogramação e é ilustrado na figura abaixo.
Figu
ura 1.5. (a) Mu
ultiprogramaçção de quatro programas. (b
b) Modelo connceitual de qu
uatro processo
os seqüenciaiss
independeentes. (c) Som
mente um proggrama está ativo a cada moomento
Figgura 1.6. Seqü
üência de trocca de surtos de UCP e E/S
Quase todos os proceessos alternaam surtos de
e computaçãão com requ
uisições de E//S (de
disco
o) como ilusttrado na figu
ura acima. O
O sucesso do
o escalonam
mento da UC
CP depende dessa
proprriedade do processo em
m que sua execução
e u ciclo de processame
consiste em um ento e
espera por E/S. Os processo alternam entre esses dois estados. A execução de um processo
inicia‐se com um surto de UCP. A isso se segue um surto de E/S que, por sua vez, é seguido por
outro surto de UCP, depois por outro de E/S e assim por diante. Por fim, o último surto de UCP
termina com um pedido do sistema para encerrar a execução (SILBERSCHATZ, A.; GALIN, P.;
GAGNE, G., 2000, p. 95).
Figura 1.7. Surtos de uso da UCP alternam‐se com períodos de espera por E/S. (a) Um processo orientado à
UCP. (b) Um processo orientado à E/S
Em geral, a UCP executa indefinidamente e então é feita uma chamada ao sistema
para a leitura ou escrita de um arquivo. Após o término da chamada ao sistema, a UCP
computa novamente até que ela requisite ou tenha de escrever mais dados e assim prossegue
sucessivamente. Percebe‐se que algumas atividades de E/S contam como computação pois
fazem uso da UCP, como quando a UCP copia bits para uma RAM de vídeo a fim de fazer a
atualização da tela, por exemplo. E/S, nesse sentido, ocorrerá apenas quando um processo
entra no estado bloqueado aguardando que um dispositivo externo termine sua tarefa
(TANENBAUM, 2003, p. 98).
Observa‐se que alguns processos, como os da Figura 1.7(a), gastam a maior parte do
tempo computando e por isso são denominados de processos orientados à UCP (ou UCP‐
bound), enquanto outros, como o da Figura 1.7(b), esperam por E/S na maior parte das vezes e
são chamados de orientados à E/S (ou I/O‐bound). Os processos orientados à UCP geralmente
apresentam longos surtos de uso da UCP e esporádicas esperas por E/S, enquanto que os
processos orientados à E/S têm pequenos surtos de uso da UCP e esperas freqüentes por E/S.
Nota‐se que o fator principal é o tamanho do surto da UCP, não o tamanho do surto de E/S. Os
processos orientados à E/S não realizam muita computação entre uma requisição e outra da
UCP, não pelo fato de terem requisições por E/S demoradas. Na verdade, o tempo de leitura
de um bloco dee disco, por exemplo, é
é sempre o mesmo ind
dependentem
mente do quanto
demo
ore processaar os dados q
que chegam p
posteriormente (TANENB
BAUM, 2003
3, p. 98).
Figura 1.8. H
Histograma daas durações dee surto de UCP
P
Tanenbau
um (2003, p.
p 98) diz que
q eniente obsservar que, à medida que
é conve q a
ologia evoluii e as UCPs tornam‐se mais
tecno m rápidass e muito mais
m velozes que os disco
os, os
proceessos tendem
m a ficar mais
m dos à E/S e conseqüentemente o eescalonamen
orientad nto de
proceessos orientaados à E/S deeverá ser um
m assunto traatado com grrande importtância num ffuturo
próximo. Basicam
mente, a idéia é que se u
um processo orientado à E/S quiser eexecutar, devve ser
rapidamente dad
da a ele essa oportunidad
de, pois assim quisições de disco,
m ele executtará suas req
manttendo o disco
o ocupado e a UCP livre p
para executaar outros pro
ocessos.
1.3 Quando escalonar
Existe uma variedade de situações nas quais o escalonamento é necessário.
Primeiramente, quando se cria um novo processo, é preciso tomar uma decisão entre executar
o processo pai ou o processo filho. Como ambos os processos estão no estado pronto, essa é
uma decisão normal de escalonamento e pode levar à escolha de um ou de outro, ou seja, o
escalonador pode escolher legitimamente executar o pai ou o filho. Em segundo lugar, uma
decisão de escalonamento deve ser tomada ao término de um processo. Como o processo não
pode executar mais, algum outro processo deve ser selecionado entre os prontos. Se nenhum
processo estiver pronto, é executado um processo ocioso gerado pelo sistema. Em terceiro
lugar, quando um processo bloqueia para E/S, sobre um semáforo ou por alguma outra razão,
outro processo precisa ser escolhido para executar. O motivo do bloqueio pode algumas vezes
influenciar na escolha. Por exemplo, se A for um processo importante e estiver esperando B
sair de sua região crítica (RC), deixar B executar em seguida permitirá que ele saia de sua RC e
assim que A continue. Contudo, surge aí um problema, pois o escalonador, em geral, não
possui a informação necessária para considerar essa dependência. Em quarto lugar, quando
ocorre uma interrupção de E/S, pode‐se tomar uma decisão de escalonamento. Se a
interrupção vem de um dispositivo de E/S que acabou de fazer seu trabalho, o processo que
estava bloqueado, esperando pela E/S, pode agora ficar pronto para execução. Cabe ao
escalonador decidir se executa o processo que acabou de ficar pronto ou se continua
executando o processo atual (que estava executando no momento da interrupção) ou ainda se
escolhe um terceiro processo para executar (TANENBAUM, 2003, p. 99).
Se um hardware de relógio fornece interrupções periódicas de 50 Hz, 60 Hz ou alguma
outra freqüência, uma decisão de escalonamento pode ser tomada a cada interrupção de
relógio ou a cada k‐ésima interrupção de relógio. Os algoritmos ou disciplinas de
escalonamento podem ser classificados em duas categorias quanto ao modo como tratam
essas interrupções (TANENBAUM, 2003, p. 99). Uma disciplina é não preemptiva se, uma vez
que o sistema tenha designado um processador a um processo, não puder retirar aquele
processador daquele processo. Uma disciplina é preemptiva se o sistema puder retirar o
processador do processo que estiver executando. Sob uma disciplina de escalonamento não
preemptiva, cada processo, uma vez recebido um processador, executa até concluir ou até
devolver voluntariamente um processador (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005,
p. 211). Na verdade, nenhuma decisão de escalonamento é tomada durante as interrupções de
relógio. Após o processamento da interrupção de relógio ter findado, o processo que estava
executando antes da interrupção prossegue até acabar (TANENBAUM, 2003, p. 99). Sob uma
disciplina preemptiva de escalonamento, o processador pode executar uma parte do código de
um processo durante um tempo máximo fixado e então fazer um chaveamento de contexto
(DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 211; TANENBAUM, 2003, p. 99). Se
ainda estiver executando ao final desse intervalo de tempo, o processo será suspenso e o
escalonador escolherá outro processo para executar caso haja algum disponível (TANENBAUM,
2003, p. 99).
1.4 Escalonamento preemptivo versus escalonamento não preemptivo
O escalonamento preemptivo é útil em sistemas nos quais processos de alta prioridade
exigem resposta rápida. Em sistemas de tempo real, por exemplo, as conseqüências seriam
catastróficas caso as interrupções não fossem respondidas (ABBOT, 1984; RAMAMRITHAM;
STANOVIC, 1984; VOLZ; MUDGE, 1987; POTKONJAK; WOLF, 1999 apud DEITEL, H. M.; DEITEL,
P. J.; CHOFFNES, D. R., 2005, p. 211)6,7, 8, 9. Em sistemas interativos de tempo compartilhado, o
escalonamento preemptivo ajuda a garantir tempos de resposta aceitáveis ao usuário. A
preempção não deixa de ter um custo pois os chaveamentos de contexto incorrem em
sobrecarga. Para tornar a preempção efetiva, o sistema deve manter muitos processos na
memória principal, de modo que o processo seguinte esteja pronto quando um processador
tornar‐se disponível. Normalmente, apenas uma parte de cada processo está na memória
principal a qualquer instante e as partes menos ativas geralmente estão em discos (DEITEL, H.
M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 211).
Em sistemas não preemptivos, processos curtos podem sofrer longas esperas para
serem atendidos enquanto são concluídos processos mais longos, mas os tempos de retorno
são mais previsíveis, porque os processos de alta prioridade que chegam não podem desalojar
os processos que estão à espera. Como um sistema não preemptivo não pode retirar um
processo de um processador até que o processo conclua a execução, programas errantes que
nunca são concluídos (porque entraram em laço infinito) podem nunca entregar o controle do
sistema. Além disso, em um sistema não preemptivo, a execução de processos não
importantes pode fazer que processos muito importantes permaneçam esperando (DEITEL, H.
M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 211).
6
ABBOT, C. “Intervention schedules for real‐time programming”, IEEE Transactions on Software Engineering, v. SE‐
10, nº 3, maio 1984, p. 268‐274.
7
RAMAMRITHAM, K. ; STANOVIC, J. A. “Dynamic task scheduling in hard real‐time distributed systems”, IEEE
Software, v. 1, nº 3, jul. 1984, p. 65‐75.
8
VOLZ, R. A.; MUDGE, T. N. “Instruction level timing mechanisms for accurate real‐time task scheduling”, IEEE
Transactions on Computers, v. C‐36, nº 8, ago. 1987, p. 988‐993.
9
POTKONJAK, M.; WOLF, W. “A methodology and algorithms for the design of hard real‐time ultitasking ASICs”, ACM
Transactions on Design Automation of Electronic Systems (TODAES), v. 4, nº 4, out. 1999.
Para evitar que usuários monopolizem o sistema (acidental ou propositalmente), um
sistema preemptivo pode retirar o processador de um processo. Normalmente tal operação é
implementada instalando‐se um relógio de interrupção ou um temporizador de intervalo que
gere um interrupção periodicamente, permitindo que o sistema execute. Tão logo um
processador seja designado a um processo, esse executa até liberar voluntariamente seu
processador ou até ocorrer uma interrupção do relógio ou alguma interrupção. Então o
sistema pode decidir se o processo em execução deve continuar ou algum outro processo
“seguinte” deve executar (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 211).
O escalonamento preemptivo requer a existência de uma interrupção de relógio ao fim
do intervalo de tempo para que o controle sobre a UCP seja devolvido ao escalonador. Se não
houver relógio disponível, o escalonamento não preemptivo será a única opção (TANENBAUM,
2003, p. 99). Assim, o relógio de interrupção ajuda a garantir tempos de resposta razoáveis
para usuários interativos, evita que o sistema fique suspenso em um laço infinito do usuário e
permite que processos respondam a eventos que dependam de tempo. Processos que
precisam executar periodicamente dependem do relógio de interrupção (DEITEL, H. M.;
DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 211).
Segundo Deitel, H. M.; Deitel, P. J.; Choffnes, D. R. (2005, p. 212):
1.5 Filas de escalonamento
À medida que os processos entram no sistema, são colocados em uma fila de jobs.
Essa fila consiste em todos os processos do sistema. Os processos que estão residindo na
memória principal e estão prontos e esperando para executar são mantidos na fila de
processos prontos (ready queue). A fila dos processos prontos geralmente é armazenada como
uma lista encadeada. Um cabeçalho de fila de processos prontos contém ponteiros ao
primeiro e último BCPs na lista (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 66).
Existem também outras filas no sistema. Quando a UCP é alocada a um processo, ele
executa durante um tempo e é encerrado, interrompido ou espera pela ocorrência de
determinado evento, como a conclusão de um pedido de E/S, por exemplo. No caso de um
pedido de E/S, esse pedido pode ser para uma unidade de fita dedicada ou para um dispositivo
compartilhado, como um disco. Como existem muitos processos no sistema, o disco pode
estar ocupado com o pedido de E/S de algum outro processo. O processo, portanto, pode ter
de esperar pelo disco. A lista de processos esperando por determinado dispositivo de E/S é
chamada fila de dispositivo. Como ilustrado na figura abaixo, cada dispositivo tem sua própria
fila de dispositivo (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 66).
Figura 1.9. A fila de processos prontos e várias filas de dispositivos de E/S
Uma representação comum para uma discussão sobre escalonamento de processos é
um diagrama de filas, como o da Figura 1.9. Cada caixa retangular representa uma fila. Dois
tipos de fila estão presentes: a fila de processos prontos e um conjuntos de filas de dispositivo.
Os círculos representam os recursos que servem as filas e as setas indicam o fluxo de
processos no sistema (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 67).
Figura 1.10. Representação do diagrama de filas do escalonamento de processos
Um processo novo é colocado inicialmente na fila de processos prontos. Ele espera na
fila até ser selecionado para a execução ou ser submetido (dispatched). Depois que o processo
recebe a UCP e está em execução, um dos vários eventos a seguir poderá ocorrer:
• O processo pode emitir um pedido de E/S e ser colocado em
uma fila de E/S;
• O processo pode criar um novo subprocesso e esperar seu
término;
• O processo pode ser removido à força da UCP, como resultado
de uma interrupção e ser colocado de volta na fila de processos prontos.
Nos dois primeiros casos, o processo acaba alternando do estado de espera para o
estado de pronto e, em seguida, é colocado de volta na fila de processos prontos. Um processo
continua o seu ciclo até terminar e, nesse ponto, é removido de todas as filas, como seu BCP e
recursos sendo desalocados (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 67).
1.6 Níveis de Escalonamento
Um processo migra entre várias filas de escalonamento ao longo de toda sua vida. O
sistema operacional deve selecionar, para fins de escalonamento, os processos dessas filas de
alguma forma. O processo de seleção é executado pelo escalonador (scheduler) adequado.
Três níveis de escalonamento podem ser considerados conforme ilustra a Figura 1.11.
O escalonamento de alto nível, também denominado escalonamento de jobs ou
escalonamento de longo prazo determina quais jobs o sistema permite que disputem
ativamente os recursos do sistema. Denominado às vezes de escalonamento de admissão, ele
determina quais jobs são admitidos no sistema. Uma vez admitidos, os jobs são inicializados e
tornam‐se processos ou grupos de processos. A política de escalonamento de alto nível
determina o grau de multiprogramação, isto é, o número total de processos em um sistema
em dado instante (IBARAKI, T.; ABDEL‐WAHAB, H. M.; KAMEDA, T.; 1983 apud DEITEL, H. M.;
DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 209)10. A entrada de muitos processos em um sistema
pode causar a saturação de recursos do sistema ocasionando um mau desempenho. Nesse
caso, a política de escalonamento de alto nível pode decidir proibir temporariamente que
novos jobs entrem até que outros sejam concluídos (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D.
R., 2005, p. 209).
Figura 1.11. Níveis de escalonamento
Após a política de escalonamento de alto nível ter admitido um job (que pode conter
um ou mais processos) no sistema, a política de escalonamento de nível intermediário,
também denominado escalonamento de médio prazo, determina quais processos terão
permissão de concorrer por processadores. Essa política atende às flutuações de médio prazo
10
IBARAKI, T.; ABDEL‐WAHAB, H. M.; KAMEDA, T. “Design of minimum‐cost deadlock‐free systems”, Journal of the
ACM, v. 30, nº 4, out. 1983, p. 750.
da carga do sistema, atuando na suspensão e retomada de processos temporariamente de
forma a conseguir uma operação tranqüila do sistema e ajudar a cumprir certas metas de
desempenho no âmbito geral do sistema. O escalonador de nível intermediário funciona como
um buffer entre admissão de jobs no sistema e a designação de processadores a processos que
representam esses jobs (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 209). O
escalonador de nível intermediário aparece apenas em alguns sistemas operacionais, como os
de tempo compartilhado. Seu funcionamento está representado na Figura 1.12. A principal
idéia por trás dessa política de escalonamento é que às vezes pode ser mais vantajoso remover
processos da memória (e da disputa ativa por UCP) e, assim, reduzir o grau de
multiprogramação. Em algum momento posterior, o processo pode ser introduzido novamente
na memória e sua execução pode ser retomada do ponto onde parou. Esse esquema é
chamado swapping (troca). O escalonador de nível intermediário realiza as operações de
swapping. O swapping pode ser necessário para melhorar a combinação de processos ou
porque uma mudança nos requisitos de memória comprometeu a memória disponível,
exigindo a liberação de memória (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 68).
Figura 1.12. Diagrama de filas com o escalonamento de nível intermediário
Por fim, a política de escalonamento de baixo nível de um sistema, também chamada
de escalonamento de curto prazo ou escalonamento de UCP, determina quais processos
ativos o sistema designará a um processador quando o próximo ficar disponível. Em muitos
dos sistemas atuais, os únicos escalonadores são os de níveis baixo e intermediário (neste caso
a inicialização do job é realizada pelo escalonador de nível intermediário). Escalonadores de
alto nível normalmente são limitados a sistemas de grande porte (mainframes) que executam
processamento em lote (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 209). Por
exemplo, sistemas de tempo compartilhado, como o UNIX, geralmente não têm um
escalonador de alto nível, mas simplesmente colocam todo novo processo na memória para o
escalonador de baixo nível. A estabilidade desses sistemas depende de uma limitação física
(como o número de terminais disponíveis) ou da natureza de auto‐ajuste dos usuários
humanos. Se o desempenho cair para níveis inaceitáveis, alguns usuários simplesmente vão
desistir (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 68).
A principal distinção entre os escalonadores de alto e baixo nível é a freqüência da sua
execução. O escalonador de baixo nível deve selecionar um novo processo para a UCP com
freqüência. Um processo pode executar por apenas alguns milissegundos antes de esperar por
um pedido de E/S. Em geral, o escalonador de baixo nível executa pelo menos uma vez a cada
100 milissegundos. Devido à breve duração de tempo entre as execuções, o escalonador de
baixo nível deve ser rápido. Se levar 10 milissegundos para decidir executar um processo por
100 milissegundos, então 10/(100 + 10) = 9% da UCP está sendo usado (desperdiçado)
simplesmente para escalonar o trabalho (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p.
68).
O escalonador de alto nível, por outro lado, executa com muito menos freqüência.
Pode haver um intervalo de minutos entre a criação de novos processos no sistema. Como
visto, o escalonador de alto nível controla o grau de multiprogramação, isto é, o número de
processos na memória. Se o grau de multiprogramação for estável, a taxa média de criação de
processos deve ser igual à taxa média de partida de processos que saem do sistema. Assim, o
escalonador de alto nível pode precisar ser chamado apenas quando um processo sair do
sistema. Devido ao intervalo maior entre as execuções, o escalonador de alto nível pode levar
mais tempo para decidir que processos devem ser selecionados para execução (SILBERSCHATZ,
A.; GALIN, P.; GAGNE, G., 2000, p. 68).
É importante que o escalonador de alto nível faça uma seleção cuidadosa. Em geral, a
maioria dos processos podem ser escritos com orientados a E/S ou orientados a UCP. Um
processo orientado a E/S passa mais tempo realizando as operações de E/S do que efetuando
cálculos. Um processo orientado a UCP, em contrapartida, gera pedidos de E/S com pouca
freqüência, usando mais o seu tempo na computação. É importante que o escalonador de
longo prazo selecione uma boa combinação de processos incluindo processos orientados a E/S
e orientados a UCP. Se todos os processos forem orientados a E/S, a fila de processos prontos
quase sempre estará vazia e o escalonador de baixo nível terá pouco trabalho. Se todos os
processos forem orientados a UCP, a fila de espera de E/S ficará vazia a maior parte do tempo,
os dispositivos ficarão sem uso e mais uma vez o sistema ficará desequilibrado. O sistema com
o melhor desempenho terá uma combinação de processos orientados a E/S e orientados a UCP
(SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 68).
Muitas vezes as políticas de escalonamento de baixo nível atribuem uma prioridade a
cada processo, que reflete a importância desse processo – quanto mais importante for, maior
será a probabilidade de que a política de escalonamento o selecione para ser executado em
seguida. O escalonador de baixo nível (também denominado despachante) também designa
(despacha) um processador ao processo selecionado. O despachante funciona muitas vezes
por segundo e, portanto, deve residir na memória principal o tempo todo (DEITEL, H. M.;
DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 209).
Coffman e Kleinrock (1968 apud Deitel, H. M.; Deitel, P. J.; Choffnes, 2005, p. 210)
discutem políticas de escalonamento populares e indicam como usuários conhecedores de
qual política o sistema realmente usa podem conseguir melhor desempenho tomando
medidas apropriadas11. Ruschitzka e Fabry (1977 apud Deitel, H. M.; Deitel, P. J.; Choffnes,
2005, p. 210) apresentam uma classificação de algoritmos de escalonamento e formalizam a
noção de prioridade12.
1.7 Prioridades
Escalonadores muitas vezes usam prioridades para determinar como escalonar e
despachar processos. Prioridades podem ser designadas estaticamente ou mudar
dinamicamente. Elas quantificam a importância relativa dos processos. Prioridades estáticas
permanecem fixas, portanto, mecanismos baseados em prioridade estática são relativamente
fáceis de implementar e incorrem em sobrecarga relativamente baixa. Contudo, esses
mecanismos não são responsivos a mudanças no ambiente, mesmo os que poderiam
aumentar o rendimento e reduzir a latência. Por outro lado, mecanismos de prioridade
dinâmica são responsivos a mudanças. O sistema pode querer, por exemplo, elevar a
prioridade de um processo que esteja retendo um recurso importante que outro processo de
prioridade mais alta precisa. Após o primeiro processo devolver o recurso, o sistema baixa a
prioridade, para que o processo de prioridade mais alta possa executar. Esquemas de
prioridade dinâmica são mais complexos de implementar e têm sobrecargas maiores do que
esquemas estáticos. Na melhor das hipóteses a sobrecarga é justificada pelo aumento de
responsividade do sistema (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 212).
Em sistemas multiusuários, um sistema operacional deve fornecer serviços razoáveis a
uma grande comunidade de usuários, mas também deve atender a situações nas quais um
11
COFFMAN Jr., E. G.; KLEINROCK, E. L. “Computer scheduling methods and their dountermeasures”, Proceedings of
AFIPS, SJCC, v. 32, 1968, p. 11‐21.
12
RUSCHITZKA, M.; FABRY, R. S. “A unifying approach to scheduling”, Communications of the ACM, v. 20, nº 7, jul.
1977, p. 469‐477.
membro da comunidade de usuários precisa de tratamento especial. Um usuário que tenha
um job importante pode estar disposto a pagar um preço mais alto, isto é, comprar prioridade
para um serviço de nível mais alto. Essa cobrança extra é merecida, porque os recursos talvez
precisem ser retirados de outros clientes pagantes. Se não houvesse cobrança extra, todos os
usuários requisitariam o nível mais alto de serviço (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D.
R., 2005, p. 212).
1.8 Trocas de contexto
Alternar a UCP para outro processo requer salvar o estado do processo antigo e
carregar o estado salvo do novo processo. Essa tarefa é chamada de troca de contexto. O
contexto de um processo é representado no seu BCP; inclui o valor dos registradores de UCP,
o estado do processo e as informações de gerência de memória. Quando ocorre uma troca de
contexto, o kernel salva o contexto do processo antigo em seu BCP e carrega o contexto salvo
do novo processo escolhido para execução. O tempo de troca de contexto é puro overhead, já
que o sistema não efetua trabalho útil durante o processo de troca. Sua velocidade varia de
máquina a máquina dependendo da velocidade da memória, do número de registradores que
devem ser copiados e da existência de instruções especiais (como uma única instrução para
carregar ou armazenar todos os registradores). Velocidades típicas estão entre 1 a 1000
microssegundos (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 69).
Os tempos de troca de contexto são altamente dependentes do suporte de hardware.
Por exemplo, alguns processadores (como o Sun UltraSPARC) fornecem vários conjuntos de
registradores. Uma troca de contexto simplesmente inclui mudar o ponteiro para o conjunto
de registradores atual. É claro que se houver mais processos ativos do que conjuntos de
registradores, o sistema vai copiar os dados do registrador de e para a memória como antes.
Além disso, quanto mais complexo o sistema operacional, mais trabalho deve ser realizado
durante uma troca de contexto. Existem técnicas avançadas de gerência de memória podem
exigir que dados extras sejam trocados com cada contexto. Por exemplo, o espaço de
endereçamento do processo atual deve ser preservado à medida que o espaço da próxima
tarefa é preparado para uso. Como o espaço de endereçamento é preservado e quanto
trabalho será necessário para preservá‐lo dependerão do método de gerência de memória do
sistema operacional. A troca de contexto tornou‐se um gargalo de desempenho de tal ordem
que os programadores estão utilizando novas estruturas (threads) para evitá‐la sempre que
possível (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 69).
CAPÍTULO 2
ALGORITMOS DE ESCALONAMENTO
O escalonamento de UCP lida com o problema de decidir a quais processos na fila de
processos prontos a UCP deverá ser alocada. Existem muitos algoritmos diferentes de
escalonamento de UCP (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 99). Os algoritmos
de escalonamento que serão apresentados determinam, durante a execução, quais processos
executam em seguida. Esses algoritmos decidem quando e por quanto tempo cada processo
executa; escolhem preemptibilidade, prioridades, tempo de execução, tempo até a conclusão,
justiça e outras características do processo. Alguns sistemas requerem a utilização de um tipo
particular de escalonador (sistemas de tempo real normalmente requerem escalonadores
preemptivos, por prioridade). Outros confiam no comportamento do processo ao tomar
decisões de escalonamento (favorecem processos orientados a E/S) (DEITEL, H. M.; DEITEL, P.
J.; CHOFFNES, D. R., 2005, p. 215). Alguns algoritmos são usados apenas em sistemas em lote,
enquanto outros são utilizados em sistemas interativos, e outros ainda são empregados em
sistemas de tempo real. Convém ressaltar que alguns algoritmos podem ser usados tanto em
sistemas em lote quanto em sistemas interativos (TANENBAUM, 2003, p. 101).
2.1 Categorias de algoritmos de escalonamento
Para ambientes diferentes são necessários diferentes algoritmos de escalonamento.
Essa situação ocorre porque diferentes áreas de aplicação (e diferentes tipos de sistemas
operacionais) têm objetivos diferentes. Em outras palavras, o que deve ser otimizado pelo
escalonador não é o mesmo para todos os sistemas (TANENBAUM, 2003, p. 99).
De acordo com Tanenbaum (2003, p. 99) três ambientes merecem distinção:
1. Lote.
2. Interativo.
3. Tempo real.
Nos sistemas em lote não há, em seus terminais, usuários esperando impacientes por
uma resposta rápida. Conseqüentemente, os algoritmos não preemptivos ou preemptivos com
longo intervalo de tempo para cada processo são em geral aceitáveis. Essa tática reduz as
alternância entre processos e assim melhora o desempenho (TANENBAUM, 2003, p. 99).
Em um ambiente com usuários interativos, a preempção é essencial para evitar que
um processo se aposse da UCP e com isso negue serviço aos outros. Mesmo que nenhum
processo execute intencionalmente para sempre, uma falha em um programa pode levar um
processo a impedir indefinidamente que todos os outros executem. A preempção é necessária
para impedir esse comportamento (TANENBAUM, 2003, p. 99).
Em sistemas com restrições de tempo real, a preempção é, estranhamente,
desnecessária algumas vezes, pois os processos sabem que não podem executar por longos
períodos e em geral fazem seus trabalhos e bloqueiam rapidamente. A diferença com relação
aos sistemas interativos é que os sistemas de tempo real executam apenas programas que
visam ao progresso da aplicação. Já os sistemas interativos são de propósito geral e podem
executar programas arbitrários não cooperativos ou até mal‐intencionados (TANENBAUM,
2003, p. 100).
2.2 Objetivos do Escalonamento
Um projetista de sistemas deve considerar uma variedade de fatores ao desenvolver
uma disciplina de escalonamento como o tipo do sistema e as necessidades do usuário. Por
exemplo, a disciplina de escalonamento para um sistema de tempo real deve ser diferente da
disciplina para o sistema interativo de um computador de mesa; usuários esperam resultados
diferentes desses tipos de sistemas. Dependendo do sistema, o usuário e os projetistas podem
esperar que o escalonador:
• Maximize o rendimento. Uma disciplina de escalonamento deve tentar
atender ao maior número de processos por unidade de tempo.
• Maximize o número de processos interativos que estão recebendo
tempos de resposta “aceitáveis”.
• Maximize a utilização de recursos. Os mecanismos de escalonamento
devem manter os recursos do sistema ocupados.
• Evite adiamento indefinido. Um processo não deve experimentar um
tempo de espera sem limite antes de receber um serviço ou enquanto o estiver
recebendo.
• Imponha prioridades. Se o sistema designar prioridades a processos, o
mecanismo de escalonamento deve favorecer os processos de prioridade mais alta.
• Minimize sobrecarga. Curiosamente, em geral esse objetivo não é
considerado dos mais importantes. Sobrecarga freqüentemente resulta em
desperdício de recursos, mas uma certa porção dos recursos do sistema se investida
efetivamente como sobrecarga pode melhorar muito o desempenho geral do sistema.
• Assegure a previsibilidade. Minimizando a variância estática dos
tempos de resposta de processos, um sistema pode garantir que os processos
recebam níveis de serviços previsíveis.
Um sistema pode cumprir esses objetivos de diversas maneira. Em alguns casos o
escalonador pode impedir o adiamento indefinido de processos por meio do envelhecimento
(aging) – elevando gradativamente a prioridade de um processo enquanto ele espera ser
atendido. Eventualmente sua prioridade se torna alta o suficiente para que o escalonador
seleciona aquele processo para executar (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005,
p. 212‐213).
Muitos desses objetivos conflitam uns com os outros, fazendo do escalonamento um
problema complexo. Por exemplo, a melhor maneira de minimizar tempos de resposta é ter
recursos disponíveis suficientes sempre que forem necessários. O preço dessa estratégia é que
a utilização geral dos recursos será ruim. Em sistemas de tempo real, respostas rápidas,
previsíveis, são cruciais e a utilização de recursos é menos importante. Em outros tipos de
sistemas, a questão da economia comumente faz com que a utilização efetiva de recursos seja
um imperativo (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 213).
A despeito das diferenças entre os objetivos dos sistemas, muitas disciplinas de
escalonamento exibem propriedades semelhantes:
• Justiça. Uma disciplina de escalonamento é justa se todos os processos
semelhantes forem tratados da mesma maneira, e nenhum processo sofrer adiamento
indefinido devido a questões de escalonamento.
• Previsibilidade. A execução de um determinado processo sob cargas
de sistema similares deve durar aproximadamente o mesmo período de tempo.
• Escalabilidade. O desempenho do sistema deve se degradar
graciosamente (não deve entrar em colapso imediatamente) sob cargas pesadas
(DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 213‐214).
2.3 Critérios de Escalonamento
Para cumprir os objetivos de escalonamento de um sistema, o escalonador deve
considerar o comportamento do processo. Um processo orientado a UCP tende a usar todo o
tempo do processador que o sistema aloca a ele. Um processo orientado a E/S tende a usar o
processador apenas brevemente antes de gerar uma requisição de E/S e em seguida devolvê‐
lo. Processos orientados a UCP gastam a maior parte do seu tempo usando o processador;
processos orientados a E/S passam a maior parte do seu tempo esperando por recursos
externos (impressoras, unidades de disco, conexões de rede etc.) atendam a suas requisições,
e somente em tempo nominal usando processadores (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES,
D. R. , 2005, p. 214).
Uma disciplina de escalonamento também poderia considerar se um processo é em
lote ou interativo. Um processo em lote contém trabalho que o sistema executa sem interagir
com o usuário. Um processo interativo requer freqüentemente entradas do usuário. O sistema
deve fornecer bons tempos de resposta a um processo interativo, enquanto, em geral, um
processo em lote pode sofrer atrasos razoáveis. Similarmente, uma disciplina de
escalonamento deve ser sensível à urgência de um processo. Um processo em lote que deve
ser executado durante a noite não exige respostas imediatas. Um sistema de controle de
processo de tempo real que monitora uma refinaria de petróleo deve ser responsivo para
evitar uma possível explosão (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 214).
Há algum tempo atrás, usuários interagiam com processos emitindo requisições triviais
por meio do teclado. Nesse ambiente, um escalonador podia favorecer um processo interativo
e causar pouco efeito sobre outros processos porque o tempo requerido para atender a
processos interativos (por exemplo, exibir textos) era nominal. À medida que os computadores
ficaram mais potentes, projetistas de sistemas e programadores de aplicações começaram a
incluir características como gráficos e GUIs para aperfeiçoar a interação amigável com o
usuário. Embora alguns sistemas ainda utilizem interfaces de texto, grande parte dos usuários
de hoje interage via GUIs usando um mouse para executar ações como abrir, redimensionar ,
arrastar e fechar janelas de aplicativos. Usuários esperam que sistemas respondam
rapidamente, para que essas ações produzam movimentação suave. Diferentemente do que
acontece em interfaces de texto, essa tarefa pode ser intensiva em computação por exigir que
o sistema redesenhe a tela muitas vezes por segundo. Favorecer esses processos interativos
pode reduzir significativamente o nível de serviço oferecido a outros processos do sistema. No
caso de um processo em lote, essa redução temporária do serviço pode ser aceitável, embora
talvez não o seja para processos que executem em tempo real (como aplicações multimídia)
(DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 214‐215).
Em um sistema que emprega prioridades, o escalonador deve favorecer processos de
prioridades mais altas. Escalonadores podem basear suas decisões na freqüência com que um
processo de prioridade mais alta causou a preempção de um outro de prioridade mais baixa.
Sob certas disciplinas, processos que sofrem preempções freqüentes recebem um tratamento
menos favorecidos. Isso porque o curto tempo de execução do processo antes da preempção
não justifica a sobrecarga incorrida em um chaveamento de contexto toda vez que um
processo é despachado. Pode‐se argumentar o contrário, ou seja, que tal processo deve
receber tratamento mais favorável para compensá‐lo porque foi “maltratado” anteriormente
(DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 215).
Dessa maneira, diferentes algoritmos de escalonamento têm diferentes propriedades
e podem favorecer uma classe de processos mais que outra. Ao escolher que algoritmo usar
em determinada situação, deve‐se, pois, considerar as diferentes propriedades dos vários
algoritmos (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 98).
Muitos critérios foram sugeridos para comparar algoritmos de escalonamento de UCP.
As características usadas para comparação podem fazer diferença substancial na determinação
do melhor algoritmo. Os critérios usados incluem:
Pesquisadores sugeriram que, para sistemas interativos (como os de tempo
compartilhado), é mais importante minimizar a variância no tempo de resposta do que
minimizar o tempo de resposta médio. Um sistema com um tempo de resposta razoável e
previsível pode ser considerado mais desejável do que um sistema que é mais rápido em
média, mas é altamente variável. No entanto, pouco trabalho foi realizado em algoritmos de
escalonamento de UCP que minimizem a variância (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G.,
2000, p. 98‐99).
2.4 Escalonamento em Sistemas em Lote
2.4.1 Escalonamento primeiro a entrar primeiro a sair (FIFO)
Segundo Deitel, H. M.; Deitel, P. J.; Choffnes (2005, p. 215), Tanenbaum (2003, p. 102)
e Silberschatz, A., Galin, P.; Gagne, G. (2000, p. 99), o algoritmo de escalonamento mais
simples é provavelmente o algoritmo não preemptivo primeiro a entrar primeiro a sair (First In
First Out ‐ FIFO), também conhecido como primeiro a chegar primeiro a ser atendido (First
Come Firs ‐Served – FCFS).
No FIFO, os processos são despachados conforme o momento em que chegaram à fila
de prontos (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 215). Nesse esquema, o
processo que solicita a UCP primeiro, a recebe primeiro (SILBERSCHATZ, A.; GALIN, P.; GAGNE,
G., 2000, p. 99). Logo, com esse algoritmo, a UCP é atribuída aos processos na ordem em que
eles a requisitam (Figura 2.1). Basicamente, há uma fila única de processos prontos. Quando o
primeiro job entra no sistema é iniciado imediatamente e autorizado a executar por quanto
tempo queira. À medida que chegam os outros jobs, eles são encaminhados para o final da fila.
Quando o processo em execução é bloqueado, o primeiro processo aguardando na fila é o
próximo a executar. Quando um processo bloqueado fica pronto – assim como um job que
acabou de chegar ‐, ele é dirigido para o fim da fila (TANENBAUM, 2003, p. 102).
Figura 2.1. Escalonamento primeiro a entrar primeiro a sair
O FIFO é não preemptivo – uma vez obtido um processador, o processo executa até
libertá‐lo, terminando ou realizando um pedido de E/S (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES,
D. R., 2005, p. 215; SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 100). A grande vantagem
desse algoritmo é que ele é fácil de entender e igualmente fácil de programar. Com ele, uma
única lista encadeada controla todos os processos prontos. Logo, adicionar um novo job ou
processo desbloqueado requer apenas a inserção de seu BCP no final da fila (TANENBAUM,
2003, p. 102).
O algoritmo de escalonamento FIFO é justo no sentido de que escalona processos
segundo o momento em que chegaram; desse modo, todos os processos são tratados
igualmente, porém, de certa maneira é injusto porque processos longos fazem processos
curtos esperar, e processos não importantes fazem processos importantes esperar. Assim, o
FIFO não é útil para escalonar processos interativos, pois não pode garantir tempos de
resposta curtos (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 215), tornando‐se
particularmente problemático para sistemas de tempo compartilhado, onde é importante que
cada usuário tenha uma parte de UCP em intervalos regulares. Seria desastroso permitir que
um processo ficasse com a UCP por um período prolongado (SILBERSCHATZ, A.; GALIN, P.;
GAGNE, G., 2000, p. 100).
De acordo com Silberschatz, A., Galin, P.; Gagne, G (2000, p. 100), o tempo de espera
médio com a política FIFO muitas vezes é bem longo. Considere o seguinte conjunto de
processos que chegam no instante 0, com a duração de surto de UCP expressa em
milissegundos:
Tabela 2.1 ‐ Duração de surtos da UCP, em milissegundos
Processo Duração de surto
P1 24
P2 3
P3 3
Se os processos chegarem na ordem P1, P2, e P3, e forem atendidos na ordem FIFO, o
resultado seria o apresentado no seguinte diagrama de Gantt:
O tempo de espera é 0 milissegundo para o processo P1, 24 milissegundos para o
processo P2 e 27 milissegundos para o processo P3. Assim, o tempo de espera médio é de (0 +
24 + 27)/3 = 17 milissegundos. Entretanto, se os processos chegarem na ordem P2, P3 e P1, os
resultados serão semelhantes aos apresentados no diagrama de Gantt a seguir:
Além disso, considere o desempenho do escalonamento FIFO em uma situação
dinâmica. Suponha que exista um processo orientado a UCP e muitos processos orientados a
E/S. À medida que os processos fluem pelo sistema, a seguinte situação pode ocorrer: o
processo orientado a UCP obterá e manterá a UCP. Durante esse tempo, todos os demais
processos terminarão sua operação de entrada/saída e passarão para a fila de processos
prontos, esperando pela liberação da UCP. Enquanto os processos esperam na fila de
processos prontos, os dispositivos de E/S estão ociosos. Por fim, o processo orientado a UCP
termina seu surto de computação e passa para um dispositivo de E/S. Todos os processos
orientados a E/S, que têm surtos de UCP curtos, executam rapidamente e voltam para as filas
de E/S. Nesse instante, a UCP fica ociosa. O processo orientado a UCP passará então para a fila
de processos prontos e receberá a UCP. Novamente, todos os processos de E/S acabam
esperando na fila de processos prontos até que o processo orientado a UCP termine sua
execução. Existe um efeito comboio, enquanto todos os outros processos aguardam que um
grande processo saia da UCP. Esse efeito resulta em menor utilização de UCP e dispositivos
que o possível, caso processos mais curtos pudessem ser atendidos primeiro (SILBERSCHATZ,
A.; GALIN, P.; GAGNE, G., 2000, p. 100).
2.4.2 Escalonamento por job mais curto primeiro (SJF)
O escalonamento por job mais curto primeiro (Shortest Job First – SJF) é uma disciplina
de escalonamento não preemptiva na qual o escalonador seleciona o processo à espera com o
menor tempo de execução estimado até a conclusão (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES,
D. R., 2005, p. 218). Se dois processos tiverem seus próximos surtos de UCP de mesma
duração, o escalonamento FIFO será utilizado para desempate. Silberschatz, A., Galin, P.;
Gagne, G. (2000, p. 100) observam que um termo mais apropriado seria próximo surto de UCP
mais curto, porque o escalonamento é feito examinando‐se a duração do próximo surto de
UCP de um processo, em vez de sua duração total. Usa‐se, no entanto, o termo SJF porque
muitas pessoas e livros didáticos referem‐se a esse tipo de disciplina de escalonamento como
SJF.
O SJF reduz o tempo médio de espera em relação ao FIFO (BRUNO, J.; COFFMAN JR., E.
G.; SETHI, E. R., 1974, apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 218)13.
Entretanto, a variância dos tempos de espera é maior (são mais imprevisíveis) do que no FIFO,
especialmente para processos grandes (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p.
218).
O algoritmo de escalonamento SJF favorece processos curtos à custa dos mais longos.
Muitos projetistas defendem que, quanto mais curto o processo, melhor serviço deveria
receber. Outros discordam porque essa estratégia não incorpora prioridades (determinadas
pela importância de um processo). Processos interativos em particular tendem a ser “mais
curtos” do que processos orientados a processador, portanto, ainda assim, parece que essa
disciplina resultaria em bons tempos de resposta interativos. O problema é que ela é não
preemptiva, por isso, em geral, processos interativos que estão chegando não receberão
serviço imediato (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 218).
Por outro lado, Silberschatz, A., Galin, P.; Gagne, G. (2000, p. 102) dizem que o SJF
também pode ser implementado de forma preemptiva. A opção surge quando um novo
processo pode ter seu próximo surto de UCP mais curto do que o que restou do processo em
execução no momento. Um algoritmo SJF preemptivo interromperá o processo em execução
no momento, enquanto um algoritmo não preemptivo permitirá que o processo em execução
13
BRUNO, J.; COFFMAN JR., E. G.; SETHI, E. R. “Scheduling independent tasks to reduce mean finishing time”,
Communications of the ACM, v. 17, nº 7, jul. 1974, p. 382‐387.
no momento termine seu surto de computação. O escalonamento SJF preemptivo às vezes é
chamado escalonamento menor‐tempo‐de‐execução‐restante.
O SJF seleciona processos para atender de um modo que garanta que o próximo será
concluído e sairá do sistema tão logo seja possível, o que tende a reduzir o número de
processos à espera e também o número de processos que estão esperando atrás de processos
grandes. O resultado é que o SJF pode minimizar o tempo médio de espera dos processos à
medida que esses passam pelo sistema (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p.
218).
Como exemplo, considere o seguinte conjunto de processos, com a duração do surto
de UCP expressa em milissegundos.
Tabela 2.2 – Duração de surtos da UCP, em milissegundos
Processo Duração de surto
P1 6
P2 8
P3 7
P4 3
Usando o escalonamento SJF, poderíamos escalonar esses processos de acordo com o
seguinte diagrama de Gantt:
O tempo de espera é 3 milissegundos para o processo P1, 16 milissegundos para o
processo P2, 9 milissegundos para o processo P3 e 0 milissegundo para o processo P4. Assim, o
tempo de espera médio é (3 + 16 + 9 + 0)/4 = 7 milissegundos. Se tivesse sido utilizado o
esquema de escalonamento FIFO, o tempo de espera médio seria 10,25 milissegundos.
O algoritmo de escalonamento SJF é comprovadamente ótimo, pois fornece o tempo
médio de espera mínimo para um determinado conjunto de processos. Quando um processo
curto é colocado antes de um processo longo, o tempo de espera do processo curto diminui
mais do que aumenta o tempo de espera do processo longo. Conseqüentemente, o tempo de
espera médio diminui (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 100‐101).
Como o SJF sempre produz o menor tempo médio de resposta, seria ótimo se ele
pudesse ser utilizado para processos interativos também. Em certa medida, ele pode ser
utilizado. Processos interativos, em geral, seguem o padrão de esperar comando ‐ executar
comando – esperar comando – executar comando e assim por diante. Se fosse considerado a
execução de cada comando como um “trabalho” independente, então poderíamos minimizar o
tempo total de resposta para executar o mais curto primeiro. O único problema é descobrir
qual dos processos atualmente é o mais curto (TANENBAUM; WOODHULL, 2000, p. 73).
τn+1 = αtn + (1 – α)τn
Essa fórmula define uma média exponencial. O valor de tn contém a informação mais
recente; τn armazena o histórico. O parâmetro α controla o peso relativo do histórico recente e
passado para a previsão. Se α = 0, τn+1 = τn e o histórico recente não tem efeito (as condições
atuais são consideradas transitórias); se α = 1, então τn+1 = tn e apenas o surto de UCP mais
recente importa (o histórico é considerado velho e irrelevante). Mais comumente, α = 1/2, de
modo que os históricos recente e passado têm peso igual. O τ0 inicial pode ser definido como
uma constante ou como uma média geral do sistema. A figura seguinte mostra uma média
exponencial com α = 1/2 e τ0 = 10.
Figura 2.2. Previsão da duração do próximo surto de UCP
τn+1 = αtn + (1 – α)αtn‐1 + ... + (1 – α)jαtn‐j + ... + (1 – α)n+1τ0
Como α e (1 – α) são menores ou iguais a 1, cada termo sucessivo tem menos peso do
que seu precedente.
A técnica de estimar o próximo valor em uma série pegando a média ponderada do
valor atual medido e a estimativa anterior é, às vezes, chamada de envelhecimento. Ela é
aplicável a muitas situações nas quais uma previsão deve ser feita com base em valores
anteriores. O envelhecimento é especialmente fácil de implementar quando α = 1/2. Tudo que
é necessário é adicionar o novo valor à estimativa atual e dividir a soma por 2 (rotacionando‐o
1 bit para direita) (TANENBAUM; WOODHULL, 2000, p. 73).
Tanenbaum (2003, p. 103) e Tanenbaum; Woodhull (2000, p. 73) ressaltam que o SJF é
adequado somente quando todos os jobs estiverem disponíveis simultaneamente. Como um
contra‐exemplo, considere cinco jobs, de A a E, com tempos de execução 2, 4, 1, 1, e 1,
respectivamente. Seus tempos de chegada são 0, 0, 3, 3 e 3. Inicialmente, somente A ou B
podem ser escolhidos, já que outros três jobs ainda não chegaram. Usando o JSF, os jobs serão
executados na ordem A, B, C, D, E para um tempo médio de espera 4,6. Contudo, executa‐los
na ordem B, C, D, E, A implica em um tempo médio de espera de 4,4.
2.4.3 Escalonamento por próxima taxa de resposta mais alta (HRRN)
Brinch Hansen (1971, p. 103‐105 apud Deitel H. M.; Deitel, P. J.; Choffnes, D. R., 2005,
p. 218‐219)15 desenvolveu a política próxima‐taxa‐de‐resposta‐mais‐alta (Highest Response
Ratio Next – HRRN) que corrige algumas das deficiências do SJF, particularmente o viés
excessivo contra processos mais longos e o favoritismo excessivo em relação a processos
curtos. A HRRN é uma disciplina de escalonamento não preemptiva na qual a prioridade de
cada processo é uma função não apenas do seu tempo de serviço, mas também do tempo que
passou à espera pelo serviço.
Uma vez obtido o serviço, o processo executa até o fim. HRRN calcula prioridades
dinâmicas segundo a fórmula:
Prioridade = tempo de espera + tempo de serviço
tempo de serviço
15
BRINCH HANSEN, P. “Short‐term scheduling in multiprogramming systems”, Third ACM Symposium on Operating
Systems Principles, Universidade de Stanford, out. 1971, p. 103‐105.
Como o tempo de serviço aparece no denominador, processos mais curtos recebem
preferência. Entretanto, pelo fato de o tempo de espera aparecer no numerador, processos
mais longos que estão à espera também receberão tratamento favorável. Essa técnica é similar
ao envelhecimento (aging) e impede que um escalonador adie processos indefinidamente
(DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 219).
2.4.4 Escalonamento por menor tempo de execução restante (SRT)
O escalonamento por menor tempo de execução restante (Shortest Remaining Time –
SRT), também conhecido como próximo de menor tempo restane (Shortest Remaining Time
Next ‐ SRTN), é a contraparte preemptiva do SJF que tenta aumentar o rendimento atendendo
pequenos processos que chegam. O SRT era efetivo para sistemas de processamento de jobs
que recebiam um fluxo de entrada de jobs; hoje já não tem mais utilidade para a maioria dos
sistemas operacionais (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 219). Com esse
algoritmo, o escalonador sempre escolhe o processo cujo tempo de execução restante seja o
menor (TANENBAUM, 2003, p. 103). No SJF, quando um processo começa a executar, continua
até o final. No SRT, um processo recém‐chegado cujo tempo estimado de execução até o final
é menor provoca a preempção de um processo em execução cujo tempo estimado de
execução até o final é maior. Novamente, o SRT requer que as estimativas do comportamento
futuro do processo sejam efetivas, e o projetista deve levar em conta o potencial abuso da
estratégia de escalonamento da parte do usuário (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R.,
2005, p. 219).
Como exemplo, considere os quatro processos seguintes, com a duração do surto de
UCP expressa em milissegundos:
Tabela 2.3 – Duração de surtos da UCP em milissegundos e instantes de chegada de processos
Se os processos chegarem na fila de processos prontos nos tempos indicados e
precisarem dos tempos de surto a seguir, o escalonamento SRT resultante será o apresentado
no diagrama de Gantt seguinte:
O algoritmo SRT deve manter informações sobre o tempo que está sendo gasto no
serviço do processo em execução e realizar preempções ocasionais. Processos que acabaram
de chegar cujos tempos de execução são curtos executam quase imediatamente. Todavia, o
tempo médio de espera e a variância dos tempos de espera dos processos mais longos são
ainda maiores do que no SJF. Esses fatores contribuem para uma sobrecarga maior no SRT do
que no SJF (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 219).
O algoritmo SRT teoricamente oferece tempos de espera mínimos, mas, em certas
ocasiões, devido à sobrecarga de preempção, o SJF pode se sair melhor. Por exemplo,
considere um sistema no qual um processo em execução esteja quase no final e chegue um
novo processo cujo tempo estimado de serviço seja pequeno. O processo em execução deve
sofrer preempção? A disciplina SRT faria preempção, mas essa pode não ser a alternativa
ótima. Uma solução é garantir que um processo em execução não possa mais sofrer
preempção quando o tempo de execução restante atingir um determinado nível baixo. Um
problema semelhante surge quando um processo recém‐chegado requer um tempo
ligeiramente menor para concluir do que o processo em execução. Embora o algoritmo
impusesse corretamente a preempção do processo em execução, essa poderia não ser a
política ótima. Por exemplo, se a sobrecarga de preempção for maior do que a diferença dos
tempos de serviço entre os dois processos, a preempção resultará em desempenhos inferiores
(DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 219).
De acordo com Deitel, H. M.; Deitel, P. J.; Choffnes (2005, p. 219), como esses
exemplos ilustram, o projetista de sistemas operacionais deve pesar cuidadosamente a
sobrecarga de mecanismos de gerenciamento de recursos contra os benefícios esperados.
Observam também que políticas de escalonamento relativamente simples podem resultar em
mau desempenho por razões sutis.
2.4.5 Escalonamento em três níveis
De um certo ponto de vista, os sistemas em lote permitem um escalonamento em três
níveis (Figura 2.3). À medida que chegam ao sistema, os jobs são inicialmente colocados em
uma fila de entrada armazenada em disco. O escalonador de admissão decide qual job será
admitido no sistema. Os outros são mantidos na fila de entrada até que sejam selecionados.
Um algoritmo típico para controle de admissão pode ser pensado com uma mescla de jobs
orientados a computação e jobs orientados a E/S. Por outro lado, jobs curtos poderiam ser
admitidos mais rapidamente, ao passo que jobs mais longos teriam de esperar. O escalonador
de admissão é livre para manter alguns jobs na fila de entrada e admitir jobs que cheguem
depois, se lhe convier (TANENBAUM, 2003, p. 103).
Figura 2.3. Escalonamento em três níveis
Assim que um job é admitido no sistema, um processo pode ser criado para ele, que
passa então a competir pela UCP. Contudo, pode acontecer de o número de processos ser tão
grande que não haverá lugar suficiente para todos na memória. Nesse caso, alguns dos
processos devem ser levados para o disco (swapped out). O segundo nível de escalonamento é
que decide quais processos deverão ser mantidos na memória e quais permanecerão no disco
e por isso pode ser denominado escalonador de memória. Essa decisão precisa ser revista com
freqüência para permitir que os processos em disco obtenham algum serviço. No entanto,
como trazer o processo do disco é computacionalmente caro, essa revisão não ocorrerá,
provavelmente, mais que uma vez a cada segundo, talvez menos que isso. Se o conteúdo da
memória principal estiver muito desorganizado, uma grande parcela da largura de banda de
disco será gasta, tornando a E/S de arquivos lenta (TANENBAUM, 2003, p. 103).
Segundo Tanenbaum (2003, p. 104), para otimizar o desempenho do sistema como um
todo, o escalonador de memória deve decidir, com parcimônia, quantos processos ele quer
manter na memória (ou seja, o grau de multiprogramação) e que tipo de processos. Se tiver
informação sobre quais processos são orientados a computação e a E/S, ele poderá tentar
conservar na memória uma mistura desses tipos de processo. Em uma aproximação muito
grosseira, se uma certa classe de processos computa por aproximadamente 20% do tempo,
manter cerca de cinco processos é uma boa aproximação para manter a UCP ocupada.
Tanenbaum (2003, p. 142) ainda ressalta que esse é um modelo otimista, não realista, pois
presume que os cinco processos nunca poderão estar ao mesmo tempo esperando por E/S.
Um modelo melhor é considerar a utilização da UCP de um ponto de vista probabilístico.
Suponha que um processo gaste uma fração p de seu tempo esperando pela finalização de sua
solicitação de E/S. Com n processos simultâneos na memória, a probabilidade de todos os n
processos estarem esperando por E/S (nessa situação a UCP estaria ociosa) é pn. A utilização da
UPC é então dada pela fórmula
Utilização da UCP = 1 ‐ pn
A figura que segue mostra a utilização da UCP em função de n, ou seja, do grau da
multiprogramação.
Figura 2.4. Utilização da UCP como uma função do número de processos na memória
A figura deixa claro que, se os processos gastarem 80% de seus tempos esperando pela
conclusão de E/S, no mínimo dez processos deveriam estar simultaneamente na memória para
que o grau de ociosidade da UCP pudesse ser mantido abaixo de 10%. Tempos de espera iguais
ou superiores a 80% não são incomuns em um processo interativo que espera um usuário
digitar algo em um terminal. Entretanto, mesmo em sistemas em lote, processos que realizam
muitos acessos a disco poderão, com freqüência, atingir essa porcentagem ou superá‐la
(TANENBAUM, 2003, p. 142).
Para tomar suas decisões, o escalonador de memória revisa periodicamente cada
processo que está no disco para decidir se o traz ou não para a memória. Entre os critérios
utilizados para tomar essa decisão estão os seguintes:
1. Há quanto tempo o processo passou por uma troca entre o disco e a
memória (swapped in ou out)?
2. Quanto tempo de UCP o processo teve da última vez?
3. Qual o tamanho do processo? (Os pequenos não entram)
4. Qual a importância do processo?
O terceiro nível de escalonamento, na verdade, escolhe um dos processos prontos na
memória principal para executá‐lo depois. Muitas vezes, esse nível é chamado de escalonador
de UCP e é nele que normalmente se pensa quando se fala sobre o “escalonador”. Qualquer
algoritmo adequado pode ser usado aqui, seja preemptivo ou não preemptivo (TANENBAUM,
2003, p. 104).
2.5 Escalonamento em sistemas interativos
São apresentados alguns algoritmos aplicados a sistemas interativos. Todos eles
podem também ser utilizados como escalonadores de UCP em sistemas em lote. Se por um
lado o escalonamento em três níveis não é adequado para sistemas interativos, por outro, o
escalonamento em dois níveis (escalonador de memória e de UCP) não só é possível como
também comum (TANENBAUM, 2003, p. 104).
2.5.1 Escalonamento Round Robin
Um dos algoritmos de escalonamento mais simples, mais antigos, justo e mais
amplamente utilizado é o Round Robin (RR), também chamado de revezamento circular
(TANENBAUM; WOODHULL, 2000, p. 70; TANENBAUM, 2003, p. 104). O RR foi projetado
especialmente para sistemas de tempo compartilhado. Trata‐se de uma política de
escalonamento preemptiva. É semelhante ao escalonamento FIFO, mas a preempção é
acrescentada para alternar entre processos. A cada processo é atribuído um pequeno
quantum de tempo, geralmente entre 10 e 100 milissegundos, durante o qual lhe é permitido
executar. A fila de processos prontos é tratada como uma fila circular. O escalonador de UCP
percorre a fila de processos prontos, alocando a UCP a cada processo por intervalo de tempo
de até 1 quantum de tempo (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 103). Se o
processo ainda está executando no fim do quantum, é feita a preempção da UCP e ela é dada a
outro processo. Se o processo bloqueou ou terminou antes de o quantum ter se esgotado, é
feita a comutação da UCP quando o processo bloqueia, naturalmente. Este processo é
ilustrado na figura que segue.
Figura 2.5. Escalonamento Round Robin
A alternância circular é efetiva para ambientes interativos nos quais o sistema precisa
garantir tempos de resposta razoáveis. O sistema pode minimizar a sobrecarga de preempção
por meio de mecanismos eficientes de chaveamento de contexto e mantendo os processos na
memória principal (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 216).
Como o FIFO, a alternância circular é comumente encontrada dentro de algoritmos de
escalonamento de processador mais sofisticados, mas raramente é o esquema mestre. Muitos
outros algoritmos sofisticados de escalonamento de UCP degeneram para FIFO ou para
alternância circular, quando todos os processos têm a mesma prioridade. Por essa razão, FIFO
e RR são dois dos três algoritmos de escalonamento requeridos pela especificação POSIX para
sistemas de tempo real (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 216).
O RR é fácil de implementar. Tudo que é necessário que o escalonador faça é manter
uma lista de processos prontos como uma fila FIFO. O escalonador então seleciona o primeiro
processo da fila, define um temporizador para interromper depois de 1 quantum e submete o
processo, como mostrado na Figura 2.6(a). Quando o processo utiliza todo seu quantum, ele é
posto no fim da lista, como ilustrado na Figura 2.6(b) (TANENBAUM, 2003, P. 104;
SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 103).
Figura 2.6. Agendamento Round Robin. (a) Lista de processos executáveis. (b) Lista de processos
executáveis depois que B utiliza todo seu quantum
Como dito anteriormente, neste momento, duas opções podem ocorrer. O processo
poderá ter um surto de UCP de menos de 1 quantum. Nesse caso, o próprio processo liberará a
UCP voluntariamente. O escalonador procederá então para o próximo processo na fila de
processos prontos. Caso contrário, se o surto de UCP do processo em execução no momento
for maior do que 1 quantum de tempo, o temporizador se esgotará e causará um interrupção
para o sistema operacional. Uma troca de contexto será executada, e o processo será colocado
no final da fila de processos prontos. O escalonador de UCP selecionará então o próximo
processo da fila. No algoritmo de escalonamento RR, nenhum processo recebe UCP por mais
do que 1 quantum de tempo consecutivo, a menos que seja o único processo pronto
(SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 104).
Uma questão importante para o escalonamento RR é o tamanho do quantum que é
atribuído aos processos (TANENBAUM, 2003, p. 104). A determinação do tamanho do
quantum, q, é crítica para a operação efetiva de um sistema de computação com
escalonamento preemptivo (POTIER, D; GELENBE, E.; LENFANT, J., 1976 apud DEITEL, H. M.;
DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 217)16. O quantum deve ser pequeno ou grande? Deve
ser fixo ou variável? Deve ser o mesmo para todos os processos ou deve ser determinado
separadamente para cada processo? Primeiro, considere o comportamento do sistema quando
o quantum torna‐se extremamente grande ou extremamente pequeno. À medida que o
quantum torna‐se grande, processos tendem a receber o tempo que precisam para concluir,
portanto, o esquema RR degenera para FIFO. À medida que o quantum torna‐se pequeno, o
chaveamento de contexto predomina; o desempenho eventualmente se degrada até o ponto
em que o sistema passa grande parte de seu tempo fazendo chaveamento de contexto e
realizando pouco, ou nenhum, trabalho (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p.
217).
Exatamente onde, entre zero e infinito, o quantum deve ser estabelecido? Considere o
seguinte experimento. Suponha que os valores estampados na face de um mostrador circular
variem entre q = 0 e q = c, onde c é um valor extremamente grande. O mostrador inicia‐se
posicionado em zero. À medida que gira‐se o mostrador, o quantum do sistema aumenta.
Suponha que o sistema esteja em funcionamento e que haja muitos processos interativos.
Quando começa‐se a girar o mostrador, os tamanhos dos quanta estão perto de zero e a
sobrecarga de chaveamento de contexto consome a maioria dos cliclos do processador. Os
usuários interativos defrontam‐se com um sistema vagaroso com tempos de resposta ruins.
16
POTIER, D.; GELENBE, E.; LENFANT, J. “Adaptative allocation of central processing unit Quant”, Journal of the ACM,
v. 23, nº 1, jan. 1976, p. 97‐102.
Conforme aumenta‐se o quantum, os tempos de resposta melhoram. A porcentagem do
processador consumida por sobrecarga é suficientemente pequena para que os processos
recebam um pouco de serviço do processador, mas os tempos de resposta ainda não estão tão
rápidos quanto cada usuário gostaria. Ao continuar a girar o mostrador, os tempos de resposta
melhoram e, eventualmente, alcança‐se um tamanho de quantum com o qual grande parte
dos processos interativos recebe resposta imediatas do sistema, mas ainda não está claro se o
ajuste do quantum é ótimo. Girando o mostrador um pouquinho mais, e os tempos de
resposta ficam ligeiramente melhores. Girando o mostrador novamente, e os tempos de
resposta voltam a ficar vagarosos. O quantum vai ficando maior até que eventualmente torna‐
se grande o suficiente para que cada processo execute até o final após receber o processador.
O escalonamento está degenerando para FIFO, pelo qual processos mais longos fazem os mais
curtos esperar, e o tempo médio de espera aumenta enquanto processos mais longos
executam até a finalização antes de devolver o processador (DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R., 2005, p. 217).
A alternância de um processo para outro requer uma certa quantidade de tempo para
sua administração – salvar e carregar registradores e mapas de memória, atualizar várias listas
e tabelas, carregar e descarregar a memória cache etc. Suponha que esse chaveamento de
contexto dure 1 ms e que o comprimento do quantum seja de 4 ms. Dessa forma, depois de
realizar 4 ms de trabalho útil, a UCP terá de gastar 1 ms para alternar o processo. Nesse
exemplo, 20% do tempo de UCP é gasto em administração, o que sem dúvida é demais
(TANENBAUM, 2003, p. 104).
Para melhorar a eficiência da UCP, pode‐se estabelecer o valor do quantum em 100
ms, por exemplo. Agora, o tempo gasto com chaveamento de contexto é de apenas 1%. No
entanto, considere o que pode acontecer em um sistema de tempo compartilhado se dez
usuários interativos clicarem a tecla <Enter> quase ao mesmo tempo. Dez processos serão
colocados na lista de processos executáveis. Se a UCP estiver ociosa, o primeiro dos processos
iniciará imediatamente, o segundo não poderá iniciar enquanto não se passarem 100 ms e
assim por diante. O último azarado pode ter de esperar um segundo antes de ter uma
oportunidade para executar – supondo que todos os outros usem inteiramente seus quanta. A
maioria dos usuários verá como problema uma resposta para um pequeno comando demorar
um segundo (TANENBAUM, 2003, p. 105).
Por curiosidade, no Linux, o quantum‐padrão designado para um processo é 100 ms,
podendo variar de 10 ms a 200 ms dependendo da prioridade e do comportamento do
processo. Processos de alta prioridade e processos orientados a E/S recebem um quantum
maior do que processos de baixa prioridade e processos orientados a UCP (LINUX apud DEITEL,
H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 217)17. No Windows XP, o quantum‐padrão
designado a um processo é um valor específica da arquitetura igual a 20 ms na maioria dos
sistemas. Esse valor pode variar dependendo de o processo executar no primeiro plano ou no
segundo plano da GUI (SOLOMON, D.; RUSSINOVICH, M., 2000 apud DEITEL, H. M.; DEITEL, P.
J.; CHOFFNES, D. R., 2005, p. 217)18.
Se houver n processos na fila de processo prontos e o quantum for q, então cada
processo obterá 1/n do tempo de UCP em lotes de no máximo q unidades de tempo. Cada
processo deve esperar no máximo (n ‐ 1) x q unidades de tempo até seu próximo quantum. Por
exemplo, se houver 5 processos com um quantum de tempo de 20 milissegundos, cada
processo obterá até 20 milissegundos a cada 100 milissegundos (SILBERSCHATZ, A.; GALIN, P.;
GAGNE, G., 2000, p. 104).
O tempo de espera médio na política RR, no entanto, é geralmente longo.
Considerando o seguinte conjunto de processos que chegam no instante 0, com duração de
surto de UCP expressa em milissegundos:
Tabela 2.4 ‐ Duração de surtos da UCP, em milissegundos
Processo Duração de surto
P1 24
P2 3
P3 3
Com um quantum de 4 milissegundos, o processo P1 obtém os 4 primeiros
milissegundos. Como mais 20 milissegundos são necessários, ele será interrompido após o
primeiro quantum de tempo, e a UCP será passada ao próximo processo na fila, o processo P2.
Como o processo P2 não precisa de 4 milissegundos, ele encerra antes que o seu quantum
expire. A UCP é então passada para o próximo processo, o processo P3. Assim que cada
processo tiver recebido 1 quantum, a UCP é retornada ao processo P1 para uma quantum
adicional. O escalonamento RR resultante é esquematizado no gráfico de Gantt abaixo:
17
LINUX source code, version 2.6.0‐test3, sched.c, linhas 62‐135,
miller.cs.wm.edu/lxr3.linux/http/source/kernel/sched.c?v=2.6.0‐test3.
18
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000. 3 ed. Redmond: Microsoft Press, 2000, p. 338‐347.
O tempo de espera médio para este caso é 17/3 = 5,66 milissegundos.
Outro ponto importante a considerar é que o desempenho do algoritmo RR depende
muito do tamanho do quantum de tempo. Se o quantum for maior que o surto médio de UCP,
a preempção raramente ocorrerá e a regra de RR será a mesma que a regra FIFO. Na verdade,
a maior parte dos processos bloqueará antes que o quantum acabe, causando uma alternância
de processo. Eliminar a preempção melhora o desempenho porque a alternância de processo
somente ocorre quando é logicamente necessária, isto é, quando um processo bloqueia e não
é mais capaz de continuar (TANENBAUM, 2003, p. 105; SILBERSCHATZ, A.; GALIN, P.; GAGNE,
G., 2000, p. 104). Por outro lado, se o quantum de tempo for extremamente pequeno (por
exemplo, 1 microssegundo), a abordagem RR será chamada de compartilhamento de
processador e, para os usuários, é como se em teoria cada um dos n processos tivesse seu
próprio processador executando a 1/n da velocidade do processador real. A abordagem foi
usada no hardware da Control Data Corporation (CDC) para implementar 10 processadores
periféricos com apenas um conjunto de hardware e 10 conjuntos de registradores. O hardware
executa uma instrução para um conjunto de registradores e passa para o próximo. Esse ciclo
continua, resultando em 10 processadores lentos em vez de um rápido. (Na verdade, como o
processador era muito mais rápido do que a memória e cada instrução referenciava a
memória, os processadores não eram muito mais lentos do que 10 processadores reais teriam
sido.) (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 104).
Como já citado, no software, no entanto, também precisa‐se considerar o efeito da
troca de contexto no desempenho do escalonamento RR. Suponha que exista apenas um
processo com 10 unidades de tempo. Se o quantum for 12 unidades de tempo, o processo
terminará em menos de 1 quantum, sem custo adicional. Se o quantum for de 6 unidades de
tempo, entretanto, o processo exigirá 2 quanta, resultando em uma troca de contexto. Se o
quantum for 1 unidade de tempo, novo troca de contexto ocorrerão, tornando a execução do
processo mais lenta, como ilustrado na Figura 2.7 (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G.,
2000, p. 104).
Figura 2.7. A forma como um quantum de tempo menor aumenta as trocas de contexto
Dessa forma, o quantum deve ser grande com relação ao tempo de troca de contexto.
Se o tempo de troca for de cerca de 10% do quantum, então cerca de 10% do tempo de UCP
será gasto em troca de contexto (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 105).
O tempo de retorno também depende do tamanho do quantum. Como pode‐se
observar na Figura 2.8, o tempo de retorno médio de um conjunto de processos não melhora
necessariamente à medida que o quantum aumenta. Em geral, o tempo de retorno médio
pode ser melhorado se a maioria dos processos terminar seu próximo surto de UCP em um
único quantum. Por exemplo, dados três processos com 10 unidades de tempo cada e um
quantum de 1 unidade de tempo, o tempo de retorno médio é 29. Se o quantum for 10, no
entanto, o tempo de retorno cairá para 20. Se o tempo de troca de contexto for acrescentado,
o tempo médio aumentará para um quantum menor, já que um número maior de trocas de
contexto será necessário (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 105).
Figura 2.8. A forma como o tempo de retorno varia com a duração do quantum
Assim, adotar um quantum muito curto causa muitas alternâncias de processo e reduz
a eficiência da UCP, mas um quantum muito longo pode gerar uma resposta pobre às
requisições interativas curtas e o RR degenera para a política FIFO. Tanenbaum (2003, p. 105)
considera que um quantum em torno de 20 a 50 ms é bastante razoável. Silberschatz, A.;
Galin, P.; Gagne, G. (2000, p. 105) observam que uma regra geral é que 80% do surtos de UCP
devem ser menores do que o quantum de tempo para tempos de resposta aceitáveis.
2.5.2 Escalonamento por Prioridades
O algoritmos SJF é um caso especial do algoritmo geral de escalonamento por
prioridade. Uma prioridade está associada a cada processo, e a UCP é alocada ao processo com
prioridade mais alta (Figura 2.9). Processos de prioridade igual são escalonados na ordem FIFO.
Um algoritmo SJF nada mais é do que um algoritmo por prioridade no qual a prioridade (p) é o
inverso do próximo surto de UCP previsto. Quanto maior o surto, menor a prioridade, e vice‐
versa (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 102).
Figura 2.9. Escalonamento por prioridades
Figura 2.9. Escalonamento por prioridades
Discute‐se o escalonamento em termos de alta e baixa prioridade. Silberschatz, A.;
Galin, P.; Gagne, G. (2000, p. 102) observam que as prioridades em geral estão em uma faixa
fixa de números, tais como 0 a 7, ou 0 a 4095. No entanto, não existe consenso geral sobre se
0 é a prioridade mais alta ou mais baixa. Alguns sistemas usam números baixos para
representar baixa prioridade; outros usam números baixos para alta prioridade. Essa diferença
pode levar à confusão. Neste texto, considera‐se que os números baixos representam baixa
prioridade.
Como um exemplo, considerando o seguinte conjunto de processos, que têm seu
instante de chegada definido em 0, na ordem P1, P2, ..., P5, com a duração do surto de UCP
expressa em milissegundos:
Tabela 2.5 ‐ Duração de surtos da UCP, em milissegundos e prioridades
Prioridades podem ser atribuídas estatística ou dinamicamente aos processos a partir
de fatores internos ou externos ao sistema. As prioridades definidas internamente utilizam
alguma quantidade ou quantidades mensuráveis a fim de calcular a prioridade de um
processo. Por exemplo, os limites de tempo, requisitos de memória, o número de arquivos
abertos e a razão entre os surtos de E/S e UCP médios têm sido usados no cálculo de
prioridades. As prioridades externas são definidas pro critérios que são externos aos sistema
operacional, tais como a importância do processo, o tipo e a quantidade de fundos pagos para
uso do computador, o departamento patrocinando o trabalho e outros fatores, geralmente
políticos. Exemplificando, em um computador militar, os processos iniciados por generais
podem partir com prioridade 100; os processos iniciados por coronéis, em 90; os de majores,
em 80; os de capitães, em 70; os de tenentes, em 60 e assim por diante. Assim, em um centro
de computação comercial, trabalhos de alta prioridade podem custar cem dólares por uma
hora; um de prioridade média, 75 dólares por hora, e os de prioridade baixa, 50 dólares pelo
mesmo período. O sistema Unix tem um comando, nice, que permite que um usuário reduza
voluntariamente a prioridade de seu processo e assim seja gentil com os outros usuários.
Ninguém usa esse comando (TANENBAUM, 2003, p. 105; SILBERSCHATZ, A.; GALIN, P.; GAGNE,
G., 2000, p. 103).
O sistema também pode atribuir dinamicamente as prioridades para atingir certos
objetivos. Por exemplo, alguns processos são altamente orientados a E/S e gastam a maior
parte de seu tempo esperando que um E/S termine. Se um processo como esse requisitasse a
UCP, ela deveria ser dada a ele imediatamente, para deixá‐lo iniciar sua próxima requisição de
E/S, a qual poderia então continuar em paralelo com outro processo que estivesse realmente
computando. Fazer o processo orientado a E/S esperar um longo tempo pelo UCP significa tê‐
lo ocupando a memória desnecessariamente por tempo demais. Um algoritmo simples e que
funciona bem para processos orientados a E/S é atribuir 1/f à prioridade, sendo f a fração do
último quantum que o processo usou. Um processo que tivesse utilizado somente 1 ms de seu
quantum de 50 ms obteria prioridade 50 (já que ele utilizou 1/50 ms de seu último quantum,
logo 1/(1/50) = 50 ms), enquanto um processo que houvesse consumido todo o quantum teria
prioridade 1 (TANENBAUM, 2003, p. 105).
O escalonamento por prioridade pode ser preemptivo ou não preemptivo. Quando um
processo chega na fila de processos prontos, sua prioridade é comparada com a prioridade do
processo em execução no momento. Um algoritmo de escalonamento por prioridade
preemptivo interromperá a UCP se a prioridade do processo recém‐chegado for maior do que
a prioridade do processo em execução no momento. Um algoritmo de escalonamento não
preemptivo simplesmente colocará o novo processo no topo da fila de processos prontos
(SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 103).
É muitas vezes conveniente agrupar processos em classes de prioridade e usar o
escalonamento por prioridade entre as classes – contudo, dentro de cada classe, usar o
escalonamento circular. A Figura 2.10 mostra um sistema com quatro classes de prioridade. O
algoritmo de escalonamento é o seguinte: enquanto houver processos executáveis na classe
de prioridade 4, execute apenas um por quantum em RR e nunca perca tempo com as classes
de baixa prioridade. Se a classe de prioridade 4 estiver vazia (sem processos para executar),
então execute os processos da classe 3 em RR. Se as classes 4 e 3 estiverem ambas vazias,
então execute a classe 2 em RR e assim por diante. Se as prioridades não forem
ocasionalmente ajustadas, as classes de prioridade mais baixas poderão todas morrer de fome
(TANENBAUM, 2003, p. 105‐106).
Figura 2.10. Um algoritmo de escalonamento com quatro classes de prioridade
Um problema crítico com os algoritmos de escalonamento por prioridade é o bloqueio
por tempo indefinido ou starvation (estagnação). Um processo que está pronto para executar
mas que não tem a UCP pode ser considerado bloqueado, esperando pela UCP. Um algoritmo
de escalonamento por prioridade pode deixar que alguns processos de baixa prioridade
fiquem esperando indefinidamente pela UCP. Em um sistema de computação altamente
carregado, um fluxo constante de processos de maior prioridade pode impedir que um
processo de baixa prioridade obtenha a UCP. Geralmente, ocorre uma de duas opções. O
processo acabará sendo executado tardiamente, quando o sistema finalmente ficar com
menos carga, ou o sistema de computador acabará falhando e perderá todos os processos de
baixa prioridade não terminados. (Há boatos, inclusive, que quando o IBM 7094 do MIT foi
desligado em 1973, encontraram um processo de baixa prioridade que tinha sido submetido
em 1967 e que ainda não tinha sido executado) (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G.,
2000, p. 103)
Uma solução para o problema do bloqueio indefinido de processo de baixa prioridade
é o envelhecimento (aging). O envelhecimento é uma técnica para aumentar gradualmente a
prioridade dos processos que ficam esperando no sistema durante muito tempo. Por exemplo,
se as prioridades tiverem uma faixa de 0 (baixa) a 127 (alta), pode‐se aumentar a prioridade
de um processo em espera em espera em 1 ponto a cada 15 minutos. Por fim, mesmo um
processo com prioridade inicial 0 teria a maior prioridade no sistema e poderia ser executado.
Na verdade, bastariam 32 horas para um processo de prioridade 0 passar para um processo de
prioridade 127 (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 103).
2.5.2.1 Alternância Circular Egoísta
Kleinrock (1970 apud Deitel H. M.; Deitel, P. J.; Choffnes, D. R., 2005, p. 216) discutiu
uma variante da alternância circular denominada alternância circular egoísta (Selfish Round
Robin ‐ SRR) que usa o envelhecimento para elevar gradativamente as prioridades dos
processos ao longo do tempo19.
Nesse esquema, ao entrar em um sistema, cada processo fica primeiramente em uma
fila de retenção onde envelhece até sua prioridade atingir o nível dos processos que estão na
fila ativa. Nesse ponto ele é passado para a fila ativa e escalonado por RR juntamente com
outros processos que também estão na fila ativa, o que significa que processos mais velhos são
favorecidos sobre os que acabaram de entrar no sistema (DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R., 2005, p. 216).
Na SSR, a prioridade de um processo aumenta a uma taxa a enquanto está na fila de
espera, e a uma taxa b, onde b ≤ a, quando está na fila ativa. Quando b < a, os processos da fila
de retenção envelhecem a uma taxa mais alta do que os que estão na fila ativa, portanto,
eventualmente entrarão na fila ativa e disputarão um processador. O ajuste dos parâmetros a
e b causa impacto sobre o modo como a idade de um processo afeta a latência média e o
rendimento. Por exemplo, à medida que a fica muito maior do que b, os processos que entram
19
KLEINROCK, L. “A continuum of time‐sharing scheduling algorithms”, Proceedings of AFIPS, SJCC, 1970, p. 453‐458.
no sistema passarão pouco tempo, ou nenhum, na fila de retenção. Se b << a, os processos
passarão uma quantidade insignificante de tempo na fila de retenção, portanto, a SRR
degenera para RR. Se b = a, cada processo do sistema funciona à mesma taxa, portanto, SRR
degenera para FIFO (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 216‐217).
2.5.3 Escalonamento por Múltiplas Filas
Outra classe de algoritmos de escalonamento foi criada para situações em que os
processos são facilmente classificados em diferentes grupos. Por exemplo, uma divisão comum
é feita entre processos de primeiro plano (interativos) e processos de segundo plano (batch).
Esses dois tipos de processos têm diferentes requisitos de tempo de resposta e, por isso,
podem ter diferentes necessidades de escalonamento. Além disso, os processos de primeiro
plano podem ter prioridade (extremamente definida) em relação aos processos de segundo
plano (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 105).
Um dos primeiros escalonadores por prioridades foi implementado no CTSS (CORBATÓ
et al., 1962 apud TANENBAUM, 2003, p. 106)20. O CTSS tinha um problema: a alternância de
processo era muito lenta porque o 7094 só podia manter na memória um processo por vez.
Cada alternância significava trocar o processo, ou seja, enviá‐lo para o disco e ler outro do
disco. Os projetistas do CTSS logo perceberam que era mais eficiente dar de vez em quando
um quantum grande para os processos orientados a UCP do que fornecer freqüentemente um
quantum pequeno (para reduzir as operações de troca entre o disco e a memória). Por outro
lado, dar a todos os processos um quantum grande significaria ter um tempo de resposta
inadequado, conforme visto anteriormente. A solução, então, foi definir classes de prioridade.
Os processos na classe de prioridade mais alta eram executados por um quantum. Os
processos na classe seguinte de prioridade mais alta executavam por dois quanta. Os
processos na próxima classe executavam por quatro quanta e assim por diante. Se um
processo terminasse, todos os quanta a ele alocados iriam para uma classe abaixo
(TANENBAUM, 2003, p. 106).
Como exemplo, imagine um processo que precisasse computar continuamente por 100
quanta. Inicialmente, a ele seria dado um quantum, e ele então seria levado da memória para
o disco (troca para o disco). Na vez seguinte ele teria dois quanta antes de ocorrer a troca para
o disco. As próximas execuções obteriam 4, 8, 16, 32 e 64 quanta, embora ele tivesse usado
apenas 37 dos últimos 64 quanta para realizar seu trabalho. Seriam necessárias somente sete
trocas entre a memória e o disco (incluindo a carga inicial), em vez de cem para um algoritmo
20
CORBATÓ, F. J.; MERWIN‐DAGGETT, M.; DALEY, R. C. “An experimental time‐sharing system”, Proc. AFIPS Fall Joint
Computer Conf., AFIPS, 1962, p. 335‐344.
puramente circular. Além disso, à medida que o processo se aprofundasse mais nas filas de
prioridade, ele seria cada vez menos freqüentemente executado, liberando a UCP para
processos interativos e rápidos (TANENBAUM, 2003, p. 106).
Assim, um algoritmo de escalonamento por múltiplas filas divide a fila de processos
prontos em várias filas separadas (Figura 2.11). Os processos são permanentemente atribuídos
a uma fila, geralmente com base em alguma propriedade do processo, tais como tamanho da
memória, prioridade do processo ou tipo de processo. Cada fila tem seu próprio algoritmo de
escalonamento. Por exemplo, filas separadas podem ser usadas para processos de primeiro e
segundo planos. Afila de primeiro plano pode ser escalonada por um algoritmo RR, enquanto a
fila de segundo plano é escalonada por um algoritmo FIFO (SILBERSCHATZ, A.; GALIN, P.;
GAGNE, G., 2000, p. 106).
Figura 2.11. Escalonamento por múltiplas filas
Além disso, deve haver escalonamento entre as filas, que é normalmente
implementado como escalonamento preemptivo de prioridade fixa. Por exemplo, a fila de
primeiro plano pode ter prioridade absoluta sobre a fila de segundo plano (SILBERSCHATZ, A.;
GALIN, P.; GAGNE, G., 2000, p. 106).
Analisando um exemplo de um algoritmo de escalonamento por múltiplas filas com
cinco filas com prioridade descendente:
1. Processos do sistema
2. Processos interativos
3. Processos de edição interativa
4. Processos em batch
5. Processos secundários
Cada fila tem prioridade absoluta sobre as filas de menor prioridade. Nenhum
processo na fila batch, por exemplo, poderia executar a menos que as filas para os processos
do sistema, processos interativos e processos de edição interativa estivessem todas vazias. Se
um processo de edição interativa entrasse na fila de processos prontos enquanto um processo
em batch estivesse executando, o processo em batch seria interrompido. Outra possibilidade é
fracionar o tempo entre as filas. Cada fila obtém uma certa parte do tempo de UCP, que pode
então ser escalonado entre os vários processos na fila. Por exemplo, no exemplo de fila de
primeiro e segundo planos, a fila de primeiro plano pode receber 80% do tempo de UCP para o
escalonamento RR entre seus processos, enquanto a fila de segundo plano recebe 20% da UCP
para dar aos seus processos de modo FIFO (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p.
106).
Para impedir uma longa punição de um processo que, quando iniciado pela primeira
vez, precisasse executar por um longo intervalo de tempo, mas que depois se tornasse
interativo, adotou‐se a seguinte estratégia: se for digitado um <Enter> em um terminal, o
processo pertencente àquele terminal era movido para a classe de prioridade mais alta,
supondo que ele estivesse para se tornar interativo. Entretanto, certo dia, algum usuário com
um processo pesadamente orientado à CPU descobriu que, sentando ao terminal e digitando
<Enter> de maneira aleatória e a intervalos de poucos segundos, fazia maravilhas por seu
tempo de resposta, negligenciando o sistema. Ele contou isso para todos os seus amigos que
passaram também a negligenciar a execução de seus processos. Moral da história: conseguir
acertar na prática é muito mais difícil que acertar na teoria (TANENBAUM, 2003, p. 106).
Muitos outros algoritmos foram usados para atribuir processos a classes de prioridade.
Por exemplo, o influente sistema XDS 940 (LAMPSON, B. W., 1968 apud TANENBAUM, 2003, p.
106) 21, construído em Berkeley, possuía quatro classes de prioridade: terminal, E/S, quantum
curto e quantum longo. Quando um processo que estivesse esperando pela entrada de um
terminal finalmente acordasse, ele iria para classe de prioridade mais alta (terminal). Quando
um processo bloqueado pelo disco ficasse pronto, ele iria para a segunda classe. Se, enquanto
um processo ainda estivesse executando, seu quantum acabasse, seria inicialmente alocado na
21
LAMPSON, B.W. “A scheduling philosophy for multiprogramming systems”. Communications of the ACM, vol. 11, p.
347‐360. Maio 1968.
terceira classe. Contudo, se um processo terminasse seu quantum várias vezes sem ser
bloqueado pelo terminal ou por outra EIS, iria para a última fila. Muitos outros sistemas usam
algo semelhante para favorecer os usuários interativos mais do que os processos em segundo
plano (TANENBAUM, 2003, p. 106).
2.5.4 Escalonamento por múltiplas filas com realimentação
Normalmente, em um algoritmo de escalonamento por múltiplas filas, os processos
são permanentemente atribuídos a uma fila ao entrar no sistema. Os processos não se movem
entre as filas. Se houver filas separadas para processos de primeiro e segundo planos, por
exemplo, os processos não passam de uma fila para outra, já que não mudam sua natureza de
primeiro ou segundo plano. Essa configuração tem a vantagem de apresentar baixo custo de
escalonamento, mas é inflexível (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 106).
O escalonamento por múltiplas filas com realimentação ou filas multiníveis de
retorno, no entanto, permite que um processo se mova entre as filas. A idéia é separar os
processos com diferentes características de surto de UCP. Se um processo usar tempo de CPU
excessivo, será movido para uma fila de menor prioridade. Esse esquema deixa os processo
orientados a E/S e interativos nas filas de prioridade mais alta. Da mesma forma, um processo
que espera demais em uma fila de baixa prioridade pode ser passado para uma fila de maior
prioridade. Essa forma de envelhecimento evita a estagnação (SILBERSCHATZ, A.; GALIN, P.;
GAGNE, G., 2000, p. 106‐107).
22
KLEINROCK, L. “A continuum of time‐sharing scheduling algorithms”, Proceeding of AFIPS, SJCC, 1970, p. 453‐458.
Usualmente há uma fila de nível limite na qual o processo percorre em alternância circular até
concluir. O processo que obtém um processador em seguida é aquele que está no início da fila,
não vazia, de nível mais alto de multinível de retorno. Um processo que está em execução
sofre preempção em favor de outro que está chegando a uma fila de nível mais alto (DEITEL, H.
M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 220‐221).
Nesse sistema, um processo que está em uma fila de nível mais baixo pode sofrer
adiamento indefinido se uma fila de nível mais alto contiver pelo menos um processo. Isso não
pode ocorrer em sistemas cuja taxa de processos que chegam para serem atendidos é alta, ou
nos quais há diversos processos orientados a E/S consumindo seus quanta (DEITEL, H. M.;
DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 220‐221).
Em muitos esquemas de filas multiníveis de retorno, o escalonador aumenta o
tamanho do quantum de um processo à medida que esse passa para cada fila de nível mais
baixo. Assim, quanto mais tempo um processo estiver na rede de enfileiramento, maior será o
quantum que recebe cada vez que obtém um processador (DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R., 2005, p. 220‐221).
Por exemplo, considere um escalonador por múltiplas filas com realimentação que
tenha três filas,numeradas de 0 a 2, onde a fila de menor número comporta os processos de
maior prioridade (Figura 2.12). O escalonador primeiro executa todos os processos na fila 0.
Somente quando a fila 0 estiver vazia, ela executará os processos na fila 1. Da mesma forma,
os processos na fila 2 só serão executados se as filas 0 e 1 estiverem vazias. Um processo que
chega na fila 1 interromperá um processo na fila 2. Um processo na fila 1, por sua vez, será
interrompido por um processo que chega na fila 0 (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G.,
2000, p. 106‐107).
Figura 2.12. Escalonamento por múltiplas filas de realimentação
Um processo que entra na fila de processos prontos é colocado na fila 0.Um processo
na fila 0 recebe um quantum de 8 milissegundos. Se não terminar dentro desse prazo, ele é
passado para o final da fila 1. Se a fila 0 estiver vazia, o processo no início da fila 1 recebe um
quantum de 16 milissegundos. Se não completar, ele será interrompido e colocado na fila 2. Os
processos na fila 2 são executados em modo FIFO, apenas quando as filas 0 e 1 ficam vazias
(SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 107).
Esse algoritmo de escalonamento fornece prioridade mais alta a qualquer processo
com um surto de UCP de 8 milissegundos ou menos. Tal processo obterá UCP rapidamente,
terminará seu surto de UCP e passará para o próximo surto de E/S. Os processos que precisam
de mais de 8, mas menos de 24 milissegundos, também são atendidos rapidamente, embora
com menor prioridade do que os processos mais curtos. Os processos mais longos
automaticamente caem para fila 2 e são servidos na ordem FIFO com quaisquer ciclos de UCP
deixados das filas 0 e 1 (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 107).
Considerando como essa disciplina responde a diferentes tipos de processo é possível
fazer uma análise do tratamento que cada processo recebe em relação ao seu tipo. Filas
múltiplas com realimentação favorecem processos orientados a E/S e outros processos que
necessitam apenas de pequenos surtos de tempo de processador, porque esses processos
entram na rede com prioridade alta e obtêm um processador rapidamente. A disciplina
escolhe um quantum grande o suficiente para a primeira fila, de modo que a vasta maioria dos
processos orientados a E/S (e processos interativos) emita uma requisição de E/S antes da
expiração do quantum. Quando um processo requisita E/S, ele sai da rede de enfileiramento
depois de ter recebido o tratamento favorecido desejado. O processo volta à rede quando
estiver pronto novamente (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 221).
Com relação aos processos orientados à UCP, o processo entra na rede com prioridade
alta, e o sistema o coloca na fila de nível mais alto. Nesse ponto a rede de enfileiramento não
“sabe” se o processo é orientado a UCP ou a E/S – a meta da rede é decidir isso rapidamente.
O processo obtém o processador rapidamente, usa todo o seu quantum, seu quantum expira e
o escalonador passa o processo para a fila seguinte de nível mais baixo. Agora o processo tem
uma prioridade mais baixa e os processos que chegam obtêm o processador primeiro, o que
significa que processos interativos ainda continuarão a receber bons tempos de resposta,
mesmo que muitos processos orientados a UCP passem mais para baixo na rede de
enfileiramento. Eventualmente o processo orientado a UCP obtém o processador, recebe um
quantum maior do que na fila de nível mais alto e novamente usa todo o seu quantum. Então
o escalonador coloca o processo no final da próxima fila de nível mais baixo. O processo
continua a passar para filas de nível mais baixo, espera mais tempo entre intervalos de tempo
e sua todo o seu quantum cada vez que obtém o processador (a menos que sofra preempção
por um processo que chegue). Em algum momento o processo orientado a UCP chega à fila de
nível mais baixo, onde percorre em alternância circular juntamente com outros processos
orientado a UCP até finalizar (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 221).
Portanto, múltiplas filas com realimentação são ideais para separar processos em
categorias segundo suas necessidades de processador. Quando um processo sair da rede de
enfileiramento pode ser “marcado” com a identidade da fila de nível mais baixo na qual
residiu. Quando o processo entrar novamente na rede de enfileiramento, o sistema poderá
colocá‐lo diretamente na fila em que concluiu a operação da última vez – nesse caso o
escalonador estará empregando a heurística que diz que o comportamento recente de um
processo é um bom indicador de seu comportamento futuro próximo. Essa técnica permite ao
escalonador evitar colocar um processo orientado a UCP que está retornando nas filas de nível
mais alto, em que interferiria no serviço para processos curtos de alta prioridade ou processos
orientados a E/S (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 221).
Infelizmente, se o escalonador sempre colocar um processo que retorne na fila de
nível mais baixo que ocupava da última vez que esteve no sistema, o escalonador não
conseguirá responder a mudanças no comportamento do processo (o processo pode estar
transitando de orientado a UCP para orientado a E/S). O escalonador pode resolver esse
problema registrando não somente a identidade da fila de nível mais baixo na qual o processo
residia, mas também a quantidade de seu quantum que não foi utilizada durante sua última
execução. Se o processo consumir todo o seu quantum, então será colocado em uma fila de
nível mais baixo (se houver uma disponível). Se o processo emitir uma requisição de E/S antes
de seu quantum expirar, poderá ser colocado em uma fila de nível mais alto. Se o processo
estiver entrando em uma nova fase na qual mudará de orientado a UCP para orientado a E/S,
inicialmente poderá experimentar alguma lentidão enquanto o sistema determinar que sua
natureza está mudando, mas o algoritmo de escalonamento responderá a essa mudança
(DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 221).
Uma outra maneira de tornar o sistema responsivo a mudanças no comportamento de
um processo é permitir que o processo suba um nível na rede de filas de retorno cada vez que
devolver voluntariamente o processador antes de seu quantum expirar. Similarmente, o
escalonador ‐ ao designar uma prioridade – pode considerar o tempo que um processo tem
gasto à espera por serviço. O escalonador pode envelhecer o processo promovendo‐o e
colocando‐o na fila de nível mais alto seguinte depois de ele ter passado uma certa quantidade
de tempo à espera por serviço (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 221).
Em geral, um escalonador por múltiplas filas com realimentação é definido pelos
seguintes parâmetros:
• O número de filas
• O algoritmo de escalonamento para cada fila
• O método usado para determinar quando promover um processo para
uma fila de maior prioridade
• O método usado para determinar quando rebaixar um processo para
uma fila de menor prioridade
• O método usado para determinar em que fila um processo entrará
quando esse processo precisar de serviço
Uma variação comum do mecanismo de múltiplas filas com realimentação é fazer um
processo passar por alternância circular diversas vezes em cada fila antes de passar para a fila
de nível mais baixo seguinte. E também, o número de ciclos por que passa em cada fila pode
ser aumentado à medida que o processo passa para a fila de nível mais baixo seguinte. Essa
variação tenta refinar mais ainda o serviço que o escalonador fornece a processos orientados a
E/S e a processos orientados a processador (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R.,
2005, p. 221).
Múltiplas filas com realimentação é um bom exemplo de mecanismo adaptativo, um
mecanismo que responde às mudanças no comportamento do sistema que controla (BLEVINS,
P. R.; RAMAMOORTHY, C. V., 1976; POTIER, D.; GELENBE, E.; LENFANT, J., 1976 apud DEITEL, H.
M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 221)23, 24. Mecanismos adaptativos em geral
requerem mais sobrecarga do que os não‐adaptativos, mas a resultante sensibilidade a
mudanças no sistema torna o sistema mais responsivo e ajuda a justificar o aumento de
sobrecarga (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 221).
A definição de um escalonador por múltiplas filas com realimentação torna o algoritmo
de escalonamento de UCP mais geral. Pode ser configurado para corresponder a um sistema
23
BLEVINS, P. R.; RAMAMOORTHY, C. V. “Aspects of a dynamically adaptative operating system”, IEEE Transactions on
Computers, v. 25, nº 7, jul. 1976, p. 713‐715.
24
POTIER, D.; GELENBE, E.; LENFANT, J. “Adaptive allocation of central processing unit Quanta”, Journal of the ACM,
v. 23, nº 1, jan. 1976, p. 97‐102.
específico em desenvolvimento. Infelizmente, requer algum meio segundo o qual seja possível
selecionar valores para todos os parâmetros para definir o melhor escalonador. Embora uma
fila multinível com realimentação seja o esquema mais geral, também é o mais complexo
(SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 107).
2.5.5 Próximo processo mais curto (shortest process next ‐ SPN)
Como o algoritmo de escalonamento SJF sempre resulta no mínimo tempo médio de
resposta para sistemas em lote, seria bom se ele pudesse ser usado para processos interativos
também. Até certo ponto, isso é possível. Processos interativos geralmente seguem o padrão
de esperar por comando, executar comando, esperar por comando, executar comando e assim
por diante. Se a execução de cada comando fosse visualizada como um 'job' isolado, então
seria possível minimizar o tempo de resposta geral executando o SJF.O único problema é saber
qual dos processos atualmente executáveis é o mais curto (TANENBAUM, 2003, p. 107).
Uma saída é realizar uma estimativa com base no comportamento passado e, então,
executar o processo cujo tempo de execução estimado seja o menor. Suponha que o tempo
estimado por comando para algum terminal seja T0 e que sua próxima execução seja medida
como T1. A estimativa pode ser atualizada tomando uma soma ponderada desses dois
números, isto é, aT0 + (1 – a)T1. Pela escolha de a pode‐se decidir se o processo de estimativa
esquecerá rapidamente as execuções anteriores ou se lembrará delas por um longo tempo.
Com a = 1/2, obtém‐se estimativas sucessivas de
T0, T0/2 + T1/2, T0/4 + T1/4 + T2/2, T0/8 + T1/8 + T2/4 + T3/2
Depois de três novas execuções, o peso de T0 na nova estimativa caiu para 1/8.
A técnica de estimar o valor seguinte da série, tomando a média ponderada do valor
sendo medido e a estimativa anterior, é algumas vezes chamada de aging. Essa técnica é
aplicável a muitas situações nas quais é preciso uma previsão baseada nos valores anteriores.
Aging é especialmente fácil de implementar quando a = 1/2. Basta apenas adicionar o novo
valor à estimativa atual e dividir a soma por 2 (deslocando1bit à direita) (TANENBAUM, 2003,
p. 107).
2.5.6 Escolamento Garantido
Um método completamente diferente de lidar com o escalonamento é fazer
promessas reais sobre o desempenho aos usuários e então satisfazê‐los. Uma promessa
realista e fácil de cumprir é a seguinte: se houver n usuários conectados, cada qual receberá
cerca de 1/n de tempo de UCP. De modo semelhante, em um sistema monousuário com n
processos em execução, todos iguais, cada um deve receber 1/n ciclos de UCP (TANENBAUM,
2003, p. 107).
Para fazer valer essa promessa, o sistema deve manter o controle da quantidade de
UCP que cada processo recebe desde sua criação. Ele então calcula a quantidade de UCP
destinada a cada um ou simplesmente o tempo desde a criação dividido por n. Como a
quantidade de tempo de UCP que cada processo realmente teve é também conhecida, torna‐
se fácil calcular a taxa entre o tempo de UCP de fato consumido e o tempo de UCP destinado a
cada processo. Uma taxa de 50% significa que um processo teve somente a metade do que ele
deveria ter e uma taxa de 200% significa que um processo teve duas vezes mais do que lhe foi
destinado. O algoritmo então executará o processo com a taxa mais baixa, até que sua taxa
cresça e se aproxime da taxa de seu competidor (TANENBAUM, 2003, p. 107).
2.5.7 Escalonamento por Loteria
Fazer promessas aos usuários e satisfazê‐Ios é uma boa idéia, porém difícil de
implementar. Contudo, um outro algoritmo pode ser usado com resultados similarmente
previsíveis, mas de implementação muito mais simples. É o chamado escalonamento por
loteria (WALDSPURGER ; WEIHL, 1994 apud TANENBAUM, 2003, p. 107)25.
A idéia básica é dar bilhetes de loteria aos processos, cujos prêmios são vários recursos
do sistema, como tempo de UCP. Se houver uma decisão de escalonamento, um bilhete de
loteria será escolhido aleatoriamente e o processo que tem o bilhete conseguirá o recurso.
Quando aplicado ao escalonamento de UCP, o sistema pode fazer um sorteio 50 vezes por
segundo e, portanto, cada vencedor terá 20 ms de tempo de CPU como prêmio (TANENBAUM,
2003, p. 107).
Parafraseando George Orwell: "Todos os processos são iguais, mas alguns são mais
iguais que os outros". Aos processos mais importantes podem ser atribuídos bilhetes extras
para aumentar suas probabilidades de vitória. Se houver cem bilhetes extras e um processo
detiver vinte deles, esse processo terá uma chance de 20% de vencer cada loteria. Ao longo da
execução, ele obterá 20% da UCP. Diferentemente de um escalonador por prioridades, no qual
é muito difícil estabelecer o que de fato significa uma prioridade 40, aqui há uma regra clara:
um processo que detenha uma fração f dos bilhetes obterá em torno de uma fração f do
recurso em questão (TANENBAUM, 2003, p. 107).
25
WALDSPURGER, C. A.; WEIHL, W. E. Lottery scheduling: Flexible proportionalshare resource management. In
Proceedings of the First Symposium on Operating System Design and Implementation. Nov. 1994, p. 1‐11.
O escalonamento por loteria tem várias propriedades interessantes. Por exemplo, se
aparece um novo processo e a ele são atribuídos alguns bilhetes, já no próximo sorteio da
loteria sua probabilidade de vencer será proporcional ao número de bilhetes que ele tiver. Em
outras palavras, o escalonamento por loteria é altamente responsivo (TANENBAUM, 2003, p.
107‐108).
Os processos cooperativos podem trocar bilhetes entre si, se assim desejarem. Por
exemplo, quando um processo cliente envia uma mensagem para um processo servidor e
então é bloqueado, ele pode dar todos os seus bilhetes ao servidor, para que aumentem as
probabilidades de o servidor executar logo. Quando o servidor termina, retorna os bilhetes
para o cliente executar novamente. Na verdade, na ausência de clientes, os servidores nem
precisam de bilhetes (TANENBAUM, 2003, p. 108).
O escalonamento por loteria pode ser usado para resolver problemas difíceis de
solucionar a partir de outros métodos. Um exemplo é o de um servidor de vídeo, no qual
vários processos alimentam o fluxo de vídeo de seus clientes, mas em diferentes taxas de
apresentação dos quadros. Supondo‐se que os processos precisem de taxas em 10, 20 e 25
quadros/s. Alocando a esses processos 10, 20 e 25 bilhetes, respectivamente, eles vão
automaticamente dividir a UCP aproximadamente na proporção correta, que é 10:20:25
(TANENBAUM, 2003, p. 108).
2.5.8 Escalonamento por Fração Justa (fair share scheduling ‐ FSS)
Até agora partiu‐se do pressuposto de que cada processo é escalonado por si próprio,
sem preocupar‐se com quem é o dono do processo. Como resultado, se o usuário 1 inicia nove
processos e o usuário 2 inicia um processo, com alternância circular ou com prioridades iguais,
o usuário 1 obterá 90% da UCP e o usuário 2 terá somente 10% dela (TANENBAUM, 2003, p.
108).
Para evitar isso, alguns sistemas consideram a propriedade do processo antes de
escaloná‐lo. Nesse modelo, a cada usuário é alocada uma fração da UCP, e o escalonador
escolhe os processos de modo que garanta essa fração. Assim, se dois usuários tiverem 50% da
UCP prometida a cada um deles, cada um obterá os 50%, não importando a quantidade de
processos que eles tenham gerado (TANENBAUM, 2003, p. 108).
Como exemplo, imagine um grupo de pesquisa cujos membros compartilhem um
sistema multiusuário; eles estão divididos em dois grupos. Os pesquisadores principais – cujo
número é pequeno – usam o sistema para executar trabalhos importantes, intensivos em
computação, como fazer simulações. Os assistentes de pesquisa – em grande número – usam
o sistema para trabalhos menos intensivos, como agregar dados e imprimir resultados. Agora
imagine que muitos assistentes de pesquisa e somente um pesquisador principal esteja
utilizando o sistema. Os assistentes de pesquisa podem consumir a maioria do tempo do
processador em detrimento do pesquisador principal que deve executar o trabalho mais
importante. Todavia, se o sistema permitisse que o grupo de assistentes de pesquisa usasse
somente 25% do tempo de processador e permitisse que o grupo de pesquisadores principais
usasse 75%, o pesquisador principal não sofreria tal degradação do serviço. Desse modo, o
escalonamento por fração justa assegura que o desempenho de um processo seja afetado
somente pela população de seu grupo de processo, e não pela população de usuários como
um todo (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 222).
Dessa maneira, em um sistema com dois usuários, cada qual com 50% da UCP
prometida a ele, sendo que o usuário 1 possui quatro processos, A, B, C e D, e o usuário 2
possui somente um processo, E; se for usado o escalonamento circular, uma seqüência
possível de escalonamento que cumpra todas as exigências será a seguinte:
A E B E C E D E A E B E C E D E...
26
NEWBURY, J. P. “Immediate turnaround: an elusive goal”, Software: Practice and Experience, v. 12, nº 10, ou. 1982,
p. 897‐906.
27
HENRY, G. J. “The fair share scheduler”, Bell Systems Technical Journal, v. 63, nº 8, parte 2, out. 1984, p. 1845‐
1857.
28
WOODSIDE, C. M. “Controllability of computer performance tradeoffs obtained using controlled‐share queue
schedulers”, IEEE Transactions on Software Engineering, v. SE‐12, nº 10, out. 1986, p. 1041‐1048.
29
KAY, J.; LAUDER, P. “A fair share scheduler”, Communications of the ACM, v. 31, nº 1, jan. 1988, p. 44‐55.
30
HENRY, G. J. “The fair share scheduler”, Bell Systems Technical Journal, v. 63, nº 8, parte 2, out. 1984, p. 1846.
Por outro
o lado, se ao
o usuário 1 se destinar duas vezes m
mais tempo d
de UCP que p
para o
usuárrio 2, obtém‐se:
A B E C D E A B E C D EE...
Certamen
nte existem inúmeras outras
o possib
bilidades, igu
ualmente paassíveis de serem
s
explo
oradas, dependendo da n
noção de justtiça (TANENB
BAUM, 2003
3, p. 108).
Figura 2..13. Escalonad
dor de processso‐padrão do
o Unix. O escalonador conceede o processsador
aos usuário
os, cada um deeles pode ter muitos proce dade de AT&TT Archives)31
essos (Propried
31
HEN
NRY, G. J. “The fair share sched
duler”, Bell Systeems Technical Journal, v. 63, nº 8, parte 2, ou
ut. 1984, p. 1847.
4. Escalonador por fração ju
Figura 2.14 usta. O escalo
onador por fraação justa divide a capacidaade de
reccursos do sisteema em porçõ por escalonadores de proceesso designados a
ões, as quais ssão alocadas p
vários grupo edade de AT&TT Archives)32
os de fração justa. (Proprie
As instru
uções do Unix estabeleecem grupo
os de fração
o justa e aassociam usu
uários
especcíficos a eless (CHAPTER 9 ud DEITEL, H.. M.; DEITEL,, P. J.; CHOFFNES, D. R., 2005,
9, 2003 apu
22)33. Para o propósito dessa
p. 22 d discusssão, conside
era‐se a premissa de qu
ue o Unix ussa um
escalonador de processo
p po
or prioridadee em alternâância circulaar (HENRY, G
G. J. , 1984 apud
DEITEEL, H. M.; DEITEL,
D 05, p. 222)344. Cada proccesso possui uma
P. J.;; CHOFFNESS, D. R., 200
prioridade, e o esscalonador aassocia proceessos de umaa dada prioridade com a fila de priorridade
para aquele valorr. O escalonaador de proccesso seleciona o processso pronto qu
ue está à fren
nte na
fila de
d prioridadee mais alta. Processos dentro
d de um
ma dada priioridade são
o escalonado
os por
nância circular. Um processo que req
altern quer mais serviços após ssofrer preem
mpção recebe
e uma
prioridade mais b
baixa. Priorid
dades de núccleo são altaas e aplicam‐‐se a processsos que execcutam
úcleo; priorid
no nú dades de usuário são mais baixas. Eventos de disco recebem
m prioridade
e mais
alta do
d que even minal. O escaalonador designa a prioridade do usuário como
ntos de term o uma
razão
o entre a utillização recen
nte do proceessador e o ttempo real d
decorridos; q
quanto mais baixo
32
HEN
NRY, G. J. “The fair share sched
duler”, Bell Systeems Technical Journal, v. 63, nº 8, parte 2, ou
ut. 1984, p. 1846.
33
“C
CHAPTER 9, fair share shceduler”, So olaris 9 Systeem Administrrator Collectio on, 11 nov. 2003,
docs.sun.com/db/doc/806‐4076/6jd d6amqqo?a=vieew.
34
HEN
NRY, G. J. “The fair share sched
duler”, Bell Systeems Technical Journal, v. 63, nº 8, parte 2, ou
ut. 1984, p. 1848.
o tempo decorrido, mais alta a prioridade (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R., 2005,
p. 222).
Os grupos de fração justa são priorizados segundo o quanto estão próximos de atingir
seus objetivos especificados de utilização de recursos. Grupos que estão indo mal recebem
prioridade mais alta; grupos que estão indo bem recebem prioridade mais baixa (DEITEL, H.
M.; DEITEL, P. J.; CHOFFNES, D. R., 2005, p. 223).
2.6 Escalonamento em sistemas de tempo real
2.6.1 Escalonamento por prazo
No escalonamento por prazo, certos processos são escalonados para concluir em um
momento ou prazo específico. Esses processos podem ter alto valor, se entregues a tempo, e
nenhum valor, caso isso não aconteça (NIELSEN, N. R., 1970; MCKELL, L. J.; HANSEN, J. V.;
HEITGER, L. E. , 1979; KLEIJNEN, A. J. V., 1983 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D.
R., 2005, p. 223)35, 36, 37.
O escalonamento por prazo é complexo. Primeiro, o usuário deve fornecer suas exatas
requisições de recursos antecipadamente para garantir que o processo seja concluído dentro
do prazo. Essa informação raramente está disponível. Segundo, o sistema deve executar o
processo que tem prazo sem degradar seriamente o serviço oferecido a outros usuários. O
sistema também deve planejar cuidadosamente seus requisitos de recursos até o final do
prazo, o que pode ser difícil, porque podem chegar novos processos requerendo demandas
imprevisíveis. Por fim, se houver muitos processos com prazos ativos ao mesmo tempo, o
escalonador poderá se tornar extremamente complexo (DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R., 2005, p. 223).
O gerenciamento intensivo de recursos exigido pelo escalonamento por prazo pode
gerar sobrecarga substancial. O consumo líquido de recursos do sistema pode ser alto,
degradando o serviço para outros processos (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R.,
2005, p. 223).
35
NIELSEN, N. R. “The allocation of computing resourses – is pricing the answer?”, Communications of the ACM, v.
13, nº 8, ago. 1970, p. 467‐474.
36
MCKELL, L. J.; HANSEN, J. V.; HEITGER, L. E. “Charging for computer resources”, ACM Computing Surveys, v. 11, nº
2, jun. 1979, p. 105‐120.
37
KLEIJNEN, A. J. V. “Principles of computer charging in a university‐type organization”, Communications of the
ACM, v. 26, nº 11, nov. 1983, p. 926‐932.
demonstrou que, quando todos os processos podem cumprir seus prazos independentemente
da ordem em que são executados, escalonar primeiro os processos de prazo mais curtos é
ótimo38. Entretanto, quando o sistema fica sobrecarregado, esse deve alocar tempo
significativo de processador ao escalonador para determinar a ordem adequada na qual
executar processos para que cumpram seus prazos. Pesquisas recentes concentram‐se na
velocidade ou número de processadores que o sistema deve alocar ao escalonador para
cumprir os prazos (KALYANASUNDARAM, B.; PRUHS, K. , 2002; LAM, T.; TO, K., 2001; KOO, C
et al., 2002 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 223)39, 40, 41.
2.6.2 Escalonamento de Tempo Real
Um objetivo primário dos algoritmos de escalonamento apresentados anteriormente
era garantir alta utilização de recursos. Processos que devem executar periodicamente (como
uma vez por minuto) requerem algoritmos de escalonamento diferentes. Por exemplo, os
tempos de espera ilimitados do SJF podem ser catastróficos para um processo que verifique a
temperatura de um reator nuclear. Similarmente, um sistema que use SRT para escalonar um
processo que produza um videoclipe produziria uma reprodução entrecortada. Escalonamento
de tempo real atende às necessidades de processos que devem produzir saídas corretas em
determinado momento (ou seja, que têm uma restrição de tempo) (STANKOVIC, J., 1996 apud
DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 225)42.
Um sistema de tempo real é aquele no qual o tempo tem uma função essencial. Em
geral, um ou mais dispositivos físicos externos ao computador geram estímulos, e o
computador deve reagir apropriadamente a eles dentro de um dado intervalo de tempo. Por
exemplo, o computador em um tocador de CD obtém os bits que chegam do drive e precisa
convertê‐los em música em um intervalo crítico de tempo. Se o cálculo que ele fizer for muito
demorado, a música soará diferente. Outros exemplos de sistemas de tempo real incluem:
monitoração de pacientes em unidades de terapia intensiva de hospitais, piloto automático de
aeronaves e robôs de controle em fábricas automatizadas. Em todos esses casos, ter a
resposta certa mas tardia é tão ruim quanto não ter nada (TANENBAUM, 2003, p. 108).
38
DERTOUZOS, M. L. “Control robotics: the procedural controlo f physical processes”, Proceedings IFIP Congress,
1974, p. 807‐813.
39
KALYANASUNDARAM, B.; PRUHS, K. “Speed is as powerful as clairvoyance”, Journal of the ACM (JACM), v. 47, nº 4,
jul. 2002, p. 617‐643.
40
LAM, T.; TO, K. “Performance guarantee for online deadline scheduling in the presence of overload”, Proceedings
of the Twelfth Annual ACM‐SIAM Symposium on Discrete Algorithms, 7‐9 jan. 2001, p. 755‐764.
41
KOO, C.; LAM, T.; NGAN, T.; TO, K. “Extra processors versus future information in optimal deadline scheduling”,
Proceedings of the Fourteenth Annual ACM Symposium on Parallel Algorithms and Architectures, 2002, p. 133‐142.
42
STANKOVIC, J. “Real‐time and embedded systems”, ACM Computing Surveys, v. 28, nº 1, mar. 1996, p. 205‐208.
Sistemas de tempo real são em geral categorizados como tempo real crítico, isto é, há
prazos absolutos que devem ser cumpridos para completar uma tarefa crítica ou, então, como
tempo real não crítico, no qual o descumprimento ocasional de um prazo é indesejável,
contudo tolerável (TANENBAUM, 2003, p. 108). Desta forma, as disciplinas de escalonamento
de tempo real são classificadas conforme quão bem cumprem os prazos de um processo.
Escalonamento de tempo real crítico garante que as restrições de prazo de um processo
sejam sempre atendidas. Cada tarefa especificada por um processo de tempo real crítico deve
concluir antes de seu prazo final; caso isso não aconteça, os resultados poderão ser
catastróficos, entre eles trabalho inválido, falha de sistema ou até danos aos usuários do
sistema. Por outro lado, escalonamento de tempo real não crítico garante que processos de
tempo real sejam despachados antes de outros processos do sistema, mas não garante qual
processo, se é que algum deles, cumprirá suas restrições de tempo (XU, J.; PARNAS, D. L.,
1991; STEWART, D.; KHOSLA, P., 1991 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. ,
2005, p. 225)43, 44.
Em geral, em um sistema de tempo real crítico, um processo é submetido juntamente
com uma instrução sobre a quantidade de tempo necessária para concluir ou efetuar uma
operação de entrada e saída. O escalonador admite o processo, garantindo que ele concluirá
no prazo, ou rejeita o pedido como sendo impossível. Essa garantia, feita mediante reserva de
recurso, exige que o escalonador saiba exatamente quanto tempo leva para realizar cada tipo
de função do sistema operacional; portanto, deve‐se garantir a quantidade máxima de tempo
que cada operação utilizará. Essa garantia é impossível em um sistema com armazenamento
secundário ou memória virtual porque esses subsistemas causam variação inevitável e
imprevisível na quantidade de tempo utilizada para executar determinado processo. Portanto,
os sistemas de tempo real crítico são compostos por software de propósito especial
executando em hardware dedicado aos seus processos críticos e não têm a plena
funcionalidade dos computadores e sistemas operacionais modernos (SILBERSCHATZ, A.;
GALIN, P.; GAGNE, G., 2000, p. 108).
A computação de tempo real não‐crítico é menos restritiva. Requer que os processos
críticos recebam prioridade em relação a outros menos favorecidos. Embora acrescentar
funcionalidade de tempo real não‐crítico a um sistema de tempo compartilhado possa causar
alocação injusta de recursos e resultar em atrasos maiores, ou mesmo starvation (paralisação),
43
XU, J.; PARNAS, D. L. “On satisfying timing constraints in hard‐real‐time systems”, Proceedins of the Conference on
Software for Critical Systems. Nova Orleans, LO, 1991, p. 132‐146.
44
STEWART, D.; KHOSLA, P. “Real‐time scheduling of dynamically reconfigurable systems”, Proceedings of the IEEE
International Conference on Systems Engineering. Dayton, OH, ago. 1991, p. 139‐142.
para alguns processos, é pelo menos possível de alcançar. O resultado é um sistema de uso
geral que também pode suportar multimídia, gráficos interativos de alta velocidade e uma
variedade de tarefas que não funcionariam aceitavelmente em um ambiente que não suporta
computação de tempo real não‐crítico (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 108).
O escalonamento de tempo real não crítico é comumente implementado em computadores
pessoais nos quais uma reprodução suave de multimídia é desejável, mas tolera‐se
interrupções ocasionais quando a carga do sistema estiver pesada. Sistemas de tempo real não
crítico podem se beneficiar de taxas altas de interrupção que evitam que o sistema fique
“preso” executando um processo, enquanto outros perdem seus prazos. Todavia, o sistema
pode incorrer em sobrecargas significativas se a taxa de interrupção for muito alta, resultando
em mau desempenho e prazos perdidos (ETSION, Y.; TSAFRIR, D.; FEITEELSON, D., 2003 apud
DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 225)45.
Em ambos os casos, o comportamento de tempo real é implementado dividindo‐se o
programa em vários processos cujos comportamentos são previamente conhecidos. De modo
geral, esses processos têm vida curta e podem executar em bem menos de um segundo.
Quando é detectado um evento externo, o trabalho do escalonador é escalonar os processos
de tal maneira que todos os prazos sejam cumpridos (TANENBAUM, 2003, p. 108). No entanto,
implementar a funcionalidade de tempo real não‐crítico requer o projeto cuidadoso do
escalonador e aspectos relacionados do sistema operacional. Em primeiro lugar, o sistema
deve ter escalonamento por prioridade, e os processos de tempo real devem ter a prioridade
mais alta. A prioridade dos processos de tempo real não deve se degradar com o tempo,
embora a prioridade de outros processos possa. Em segundo lugar, a latência de dispatch deve
ser pequena. Quanto menor a latência, mais rápido o processo de tempo real poderá começar
a execução assim que estiver no estado executável ou pronto (SILBERSCHATZ, A.; GALIN, P.;
GAGNE, G., 2000, p. 108‐109).
Para manter a latência de dispatch baixa, precisa‐se permitir que as chamadas ao
sistema possam ser interrompidas. Existem várias maneiras de atingir essa meta. Uma delas é
inserir pontos de preempção em chamadas ao sistema de longa duração, que verificam se um
processo de alta prioridade precisa ser executado. Se precisar, ocorre uma troca de contexto;
quando o processo de alta prioridade termina, o processo interrompido continua sua chamada
ao sistema. Os pontos de preempção só podem ser colocados em posições seguras no kernel ‐
somente onde as estruturas de dados do kernel não estiverem sendo modificadas. Mesmo
com pontos de preempção, a latência de dispatch pode ser grande, porque é praticável
acrescentar apenas alguns pontos de preempção ao kernel (SILBERSCHATZ, A.; GALIN, P.;
GAGNE, G., 2000, p. 108‐109).
Outro método para lidar com a preempção é tornar todo o kernel preemptível. Para
garantir a operação correta, todas as estruturas de dados do kernel devem ser protegidas com
o uso de vários mecanismos de sincronização. Com esse método, o kernel sempre pode ser
preemptível, porque quaisquer dados sendo atualizados são protegidos contra modificação
pelo processo de alta prioridade. Esse método é usado no Solaris 2 (SILBERSCHATZ, A.; GALIN,
P.; GAGNE, G., 2000, p. 109).
O que acontece se o processo de prioridade mais alta precisar ler ou modificar os
dados do kernel que estão sendo acessados no momento por outro processo de menor
prioridade? O processo de alta prioridade estaria esperando o término de um processo de
menor prioridade. Essa situação é chamada de inversão de prioridade. Na verdade, pode haver
uma cadeia de processos, todos acessando recursos que o processo de alta prioridade precisa.
Esse problema pode ser resolvido via protocolo de herança de prioridade, no qual todos esses
processos (os processos que estão acessando recursos que o processo de alta prioridade
precisa) herdam a alta prioridade até terminarem com o recurso em questão. Quando tiverem
concluído, a sua prioridade volta ao valor original (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G.,
2000, p. 109).
Na Figura 2.15, os componentes de latência de dispatch estão representados. A fase de
conflito da latência de dispatch possui dois componentes:
1. Preempção de qualquer processo em execução no kernel;
2. Liberação por parte de processos de baixa prioridade dos recursos que
o processo de alta prioridade necessita.
Figura 2.15. Latência de dispatch
Como exemplo, no Solaris 2, com a preempção desabilitada, a latência de dispatch fica
acima de 100 milissegundos; com a preempção habilitada, geralmente cai para 2
milissegundos (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 109‐110).
Os eventos aos quais um sistema de tempo real pode precisar responder podem ser
categorizados ainda como periódicos (ocorrem em intervalos regulares) ou aperiódicos –
também chamados de assíncronos ‐ (acontecem de modo imprevisível) (TANENBAUM, 2003,
p. 108‐109; DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 225‐226).
Um sistema pode ter de responder a múltiplos fluxos de eventos periódicos.
Dependendo de quanto tempo cada evento requeira para processar, talvez nem seja possível
tratar de todos. Por exemplo, se houver m eventos periódicos e o evento i ocorrer com
período Pi e requerer Ci segundos de UCP para tratar cada evento, então a carga poderá ser
tratada somente se
m
Ci
∑
i =1 Pi
≤1
Um sistema de tempo real que satisfaça esse critério é chamado de escalonável
(TANENBAUM, 2003, p. 108‐109).
Como exemplo, considere um sistema de tempo real não‐crítico com três eventos
periódicos, com períodos de 100, 200 e 500 ms, respectivamente. Se esses eventos
requererem 50, 30 e 100 ms de tempo de UCP por evento, nessa ordem, o sistema é
escalonável porque 0,5 + 0,15 + 0,2 < 1. Se um quarto evento, com período de 1 s, for
adicionado, o sistema permanecerá escalonável desde que esse evento não precise de mais de
150 ms do tempo de UCP por evento. Implícita nesse cálculo está a hipótese de que o custo
extra do chaveamento de contextoé tão pequeno que pode ser desprezado (TANENBAUM,
2003, p. 109).
Os algoritmos de escalonamento de tempo real podem ser estáticos ou dinâmicos. Os
primeiros tomam suas decisões de escalonamento antes de o sistema começar a executar. Os
últimos o fazem em tempo de execução. O escalonamento estático só funciona quando há
prévia informação perfeita disponível sobre o trabalho necessário a ser feito e os prazos que
devem ser cumpridos. Os algoritmos de escalonamento dinâmico não apresentam essas
restrições (TANENBAUM, 2003, p. 109).
2.6.3 Algoritmos de escalonamento de tempo real estáticos
Algoritmos de escalonamento de tempo real estáticos não ajustam a prioridade do
processo ao longo do tempo. Porque as prioridades são calculadas somente uma vez, esses
algoritmos tendem a ser simples e a incorrer em pouca sobrecarga. Eles são limitados, pois não
podem se ajustar ao comportamento variável do processo e dependem de os recursos estarem
funcionando e à sua disposição para garantir que sejam cumpridas as restrições de tempo
(DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 226).
Sistemas de tempo real críticos tendem a usar algoritmos de escalonamento estáticos,
porque incorrem em baixa sobrecarga e é relativamente fácil de provar que as restrições de
prazo de cada processo sejam atendidas. O algoritmo de escalonamento por taxa monotônica
(Rate Monotonic ‐ RM), por exemplo, é um algoritmo de alternância circular preemptivo, por
prioridade, que eleva a prioridade de um processo linearmente (monotonicamente) com a
freqüência (a taxa) com a qual ele deve executar. Esse algoritmo de escalonamento estático
favorece processos periódicos que executam freqüentemente (STEWART, D.; KHOSLA, P., 1991
apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 226)46. O algoritmo por taxa
monotônica com prazo pode ser usado quando um processo periódico especifica um prazo
46
STEWART, D.; KHOSLA, P. “Real‐time scheduling of dynamically reconfigurable systems”, Proceedings of the IEEE
International Conference on Systems Engineering. Dayton, OH, ago. 1991, p. 139‐142.
que não seja igual ao seu período (POTKONJAK, M.; WOLF, W., 1999 apud DEITEL, H. M.;
DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 226)47.
2.6.4 Algoritmos de escalonamento de tempo real dinâmicos
Algoritmos de escalonamento de tempo real dinâmicos escalonam processos
ajustando suas prioridades durante a execução, o que pode causar sobrecarga significativa.
Alguns algoritmos tentam minimizar a sobrecarga de escalonamento designando prioridades
estáticas a alguns processos, e prioridades dinâmicas a outros (XU, J.; PARNAS, D. L., 1991
apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 226)48.
O algoritmo de escalonamento por prazo mais curto primeiro (Earliest Deadline First ‐
EDF) é do tipo preemptivo que despacha primeiro o processo com o prazo mais curto. Se o
processo que chegar tiver um prazo mais curto do que o processo em execução, o sistema
provocará a preempção do processo em execução e despachará o que acabou de chegar. O
objetivo é maximizar o rendimento cumprindo os prazos do maior número de processos por
unidade de tempo (análogo ao algoritmo STR) e minimizando o tempo médio de espera (o que
evita que processos curtos percam seus prazos enquanto processos longos executam).
Dertouzos (1974, apud Deitel, H. M.; Deitel, P. J.; Choffnes, D. R., 2005, p. 226) provou que, se
um sistema fornecer preempção por hardware (temporizadores de interrupção) e os processos
de tempo real que estão em escalonamento não forem independentes, o EDF minimizará a
quantidade de tempo pela qual o projeto “mais atrasado” perde seu prazo49. Todavia, muitos
sistemas de tempo real não fornecem preempção por hardware, portanto outros algoritmos
devem ser empregados (STEWART, D.; KHOSLA, P., 1991 apud DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R. , 2005, p. 226)50.
O algoritmo de escalonamento por folga mínima primeiro (Minimum Laxity First ‐
MLF) é similar ao EDF, mas baseia sua prioridade na folga de um processo. Folga é uma medida
da importância de um processo baseada na quantidade de tempo que falta até seu prazo final
e o tempo de execução que ainda resta até que sua tarefa (que pode ser periódica) tenha
terminado. A folga é calculada usando‐se a fórmula
L = D – (T + C),
47
POTKONJAK, M.; WOLF, W. “A methodology and algorithms for the design of hard real‐time multitasking ASICs”,
ACM Transactions on Design Automation of Electronic Systems (TODAES), v. 4, nº 4, out. 1999.
48
XU, J.; PARNAS, D. L. “On satisfying timing constraints in hard‐real‐time systems”, Proceedins of the Conference on
Software for Critical Systems. Nova Orleans, LO, 1991, p. 132‐146.
49
DERTOUZOS, M. L. “Control robotics: the procedural control of physical processes”, Information Processing, v. 74,
1974.
50
STEWART, D.; KHOSLA, P. “Real‐time scheduling of dynamically reconfigurable systems”, Proceedings of the IEEE
International Conference on Systems Engineering. Dayton, OH, ago. 1991, p. 139‐142.
onde L é a folga, D é o prazo do processo, T é o tempo atual e C é o tempo de execução
restante do processo. Por exemplo, se o tempo atual for 5, o prazo de um processo for 9 e o
processo precisar de 3 unidades de tempo para concluir, a folga será 1. Se um processo tiver
folga 0, deverá ser despachado imediatamente ou perderá seu prazo. Prioridades no
escalonamento por folga‐mínima‐primeiro são mais precisas do que as do EDF, porque são
determinadas incluindo o tempo de processador restante que cada processo requer para
concluir sua tarefa. Porém, às vezes essa informação não está disponível (STEWART, D.;
KHOSLA, P., 1991 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 226)51.
2.7 Escalonamento de threads
Antes de abordar o escalonamento de threads deve‐se diferenciar os SOs que
reconhecem threads com os que não reconhecem. Os SOs que não reconhecem threads junto
com seus processos de usuário devem carregar uma biblioteca que fará o papel de intermédio
entre as threads do processo e o kernel. Já os SOs que reconhecem threads não necessitam
destas bibliotecas pois o próprio kernel se encarregará dessas rotinas. No Primeiro caso a
biblioteca de threads escalona threads de usuário para a execução em um LWP disponível, um
esquema chamado escalonamento local ao processo. Considere‐se LWP como processo leve.
No segundo caso o kernel utiliza o escalonamento global do sistema para decidir que thread de
kernel escalonar (SILBERSCHATZ, A.; GALIN, P.; GAGNE, G., 2000, p. 110).
Para ilustrar esta explicação observa‐se na figura 2.16(a) os SOs orientados a processos
com suporte a threads e na figura 2.16(b) os SOs orientados a threads.
(a) (b)
Figura 2.16. Processos e threads. (a) SO orientado a processos com suporte a threads. (b) SO orientado a
threads.
51
STEWART, D.; KHOSLA, P. “Real‐time scheduling of dynamically reconfigurable systems”, Proceedings of the IEEE
International Conference on Systems Engineering. Dayton, OH, ago. 1991, p. 139‐142.
mplo 1: Solaris 2
Exem
Este SO eescala thread
ds baseados eem prioridad
des que são ssubdividas em 4 níveis: ttempo
real, sistema, inteerativo e tem
mpo comparttilhado.
Os thread
ds na classe d prioridade paara executar entre
de tempo reeal recebem aa mais alta p
todass as classes. Essa atribuiçção permite que um pro
ocesso de tempo real ten
nha uma ressposta
ntida do sisteema em um período limitado de tem
garan mpo. (SILBERSSCHATZ, A.; G
GALIN, P.; GA
AGNE,
G., 20
000, p. 111).
2.17 ilustra o
A figura 2 o escalonameento de threads no Solaris 2.
Figura 2.17. Escalonamentto no Solaris 2
2
Exem
mplo 2: Java
Ao implementar threeads de usuáário, o tempo de execuçção Java conta com intervalos
de teempo para executar
e o de threads (DEITEL, H. M.; DEITEL,, P. J.;
escaalonamento preemptivo
CHOFFFNES, D. R. , 2005, p. 22
27).
O Escalon
nador de threead Java garrante que o tthread de priioridade maiis alta da mááquina
virtuaal Java esteeja executan
ndo o temp
po todo. Se
e houver mú
últiplos threeads no nívvel de
prioridade, essess executarão
o usando alternância
a circular.
c A Figura
F 2.18 ilustra a fiila de
prioridade multin
nível de priorridade para tthreads Javaa. Na figura ssupondo um computador com
o processado
único or, cada um dos thread
ds, A e B, exxecuta duran
nte um quan
ntum por ve
ez em
altern
nância circullar até que aambos concluam a execu
ução. Em segguida, o threead C executta até
finalizar. Os threaads D, E e F executam durante um q
quantum cad
da um, em alternância circular
até que
q todos concluam a execução.EEsse processo continuaa até que ttodos os th
hreads
execu
utem até o final. Note que,
q depend
dendo do sisstema operaacional, threeads que che
egam,
cuja prioridade mais
m alta, po
odem adiar indefinidam
mente a execcução daqueeles de priorridade
mais baixa (DEITEEL, H. M.; DEITEL, P. J.; CH
HOFFNES, D.. R. , 2005, p. 227‐228).
Figura 2..18. Escalonam
mento de thre
ead Java por p
prioridade
2.8 Esscalonamento de Multip processadores
O objetivvo do escalon
namento commo já foi dito anteriormente é maximizar rendim
mento
bem como minim po de respostta e colocar prioridades de escalonamento tanto
mizar o temp o para
mas
sistem mono
oprocessadoss como em mas
sistem multip
processados. Os Sisttemas
multiiprocessadoss diferentem
mente dos monoprocesssados, que simplesmente despach
ha os
proceessos, devem
m assegurar que os processadore
p es não fiquem ociosos enquanto estão
esperrando que os processos executem (D
DEITEL, H. M.; DEITEL, P. J.; CHOFFNEES, D. R. , 20
005, p.
463).
Todos os algoritmos vistos anteriormente de escalonamento são validos para
multiprocessadores com pequenas diferenças evidentes, pois tinha um processador e agora
tem‐se vários.
2.9 Política versus Mecanismo
Até o momento, tem‐se presumido tacitamente que todos os processos no sistema
pertencem a usuários diferentes e estão, portanto, competindo pela UCP. Embora isso seja
muitas vezes verdade, um processo pode ter muitos filhos executando sob seu controle ‐
como, por exemplo, um processo de um sistema de gerenciamento de bancos de dados. Cada
filho pode estar atendendo uma requisição diferente ou ter uma função específica para
realizar (análise sintática de consultas, acesso a disco etc.). É totalmente possível que o
processo principal tenha uma idéia clara de quais de seus filhos sejam os mais importantes (ou
tenham tempo crítico) e quais sejam os menos importantes. Infelizmente, nenhum dos
escalonadores discutidos anteriormente aceita qualquer entrada proveniente de processos do
usuário sobre decisões de escalonamento. Como resultado, o escalonador raramente faz a
melhor escolha (TANENBAUM, 2003, p. 109).
A solução para esse problema é separar o mecanismo de escalonamento da política de
escalonamento. Isso significa que o algoritmo de escalonamento é de algum modo
parametrizado, mas que os parâmetros podem ser preenchidos pelos processos dos usuários.
Considerando novamente o exemplo do banco de dados. Supondo‐se que o núcleo use o
algoritmo de escalonamento por prioridades, mas que faça uma chamada ao sistema na qual
um processo seja capaz de configurar (e alterar) as prioridades e seus filhos. Desse modo, o pai
pode controlar em detalhes como seus filhos são escalonados, mesmo que ele próprio não
faça o escalonamento. Nesse exemplo, o mecanismo de escalonamento está no núcleo, mas a
política é estabelecida por um processo de usuário (TANENBAUM, 2003, p. 109).
2.10 Avaliação de Algoritmos
Existem muitos algoritmos de escalonamento, cada qual com seus próprios
parâmetros. Como resultado, selecionar um algoritmo de escalonamento de UCP para
determinado sistema pode se tornar uma tarefa difícil.
O primeiro problema é definir os critérios usados na seleção de um algoritmo. Os
critérios geralmente são definidos em termos de utilização da UCP, tempo de resposta ou
throughput. Para selecionar um algoritmo, deve‐se primeiro definir a importância relativa
dessas medidas. Esses critérios podem incluir várias medidas, como:
• Maximizar a utilização de UCP sob a limitação de que o tempo
de resposta máximo é 1 segundo;
• Maximizar o throughput de modo que o tempo de retorno seja
(em média) linearmente proporcional ao tempo de execução total.
Assim que os critérios de seleção tiverem sido definidos, deve‐se avaliar os vários
algoritmos em consideração. Existem vários métodos de avaliação diferentes, como os que
serão apresentados a seguir.
2.10.1 Modelagem determinista
Uma classe importante de métodos de avaliação é a avaliação analítica. A avaliação
analítica utiliza o algoritmo dado e o volume de trabalho do sistema para gerar uma fórmula
ou número que avalia o desempenho do algoritmo para aquele volume de trabalho.
Um tipo de avaliação analítica é a modelagem determinista. Esse método pega um
determinado volume de trabalho predeterminado e define o desempenho de cada algoritmo
para esse volume de trabalho.
Por exemplo, considere o volume de trabalho indicado na tabela abaixo. Todos os
cinco processos chegam no instante 0, na ordem dada, com a duração do surto de UCP
expressa em milissegundos:
Tabela 2.6 – Duração de surtos da UCP, em milissegundos
Processo Duração de surto
P1 10
P2 29
P3 3
P4 7
P5 12
Considere os algoritmos de escalonamento FIFO, SJF e RR (quantum = 10
milissegundos) para esse conjunto de processos. Qual dos algoritmos deve dar o mínimo
tempo de espera médio?
Para o algoritmo FIFO, os processos seriam executados da seguinte maneira:
O tempo de espera é 0 milissegundo para o processo P1, 10 milissegundos para o
processo P2, 39 milissegundos para o processo P3, 42 milissegundos para o processo P4 e 49
milissegundos para o processo P5. Assim, o tempo de espera médio é (0 + 10 + 39 + 42 + 49)/5
= 28 milissegundos.
Com o escalonamento SJF, os processos seriam executados na forma que segue:
O tempo de espera é 10 milissegundos para o processo P1, 32 milissegundos para o
processo P2, 0 milissegundo para o processo P3, 3 milissegundos para o processo P4 e 20
milissegundos para o processo P5. Logo, o tempo de espera médio é (10 + 32 + 0 + 3 + 20)/5 =
13 milissegundos.
Com o algoritmo RR, os processos seriam executados assim:
O tempo de espera é 0 milissegundos para o processo P1, 32 milissegundos para o
processo P2, 20 milissegundo para o processo P3, 23 milissegundos para o processo P4 e 40
milissegundos para o processo P5. Assim, o tempo de espera médio é (0 + 32 + 20 + 23 + 40)/5
= 23 milissegundos.
Verifica‐se, nesse caso, que a regra SJF resulta em menos do que a metade do tempo
de espera médio obtido com o escalonamento FIFO; o algoritmo RR retorna um valor
intermediário.
Em geral, no entanto, a modelagem determinista é específica demais e requer muito
conhecimento exato para ser útil.
2.10.2 Modelo de filas
Os processos que são executados em muitos sistemas variam todos os dias, por isso
não existe um conjunto fixo de processos (e tempos) para uso da modelagem determinista. O
que pode ser determinado, no entanto, é a distribuição dos surtos de UCP e de E/S. Essas
distribuições podem ser medidas e aproximadas, ou simplesmente estimadas. O resultado é
uma fórmula matemática para descrever a probabilidade de determinado surto de UCP.
Geralmente, essa distribuição é exponencial e é descrita pela sua média. Da mesma forma, a
distribuição dos tempos de chegada dos processos no sistema (a distribuição do tempo de
chegada) deve ser dada. A partir dessas duas distribuições, é possível calcular o valor médio de
throughput, utilização, tempo de espera etc. para a maioria dos algoritmos.
O sistema de computação pode ser descrito como uma rede de servidores. Cada
servidor tem uma fila de processos em espera. A UCP é um servidor com sua fila de processos
prontos, assim como o sistema de entrada e saída com suas filas de dispositivos. Conhecendo
as taxas de chegada e as taxas de serviço, pode‐se calcular a utilização, o tamanho médio da
fila, o tempo de espera médio etc. Essa área de estudo é chamada de análise de redes de filas.
Como exemplo, seja n o tamanho médio da fila (excluindo o processo sendo atendido),
seja W o tempo de espera médio na fila e λ a taxa de chegada média para novos processos na
fila (como três processos por segundo). Assim, espera‐se que durante o tempo W que
determinado processo espera, λ x W novos processos chegarão na fila. Se o sistema estiver em
uma situação estável, o número de processos que saem da fila deverá ser igual ao número de
processos que chegam. Assim,
n = λ x W
Essa equação, conhecida como a fórmula de Little, é particularmente útil porque é
válida para qualquer algoritmo de escalonamento e distribuição de chegada.
Pode‐se usar a fórmula de Little para calcular uma das três variáveis, se as outras duas
forem conhecidas. Por exemplo, sabendo que sete processos chegam a cada segundo (em
média), e que existem normalmente 14 processos na fila, pode‐se calcular o tempo de espera
médio por processo como sendo de 2 segundos.
A análise de filas pode ser útil na comparação dos algoritmos de escalonamento, mas
também tem limitações. No momento, as classes de algoritmos e distribuições que podem ser
tratadas são bem limitadas. A matemática dos algoritmos ou distribuições complicadas pode
ser difícil de dominar. Assim, as distribuições de chegada e serviço geralmente são definidas de
forma irrealista, mas matematicamente tratáveis. Geralmente, também é necessário fazer
uma série de hipóteses independentes, que talvez não sejam precisas. Assim, para que uma
resposta possa ser calculada, os modelos de fila geralmente são apenas uma aproximação de
um sistema real. Como resultado, a precisão dos resultados calculados pode ser questionável.
2.10.3 Simulações
Para obter uma avaliação mais precisa dos algoritmos de escalonamento, pode utilizar
simulações. Executar simulações envolve programar um modelo de sistema de computador.
Estruturas de dados em software representam os principais componentes do sistema. O
simulador tem uma variável que representa o relógio; à medida que o valor dessa variável
aumenta, o simulador modifica o estado do sistema para refletir as atividades dos dispositivos,
dos processos e do escalonador. À medida que a simulação executa, estatísticas que indicam o
desempenho do algoritmo são coletadas e impressas.
Os dados que conduzem a simulação podem ser gerados de várias formas. O método
mais comum utiliza um gerador de números aleatórios, que é programado para gerar
processos, tempos de surtos de UCP, chegadas, partidas e assim por diante, de acordo com
distribuições de probabilidade. As distribuições podem ser definidas matematicamente
(uniforme, exponencial, Poisson) ou empiricamente. Se a distribuição for definida
empiricamente, são feitas medidas do sistema real sendo estudado. Os resultados definem a
distribuição real de eventos no sistema real; essa distribuição pode então ser usada para
conduzir a simulação.
Uma simulação baseada em distribuição, no entanto, pode ser imprecisa devido aos
relacionamentos entre eventos sucessivos no sistema real. A distribuição de freqüência indica
apenas quantos eventos ocorrem; não revela nada sobre a ordem de sua ocorrência. Para
corrigir esse problema, pode‐se usar registros de execução. Um registro de execução é criado
por meio da monitoração do sistema real, registrando‐se a seqüência de eventos reais, como
ilustrado na figura a seguir.
Figura 2.19. Avaliação dos escalonadores de UCP por simulação
Em seguida, essa seqüência é usada para conduzir a simulação. Os registros de
execução fornecem uma forma excelente de comparar dois algoritmos exatamente no mesmo
conjunto de entradas reais. Esse método pode produzir resultados precisos para suas
entradas.
As simulações podem ser caras, geralmente exigindo horas de tempo de computador.
Uma simulação mais detalhada fornece resultados mais precisos, mas requer mais tempo de
computação. Além disso, registros de execução podem exigir grandes quantidades de espaço
de armazenamento. Finalmente, o projeto, codificação e depuração do simulador pode ser
uma grande tarefa.
2.10.4 Implementação
Mesmo uma simulação é de precisão limitada. A única forma completamente precisa
de avaliar um algoritmo de escalonamento é codificá‐lo, colocá‐lo no sistema operacional e
verificar seu funcionamento. Essa abordagem coloca o algoritmo real no sistema real para
avaliação em condições reais de operação.
A principal dificuldade dessa abordagem é o alto custo. Os custos envolvem não
apenas a codificação do algoritmo e modificação do sistema operacional para suportá‐lo e as
estruturas de dados exigidas por ele, mas também a reação dos usuários a um sistema
operacional em constante mudança. A maior parte dos usuários não está interessada em
construir um sistema operacional melhor; simplesmente querem que seus processos sejam
executados e desejam utilizar seus resultados. Um sistema operacional em constante mudança
não ajuda os usuários a realizar seu trabalho.
A outra dificuldade com qualquer avaliação de algoritmo é que o ambiente no qual o
algoritmo é usado será alterado. O ambiente mudará não só da forma normal, à medida que
novos programas são escritos e os tipos de problemas mudam, mas também como resultado
do desempenho do escalonador. Se processos curtos tiverem prioridade, os usuários poderão
quebrar processos maiores em grupos de processos menores. Se os processos interativos
tiverem prioridade sobre processos não‐interativos, os usuários poderão mudar para uso
interativo.
Por exemplo, pesquisadores projetaram um sistema que classificava os processos
interativos e não‐interativos automaticamente, analisando a quantidade de entrada e saída no
terminal. Se um processo não tivesse entrada nem saída para o terminal em um intervalo de 1
segundo, ele era classificado como não‐interativo e movido para uma fila de baixa prioridade.
Essa regra resultou em uma situação na qual um programador modificava seus programas para
escrever um caractere arbitrário para o terminal em intervalos regulares de menos de 1
segundo. O sistema dava a seus programas uma alta prioridade, embora a saída no terminal
fosse completamente sem significado.
Os algoritmos de escalonamento mais flexíveis podem ser alterados pelos gerentes do
sistema ou pelos usuários de modo que possam ser ajustados para uma aplicação ou conjunto
de aplicações específicas. Por exemplo, uma estação de trabalho que executa aplicações
gráficas de alto nível pode ter necessidades de escalonamento diferentes daquelas de um
servidor Web ou servidor de arquivos. Alguns sistemas operacionais ‐ particularmente várias
versões do UNIX ‐ permitem ao administrador do sistema ajustar os parâmetros de
escalonamento para uma configuração de sistema específica. Outra abordagem é usar APIs
como yield( ) e setPriority( ), permitindo assim que as aplicações tenham comportamento
previsível. A desvantagem dessa abordagem é que o ajuste de desempenho de um sistema ou
aplicação muitas vezes não resulta em desempenho melhorado em situações mais gerais.
CAPÍTULO 3
ESTUDOS DE CASO
3.1 Escalonamento no UNIX
O Unix é um sistema interativo projetado para tratar múltiplos processos e usuários ao
mesmo tempo. Ele foi projetado por e para programadores, para ser usado em um ambiente
no qual a maioria dos usuários é relativamente sofisticada e engajada em projetos de
desenvolvimento de software (freqüentemente complexos). Em muitos casos, um grande
número de programadores coopera ativamente para produzir um único sistema e nesse
sentido o Unix tem extensos recursos para permitir que as pessoas trabalhem juntas e
compartilhem informação de maneira controlada. O modelo de grupo de programadores
experientes trabalhando juntos para produzir software avançado é obviamente muito
diferente do modelo de computação pessoal de um único principiante trabalhando sozinho
com um processador de texto, e essa diferença é refletida por todo o Unix desde o início até o
fim.
Um sistema Unix pode ser considerado um tipo de pirâmide, como ilustrado na Figura
3.1.
Figura 3.1. As camadas em um sistema Unix
Na base está o hardware, que é formado de UCP, memória, discos, terminais e outros
dispositivos. Executando diretamente sobre o hardware está o sistema operacional Unix. Sua
função é controlar o hardware e fornecer uma interface de chamadas aos sistema para todos
os programas. Essas chamadas ao sistema permitem que os programas do usuário criem e
gerenciem processos, arquivos e outros recursos. Algumas das principais chamadas ao sistema
do Unix são relacionadas na figura abaixo.
Figura 3.2. Algumas chamadas ao sistema relacionadas com processos. O código de retorno s é ‐1
quando ocorre um erro, pid é o ID do processo e residual é o tempo restante no alarme anterior. Os
parâmetros são aqueles sugeridos pelos próprios nomes.
As únicas entidades ativas no Unix são os processos, muito similares aos processos
seqüenciais clássicos já discutidos anteriormente neste trabalho. Cada processo executa um
único programa e inicialmente tem um único thread de controle. Em outras palavras, ele
possui um único contador de programa que guarda o caminho da próxima instrução a ser
executada. Muitas versões do Unix permitem que um processo crie outros threads depois de
iniciar sua execução.
As primeiras versões do Unix não tinha threads. Essa característica foi adicionada
muitos anos depois. Inicialmente havia muitos pacotes de threads em uso, mas a proliferação
desses pacotes dificultava a escrita de códigos portáteis. Por fim, as chamadas ao sistema
usadas para gerenciar threads foram padronizadas como parte do POSIX (P1003.1c).
A especificação POSIX não estabelece se os threads devem ser implementados no
núcleo ou no espaço do usuário. A vantagem de ter os threads no espaço do usuário é que eles
podem ser implementados sem a necessidade de alterar o núcleo, além de o chaveamento de
contexto de threads ser muito eficiente. A desvantagem dos threads no espaço do usuário é
que quando um thread é bloqueado (por exemplo, em uma E/S, um semáforo ou uma falta de
página), todos os threads daquele processo também são bloqueados, pois o núcleo pensa que
existe somente um thread e não escalona o processo até que aquele thread seja liberado.
Assim, as chamadas definidas no P1003.1c foram cautelosamente escolhidas para ser
implementadas das duas maneiras. Conforme os programas dos usuários aderem
cuidadosamente à semântica do P1003.1c, as duas implementações devem funcionar
corretamente.
Um daemon típico é o cron. Ele acorda uma vez por minuto para verificar se existe
algum trabalho para fazer. Caso exista, ele faz o trabalho. Depois, ele volta a dormir até que
decorra o tempo da próxima verificação. Esse daemon é necessário porque o Unix permite
agendar atividades para serem executadas minutos, horas, dias ou mesmo meses depois. Além
disso, o daemon cron também é usado para agendar atividades periódicas, como realizar
backups do disco diariamente ou anualmente, ativar lembretes, etc. Outros daemons tratam
as mensagens eletrônicas que chegam e que saem, gerenciam a fila da impressora de linha,
verificam a quantidade de páginas na memória e assim por diante. Daemons são simples de
implementar no Unix porque cada um é um processo separado, independente de todos os
demais processos.
Como o Unix sempre constitui um sistema de multiprogramação, seu algoritmo de
escalonamento foi projetado desde o início para fornecer uma boa resposta aos processos
interativos. Ele é um algoritmo de dois níveis. O algoritmo do nível inferior escolhe para
executar o próximo processo de um conjunto de processos que estão na memória e o disco de
modo que todos os processos tenham a oportunidade de estar na memória e assim poderem
executar.
Cada versão do Unix tem um algoritmo de escalonamento de nível inferior
ligeiramente diferente, mas muitos deles são parecidos com o modelo genérico que será
apresentado. O algoritmo de nível inferior usa múltiplas filas. Cada fila é associada a uma
prioridade dentro de uma faixa de valores de prioridades não sobrepostos. Os processos
executando no modo do usuário (o topo do iceberg) têm valores positivos. Os processos que
executam no modo núcleo (fazendo chamadas ao sistema) apresentam valores negativos. Os
valores negativos possuem maiores prioridades e os valores positivos grandes têm as menores
prioridades, como ilustrado na figura que segue. Somente os processos que estão na memória
e prontos para a execução são colocados nas filas, visto que a escolha deve ser feita desse
conjunto.
Figura 3.3. O escalonador do Unix é baseado em uma estrutura de fila multinível
Quando o escalonador (de nível inferior) executa, ele investiga as filas começando com
a de maior prioridade (isto é, maior valor negativo) até encontrar uma fila que esteja ocupada.
O primeiro processo dessa fila é então escolhido e iniciado. Esse processo tem a permissão de
executar no máximo durante um quantum, geralmente 100 ms, ou até ser bloqueado. Se o
processo usar todo o seu quantum, ele será colocado de volta no final de sua fila e o algoritmo
de escalonamento executará novamente. Assim, os processos com a mesma prioridade
compartilham a UCP usando o algoritmo alternância circular (round‐robin).
Uma vez por segundo, a prioridade de cada processo é recalculada de acordo com uma
fórmula de três componentes:
Prioridade = UCP_usage + nice + base
Com base em sua nova prioridade, cada processo é ligado a uma fila apropriada da
Figura 3.3, cujo número é obtido geralmente a partir da divisão da prioridade por uma
constante. Cada um dos três componentes da fórmula apresentada são analisados a seguir.
O termo UCP_usage representa o número médio de tiques de relógio por segundo que
o processo usou durante os últimos segundos decorridos. A cada tique do relógio, o contador
de uso de UCP na entrada da tabela de processos do processo em execução é acrescido de 1.
Esse contador no final será adicionado à prioridade do processo dando a ele um valor
numérico maior e assim colocando‐o em uma fila de menor prioridade.
Contudo, o Unix não pune um processo para sempre, pelo uso da UCP e, por isso, faz o
termo UCP_usage diminuir com o tempo. Diferentes versões do Unix implementam essa
diminuição de maneiras ligeiramente diferentes. Um modo que tem sido empregado baseia‐se
em adicionar o valor atual do UCP_usage ao número de tiques adquiridos no intervalo Δt
anterior e dividir o resultado por 2. Esse algoritmo pondera o Δt mais recente por ½, o Δt
anterior por ¼ e assim por diante. Esse algoritmo de ponderação é muito rápido, pois consiste
apenas em uma adição e um deslocamento, mas há ainda outros esquemas de ponderação.
Cada processo tem um valor nice associado a ele. O valor‐padrão é 0, mas o alcance
permitido é geralmente de ‐20 a +20. Um processo pode ajustar nice para um valor entre 0 e
20 por intermédio da chamada ao sistema nice. Um usuário que calcula que p para um bilhão
de casas decimais, sem segundo plano, pode colocar essa chamada em seu programa para ser
camarada (nice, em inglês) com os outros usuários. Somente o administrador do sistema pode
solicitar um serviço melhor do que o normal (que significa valores de ‐20 a 1).
Quando um processo interrompe o núcleo, via software, para fazer uma chamada ao
sistema, é perfeitamente possível que o processo tenha de ser bloqueado antes de completar
a chamada ao sistema e retornar para o modo usuário. Por exemplo, o processo pode
simplesmente ter realizado uma chamada ao sistema waitpid e com isso ter de esperar pelo
término de um de seus filhos. Ele pode também precisar aguardar pela conclusão de uma
entrada do terminal ou de uma E/S no disco, entre outras possibilidades. Ao ser bloqueado, ele
é removido da estrutura de fila, pois já não está mais apto a executar.
No entanto, quando ocorre o evento que ele estava esperando, ele é colocado de volta
na fila com um valor negativo. A escolha da fila é determinada pelo evento que ele aguardava
acontecer. N figura 3.3, a E/S no disco é mostrada como tendo alta prioridade, de modo que
um processo que apenas leu ou escreveu em um bloco do disco provavelmente obterá a UCP
dentro de 100 ms. A prioridade relativa da E/S do disco, da E/S do terminal etc. é fixada dentro
do sistema operacional e somente pode ser modificada pela alteração de algumas constantes
do código‐fonte seguida de recompilação do sistema. Esses valores (negativos) são
apresentados por base na fórmula dada anteriormente e espaçados o suficiente para que
processos que estejam sendo reiniciados por diferentes razões sejam claramente separados
em diferentes filas.
A idéia por trás desse esquema é tirar rapidamente os processos do núcleo. Se um
processo está tentando ler um arquivo do disco, fazê‐lo esperar um segundo entre as
chamadas read causará uma lentidão enorme nesse processo. É muito melhor deixá‐lo
executar imediatamente após cada requisição ter sido concluída, de modo que ele tenha como
fazer a próxima rapidamente. De maneira similar, se um processo estava bloqueado esperando
uma entrada no terminal, ele é nitidamente um processo interativo e, como tal, a ele deveria
ser atribuída uma alta prioridade, tão logo estivesse pronto, para garantir que os processos
interativos fossem bem servidos. Desse modo, os processos orientados a UCP (isto é, aqueles
nas filas positivas) basicamente conseguem todo o serviço que sobra quando todos os
processos interativos e orientados a E/S estão bloqueados.
3.2 Escalonamento no Minix
O Minix foi um dos primeiros sistemas do tipo Unix baseado no projeto de um
micronúcleo (TANENBAUM, 2003, p. 507). Diferentemente do Unix, cujo kernel é um programa
monolítico e não dividido em módulos, o Minix é uma coleção de processos que se comunicam
entre si e com processos de usuário utilizando uma única primitiva de comunicação
interprocesso – a passagem de mensagem. Esse projeto proporciona uma estrutura mais
flexível e modular, tornando fácil, por exemplo, substituir o sistema de arquivos inteiro por um
completamente diferente, sem nem mesmo precisar recompilar o kernel (TANENBAUM;
WOODHULL, 2000, p. 76).
Micronúcleos têm vantagens sobre os sistemas monolíticos por serem fáceis de
compreender e manter devido a suas estruturas altamente modulares. Além disso, a migração
de código no modo núcleo para o modo usuário permite que eles sejam altamente confiáveis
porque a quebra de um processo no modo usuário causa menos prejuízo do que a quebra de
um componente do modo núcleo. A principal desvantagem que eles apresentam é o
desempenho ligeiramente menor devido às trocas extras entre o modo usuário e o modo
núcleo (TANENBAUM, 2003, p. 507‐508).
O Minix é estruturado em quatro camadas, com cada camada executando uma função
bem‐definida. Essas quatro camadas são ilustradas na figura abaixo.
Figura 3.4. O Minix é estruturado em quatro camadas
No Minix, o gerenciamento de recursos está em grande parte no kernel (camadas 1 e
2) e a interpretação de chamadas de sistema está na camada 3. Servidores adicionais também
podem existir na camada 3. Embora os servidores sejam processos independentes, eles
diferem de processos de usuário por que são iniciados quando o sistema é inicializado e nunca
terminam enquanto o sistema está ativo. Adicionalmente, embora executem no mesmo nível
de privilégio que os processos de usuário em termos das instruções de máquina que lhes é
permitido executar, eles recebem prioridade mais alta de execução que os processos de
usuário. Para acomodar um novo servidor, o kernel precisa ser recompilado. O código de
inicialização do kernel instala os servidores em entradas privilegiadas na tabela de processos
antes de qualquer processo de usuário receber permissão para executar.
Os processos no Minix seguem o modelo geral de processos descritos anteriormente
neste trabalho. Os processos podem criar subprocessos, que, por sua vez, podem criar mais
subprocessos, produzindo uma hierarquia de processos constituída no formado de uma
árvore. De fato, todos os processos de usuário no sistema inteiro são parte de uma única
árvore com init na raiz, como observa‐se na Figura 3.4.
As duas principais chamadas de sistema do Minix para gerenciamento de processos
são fork e exec. Fork é o único meio de criar um novo processo. Exec permite criar um
processo para executar um programa especificado. Quando um programa é executado, ele
recebe uma parte da memória cujo tamanho é especificado no cabeçalho do arquivo de
programa. Ele mantém essa quantidade de memória durante toda sua execução, embora a
distribuição entre segmento de dados, segmento de pilha e não‐utilizado possa variar à
medida que o processo executa. Todas as informações sobre um processos são mantidas na
tabela de processos, que é dividida entre o kernel, o gerenciador de memória e o sistema de
arquivos, com cada um desses tendo os campos que precisa. Quando um novo processo
aparece (por fork), ou um processo antigo termina (por exit ou por um sinal), o gerenciador de
memória primeiro atualiza sua parte na tabela de processos e, então, envia mensagens para o
sistema de arquivos e para o kernel informando‐os para fazer o mesmo.
O sistema de interrupções é o que mantém um sistema operacional multiprogramado
em funcionamento. Os processos bloqueiam quando fazem requisições entrada, permitindo
que outros processos executem. Quando a entrada torna‐se disponível, o processo atual em
execução é interrompido pelo disco, pelo teclado ou por outro hardware. O relógio também
gera interrupções utilizadas para certificar que um processo de usuário em execução que não
solicitou entrada acabe abandonando a UCP, para dar a outro processo sua chance de
executar. É trabalho da camada mais baixa do Minix ocultar essas interrupções,
transformando‐as em mensagens. No que diz respeito aos processos (e tarefas), quando um
dispositivo de E/S completa uma operação ele envia uma mensagem para o processo,
acordando‐o e tornando‐o executável.
Cada vez que um processo é interrompido, seja a partir de um dispositivo convencional
de E/S ou a partir do relógio, há uma oportunidade para determinar qual processo merece
uma oportunidade de executar. Naturalmente, isso também deve ser feito sempre que um
processo termina, mas em um sistema como o Minix as interrupções em decorrência das
operações de E/S ou geradas pelo relógio ocorrem mais freqüentemente que o término de um
processo. O escalonador do Minix utiliza um sistema de filas em três níveis, correspondentes
às camadas 2, 3 e 4 da Figura 3.4. Dentro dos níveis de tarefa e de servidor, os processos
executam até bloquearem, mas os processos de usuário são agendados utilizando round robin.
As tarefas têm a prioridade mais alta, o gerenciador de memória e o servidor de arquivos vêm
em seguida e, por último, os processos de usuário (Figura 3.5).
Figura 3.5. O escalonador no Minix mantém três filas, uma para cada nível de prioridade
Ao selecionar um processo para executar, o escalonador verifica se qualquer tarefa
está pronta. Se uma ou mais estiver pronta, a primeira da fila é executada. Se nenhuma tarefa
estiver pronta, um servidor (sistema de arquivos – FS ou gerenciador de memória – MM) é
escolhido, se possível; caso contrário um processo do usuário é executado. Se nenhum
processo estiver pronto, o processo IDLE é escolhido. Esse é um laço que executa até que a
próxima interrupção ocorra.
A cada tique do relógio, uma verificação é feita para ver se o processo atual é um
processo de usuário que executou mais de 100 ms. Se for, o escalonador é chamado para ver
se outro processo de usuário está esperando pela UCP. Se algum for localizado, o processo
atual é movido para o fim de sua fila de agendamento e o processo agora no topo é executado.
As tarefas, o gerenciador de memória e o sistema de arquivos nunca sofrem preempção pelo
relógio, independente de quanto tempo eles tenham estado executando.
3.2.1 Código e comentários
Como visto, o Minix utiliza um algoritmo de escalonamento de múltiplos níveis que
segue bem a estrutura mostrada na Figura X. Nessa figura, observa‐se tarefas de E/S na
camada 2, processos de servidor na camada 3 e processos de usuário na camada 4. O
escalonador matem três filas de processo executáveis, um para cada camada, como mostrado
na Figura 3.4. A matriz rdy_head tem uma entrada para cada fila, com essa entrada apontando
para o processo na cabeça da fila. De maneira semelhante, rdy_tail é uma matriz cujas
entradas apontam para o último processo em cada fila. Essas duas matrizes são definidas com
a macro EXTERN em proc.h (linhas 5595 e 5596 da Figura 3.6).
Sempre que um processo é bloqueado e acordado, ele é anexado ao fim de sua fila. A
existência da matriz rdy_tail torna a adicionar um processo ao fim de uma fila uma definição
eficiente. Sempre que um processo em execução torna‐se bloqueado, ou um processo
executável é eliminado por um sinal, esse processo é removido das filas do escalonador.
Somente processos executáveis são enfileirados.
Dadas as estruturas de fila descritas acima, o algoritmo de escalonamento é simplório:
encontrar a fila de prioridade mais alta que não está vazia e selecionar o processo na cabeça
dessa fila. Se todas as filas estiverem vazias, a rotina de espera será executada. Na figura Y,
TAXK_Q tem a maior prioridade. O código de escalonamento está no arquivo proc.c
demonstrado na figura B.
Figura 3.6. Definição das matrizes rdy_head e rdy_tail.
A fila é escolhida em pick_prock (linha 7910 da Figura 3.7). O principal trabalho dessa
função é configurar proc_ptr. Qualquer alteração nas filas, que possa afetar a escolha de qual
processo deve executar em seguida, requer que pick_proc seja chamada novamente. Sempre
que o processo atual bloqueia, pick_proc é chamada para reescalonar a UCP.
Prick_proc é simples. Há um teste para cada fila. TASK_Q é testada primeiro e se um
processo nessa fila estiver pronto, pick_proc configura proc_ptr e imediatamente retorna. Em
seguida, SERVER_Q é testada, e, novamente, se um processo estiver pronto, pick_proc
configura proc_ptr e retorna. Se houver um processo pronto na fila USER_Q, bill_ptr é alterada
para contabilizar ao processo de usuário o tempo da UCP que está para ser fornecido (linha
7927 da Figura 3.7). Isso garante que o último processo de usuário a executar seja cobrado
pelo trabalho feito em seu benefício pelo sistema. Se nenhuma das filas tem uma tarefa
pronta, a cobrança é transferida para o processo IDLE, o qual sempre está pronto, e o
escalona. O processo escolhido para executar não é removido de sua fila meramente porque
foi selecionado.
Os procedimentos enqueue (linha 7787 da Figura 3.7) e dequeue (linha 7823 da Figura
3.7) são chamadas para colocar um processo executável em sua fila e remover um processo
não mais executável de sua fila, respectivamente. Enqueue é chamado tanto a partir de
mini_send como de mini_receive. Enqueue manipula uma das três filas de processos. Ele
simplesmente adiciona o processo ao final da fila apropriada.
Dequeue também gerencia filas. Normalmente, o processo que ele remove está na
cabeça da sua fila, já que um processo deve estar executando para ser bloqueado. Nesse caso,
dequeue chama pick_proc antes de retornar, como, por exemplo, na linha 7852 da Figura 3.7.
Um processo de usuário que não está executando também pode tornar‐se não‐pronto se ele
enviar um sinal e se o processo não for encontrado na cabeça de uma das filas, uma pesquisa é
feita ao longo de USER_Q e ele é removido se for encontrado.
Embora a maioria das decisões de escalonamento seja feita quando um processo
bloqueia ou desbloqueia, o escalonamento também deve ser feito quando a tarefa de relógio
nota que o processo de usuário atual excedeu seu quantum. Nesse caso, a tarefa de relógio
chama sched (linha 7862 da Figura 3.7) para mover o processo da cabeça de USER_Q para o
fim dessa fila. Esse algoritmo resulta na execução de processos de usuário estritamente no
estilo round robin. O sistema de arquivos, o gerenciador de memória e as tarefas de E/S nunca
são colocados no fim de suas filas porque estiveram executando por muito tempo. Confia‐se
em que elas funcionarão adequadamente e bloquearão depois de finalizarem seu trabalho.
Ainda há mais algumas rotinas em proc.c que suportam escalonamento de processos.
Por exemplo, lock_notify, lock_send, lock_enqueue e lock_dequeue definem um bloqueio
chamando a rotina lock e então liberam o bloqueio por meio da chamada de unlock ao seu
término.
Em suma, o algoritmo de escalonamento do Minix mantém três filas de prioridade,
uma para as tarefas de E/S, uma para os processos de servidor e uma para os processos de
usuário. O primeiro processo na fila de prioridade mais alta sempre é executado primeiro. As
tarefas e os servidores sempre têm permissão para executar até bloquearem, mas a tarefa de
relógio monitora o tempo utilizado por processos de usuário. Se um processo de usuário
utilizar todo seu quantum, ele é colocado no fim de sua fila, obtendo, assim, um
escalonamento por round robin simples entre os processos de usuário.
Figura 3.7. Código de escalonamento no Minix (parte 1 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 2 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 3 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 4 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 5 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 6 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 7 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 8 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 9 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 10 de 11)
Figura 3.7. Código de escalonamento no Minix (parte 1 de 11)
3.3 Escalonamento no Windows XP
No Windows XP, um processo consiste em código do programa, um contexto de
execução, recursos (por exemplo, manipuladores de objeto) e um ou mais threads associados.
O contexto de execução inclui itens como o espaço de endereçamento virtual do processo e
vários atributos (por exemplo, prioridade de escalonamento) (PROCESSES apud DEITEL, H. M.;
DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 673)52. Ambos, processos e threads, são objetos,
portanto, outros processos podem obter manipuladores para processos e threads e outros
threads podem esperar por processos e eventos de threads exatamente como acontece com
outros tipos de objetos (CREATING; EVENT apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. ,
2005, p. 673)53, 54.
3.3.1 Organização de processos e trheads
O Windows XP armazena informações de contexto de processos e threads em diversas
estruturas de dados. Um bloco de processo executivo (EPROCESS block) é a principal estrutura
de dados que descreve um processo. O bloco EPROCESS armazena informações que
componentes do executivo usam quando manipulam um objeto processo; essas informações
incluem o ID (identificador) do processo, um ponteiro para a tabela de manipuladores do
processo, um ponteiro para a ficha de acesso do processo e informações de conjunto de
trabalho (por exemplo, os tamanhos mínimo e máximo do conjunto de trabalho do processo,
histórico de falta de páginas e o conjunto de trabalho corrente). O sistema armazena blocos
52
“PROCESSES and threads”, MSDN Library, msdn.microsoft.com/library/en‐
us/dllproc/base/about_processes_and_threads.asp.
53
“CREATING processes”, MSDN Library, msdn.microsoft.com/library/en‐us/dllproc/base/creating_processes.asp.
54
“EVENT objects”, MSDN Library, msdn.microsoft.com/library/em‐us/dllproc/base/event_objects.asp.
EPROCESS em uma lista encadeada (SOLOMON, D.; RUSSINOVICH, M., 2000; SCHREIBER, S.,
2001 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 673)55, 56.
Cada bloco EPROCESS também contém um bloco de processo de núcleo (bloco
KPROCESS). Um bloco KPROCESS armazena informações de processo usadas pelo micronúcleo
(o micronúcleo gerencia escalonamento de thread e sincronização de thread). Portanto, o
bloco KPROCESS armazena informações, tais como a classe de prioridade básica do processo, o
quantum padrão para cada um de seus threads e sua trava giratória (SCHREIBER, S., 2001;
SOLOMON, D.; RUSSINOVICH, M., 2000 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. ,
2005, p. 673)57, 58.
Os blocos EPROCESS e KPROCESS existem em espaço de núcleo e armazenam
informações para que componentes de modo núcleo as acessem enquanto manipulam um
processo. O sistema também armazena informações de processo em um bloco de ambiente
de processo (process enviroment block ‐ PEB), que é armazenado no espaço de
endereçamento do processo. O bloco EPROCESS de um processo aponta para o PEB do
processo. O PEB armazena informações úteis para os processos usuários, como uma lista de
DLLs ligadas ao processo e informações sobre o heap do processo (SCHREIBER, S., 2001;
SOLOMON, D.; RUSSINOVICH, M., 2000 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. ,
2005, p. 673)59, 60.
O Windows XP armazena dados de thread de maneira similar. Um bloco de thread
executivo (bloco ETRHEAD) é a principal estrutura de dados que descreve um thread.
Armazena informações que componentes do executivo usam ao manipular um objeto thread.
Essas informações incluem o ID do processo do thread, seu endereço de início, sua ficha de
acesso e uma lista de requisições de E/S pendentes. O bloco ETHREAD para um thread também
aponta para o bloco EPROCESS do seu processo (SCHREIBER, S., 2001; SOLOMON, D.;
RUSSINOVICH, M., 2000 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 673)61, 62.
Cada bloco ETHREAD armazena o bloco de thread de núcleo (bloco KTHREAD). O
micronúcleo usa informações do bloco KTHREAD para escalonamento e sincronização de
thread. Por exemplo, o bloco KTHREAD armazena informações, tais como a prioridade básica e
55
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000.
56
SCHREIBER, S. Undocumented Windows 2000 secrets. Boston: Assison Wesley, 2001.
57
SCHREIBER, S. Undocumented Windows 2000 secrets. Boston: Assison Wesley, 2001, p. 416.
58
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 291.
59
SCHREIBER, S. Undocumented Windows 2000 secrets. Boston: Assison Wesley, 2001, p. 429.
60
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 291.
61
SCHREIBER, S. Undocumented Windows 2000 secrets. Boston: Assison Wesley, 2001, p. 416.
62
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 319.
a prioridade corrente do thread (a prioridade de um thread pode mudar), seu estado corrente
(isto é, pronto, em espera) e quaisquer objetos de sincronização pelos quais ele esteja
esperando (SCHREIBER, S., 2001; SOLOMON, D.; RUSSINOVICH, M., 2000 apud DEITEL, H. M.;
DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 673)63, 64.
Os blocos ETHREAD e KTHREAD existem em espaço de núcleo e, portanto, não são
acessíveis a usuários. O bloco de ambiente de thread (thread enviroment block ‐ TEB)
armazena informações sobre um thread no espaço de endereçamento do processo do thread.
Cada bloco KTHREAD aponta para um TEB. O TEB de um thread armazena informações, como
as seções críticas que o thread possui, seu ID e informações sobre sua pilha. Um TEB também
aponta para o PEB do processo do thread (SCHREIBER, S., 2001; SOLOMON, D.; RUSSINOVICH,
M., 2000 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 673)65, 66.
Todos os threads que pertencem ao mesmo processo compartilham um espaço de
endereçamento virtual. Embora grande parte de sua memória seja global, threads podem
manter seus próprios dados no armazenamento local do thread (thread local storage ‐ TLS).
Um thread dentro de um processo pode alocar um índice TLS ao processo; o thread armazena
um ponteiro para dados locais em uma localização especificada (denominada um slot TLS) do
índice. Cada thread que usar o índice recebe um slot TLS no índice no qual pode armazenar um
item de dado. Uma utilização comum para um índice TLS é armazenar dados associados com
uma DLL à qual um processo se liga. Processos freqüentemente contêm muitos índices TLS
para acomodar múltiplos propósitos, como dados de subsistemas de DLL e de ambiente.
Quando threads não precisam mais de um índice TLS (por exemplo, a DLL conclui a execução),
o processo pode descartar o índice (THREAD, 2003 apud DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R. , 2005, p. 673‐674)67. Um thread também possui sua própria pilha de tempo
de execução na qual também pode armazenar dados locais.
3.3.2 Escalonamento de threads
O Windows XP não contém um módulo específico de “escalonador de threads” – o
código do escalonador é dispersado por todo o micronúcleo. O código de escalonamento é
denominado coletivamente despachador (SOLOMON, D.; RUSSINOVICH, M., 2000 apud
DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 675)68. O Windows XP suporta
63
SCHREIBER, S. Undocumented Windows 2000 secrets. Boston: Assison Wesley, 2001, p. 419.
64
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 320‐321.
65
SCHREIBER, S. Undocumented Windows 2000 secrets. Boston: Assison Wesley, 2001, p. 429.
66
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 328.
67
“THREAD local storage”, MSDN Library, fev. 2003, msdn.microsoft.com/library/en‐
us/dllproc/base/thread_local_storage.asp.
68
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000.
escalonamento preemptivo entre múltiplos threads. O despachador escalona cada thread
independentemente do processo ao qual o thread pertence, significando que, se todo o resto
for igual, o mesmo processo implementado com mais threads terá mais tempo de execução
(SCHEDULING, 2003 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 675)69. O
algoritmo de escalonamento usado pelo Windows XP é baseado na prioridade do thread.
Antes de descrever detalhadamente o algoritmo de escalonamento, é conveniente investigar o
ciclo de vida de um thread no Windows XP (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. ,
2005, p. 675).
3.3.3 Estados de threads
No Windows XP, threads podem estar em qualquer um de oito estados, como
demonstrado na Figura 3.8.
Figura 3.8. Diagrama de transição de estado de thread
Um thread começa no estado inicializado durante a sua criação. Uma vez concluída a
inicialização, o thread entra no estado pronto. Threads no estado pronto estão esperando
para usar um processador. Quando um despachador decide que um thread executará em um
determinado processador em seguida, o thread entra no estado de reserva enquanto espera
sua vez por aquele processador. Um thread está no estado de reserva, por exemplo, durante o
69
“SCHEDULING priorities”, MSDN Library, fev. 2003, msdn.microsoft.com/library/en‐
us/dllproc/base/scheduling_priorities.asp.
chaveamento de contexto do thread que estava executanto anteriormente para aquele
thread. Uma vez obtido um processador, o thread entra no estado de execução. Um thread sai
do estado de execução se terminar a execução, exaurir seu quantum, sofrer preempção, for
suspenso ou estiver esperando um objeto. Se um thread terminar, entra no estado terminado.
O sistema não apaga necessariamente um thread terminado; o gerenciador de objeto apaga
um thread somente quando a contagem de referência do objeto do thread torna‐se zero. Se
um thread em execução sofrer preempção ou exaurir seu quantum, volta ao estado pronto. Se
um thread em execução começar a esperar por um manipulador de objeto, entra no estado de
espera. E, também, um outro thread (com direitos de acesso suficientes) ou o sistema pode
suspender um thread, forçando‐o a entrar no estado de espera até que o thread seja
retomado. Quando o thread concluir sua espera, ou ele volta para o estado pronto ou entra no
estado de transição. A pilha de núcleo de um thread no estado de transição foi paginada para
fora da memória (por exemplo, porque não executou por um período de tempo e o sistema
precisou de memória para outros propósitos), mas, para todos os efeitos, o thread está pronto
para executar. O thread entra no estado pronto assim que o sistema paginar a pilha do núcleo
do thread novamente para a memória. O sistema coloca um thread no estado desconhecido
quando não está seguro do estado do thread (usualmente devido a um erro) (WIN_32, 2003;
SOLOMON, D.; RUSSINOVICH, M., 2000 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. ,
2005, p. 675‐676)70, 71.
3.3.4 Algoritmo de escalonamento de thread
O despachador escalona threads com base na prioridade – a subseção seguinte
descreve como são determinadas as prioridades dos threads. Quando um thread entra no
estado pronto, o núcleo o coloca na fila de prontos que corresponde à sua prioridade. O
Windows XP tem 32 níveis de prioridade identificados pelos inteiros de 0 a 31, sendo 31 a
prioridade mais alta e 0 a mais baixa. O despachador começa com a fila de prontos de
prioridade mais alta e escalona os threads dessa fila por alternância circular. Um thread
permanece na fila de prontos enquanto estiver no estado pronto ou no estado em execução.
Quando a fila estiver vazia, o despachador passa para a fila seguinte; continua fazendo isso até
que ou todas as filas estejam vazias ou um thread de prioridade mais alta do que a do thread
que está executando correntemente entre no seu estado pronto. Nesse último caso, o
despachador provoca a preempção do thread de prioridade mais baixa e executa o novo
thread. Em seguida, o despachador executa o primeiro thread da fila de prontos não vazia de
70
“WIN_32 thread”, MSDN Library, jul. 2003, msdn.microsoft.com/library/en‐us/wmisdk/wmi/win32_thread.asp.
71
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 348‐349.
prioridade mais alta e retorna seu procedimento normal de escalonamento (SCHEDULING,
2003 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 676)72.
Cada thread executa, por, no máximo, um quantum. No caso de preempção, os quanta
de threads de tempo real são reajustados, ao passo que todos os outros threads terminam
seus quanta quando readquirem um processador. Dar novos quanta aos threads de tempo real
após a preempção permite que o Windows XP favoreça threads de tempo real que requerem
alto nível de responsividade. O sistema devolve threads que sofreram preempção para a frente
da fila de prontos apropriada (SOLOMON, D.; RUSSINOVICH, M., 2000 apud DEITEL, H. M.;
DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 676)73. Note que threads de modo usuário podem
evitar isso mascarando certas interrupções. Eventos como um thread entrando no estado
pronto, um thread saindo do estado de execução ou uma mudança na prioridade de um thread
acionam a execução pelo sistema de rotinas de despachador – rotinas que executam no nível
DPC/despacho. Elevando o IRQL para o IRQL DPC/despacho, threads de modo núcleo podem
mascarar escalonamento e eveitar a preempção. Contuto, threads de modo usuário ainda
assim podem bloquear a execução de threads de sistema se suas prioridades forem mais altas
(SOLOMON, D.; RUSSINOVICH, M., 2000 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. ,
2005, p. 676)74.
3.3.5 Determinação de prioridades de threads
O Windows XP divide seus 32 níveis de prioridade (0‐31) em duas categorias. Threads
de tempo real (isto é, threads que devem manter um alto nível de responsividade às
requisições do usuário) ocupam os 16 níveis superiores de prioridade (16‐31), e threads
dinâmicos ocupam os 16 níveis de prioridade mais baixos (0‐15). Somente o thread de página
zero tem nível de prioridade 0. Esse thread usa ciclos sobressalentes de processador para zerar
páginas livres da memória de modo que fiquem prontas para o uso (DEITEL, H. M.; DEITEL, P.
J.; CHOFFNES, D. R. , 2005, p. 676).
Cada thread tem uma prioridade básica que define o limite inferior que sua prioridade
real pode ocupar. A prioridade básica de um thread de modo usuário é determinada pela
classe de prioridade básica do seu processo e pelo nível de prioridade do thread. A classe de
prioridade básica de um processo especifica uma estreita faixa que a prioridade básica de cada
thread de um processo pode ter. Há seis classes de prioridade básica: ocioso, abaixo do
72
“SCHEDULING priorities”, MSDN Library, fev. 2003, msdn.microsoft.com/library/en‐
us/dllproc/base/scheduling_priorities.asp.
73
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 356‐358.
74
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 338‐347.
normal, normal, acima do normal, alto e tempo real. As primeiras cinco dessas classes de
prioridade (denominadas classes de prioridade dinâmica) abrangem níveis de prioridade de 0
a 15; são chamadas classes de prioridade dinâmica porque as prioridades de threads
pertencentes a processos dessas classes podem ser alteradas dinamicamente pelo sistema
operacional. Os threads que pertencem a processos da classe de prioridade de tempo real
têm prioridades entre 16 e 31; a prioridade de threads de tempo real é estática. Dentro de
cada classe de prioridade há diversos níveis de prioridade básicas: ocioso, mais baixo, abaixo
do normal, normal, acima do normal, mais alto e de tempo crítico. Cada combinação de classe
de prioridade básica com nível de prioridade básica mapeia para uma prioridade básica
específica (por exemplo, um thread de nível de prioridade básica normal e uma classe de
prioridade normal tem prioridade básica 7) (SCHEDULING apud DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R. , 2005, p. 677)75.
A prioridade de um thread de prioridade dinâmica pode mudar. Um thread recebe
uma elevação de prioridade quando sai do estado de espera, tal como acontece após a
conclusão de uma E/S ou depois que o thread obtém um manipulador para um recurso pelo
qual está esperando. Similarmente, uma janela que recebe entrada (tal como de um teclado,
mouse ou temporizador) ganha uma elevação de prioridade. O sistema também pode reduzir a
prioridade de um thread. Quando um thread executa até que seu quantum expire, sua
prioridade é reduzida de uma unidade. Todavia, a prioridade dinâmica de um thread nunca
pode cair abaixo de sua prioridade básica nem subir até a faixa de tempo real (PRIORITY
BOOSTS apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 677)76.
75
“SCHEDULING priorities”, MSDN Library, msdn.microsoft.com/library/en‐
us/dllproc/base/scheduling_priorities.asp.
76
“PRIORITY BOOSTS”, MSDN Library, msdn.microsoft.com/library/en‐us/dllproc/base/priority_boosts.asp.
thread de baixa prioridade para que ele execute e termine de usar o recurso (PRIORITY
INVERSION apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 677)77.
3.3.6 Escalonamento em multiprocessadores
O escalonamento de thread em multiprocessadores amplia o algoritmo de
escalonamento de monoprocessadores já apresentado. Todas as versões do Window XP,
exceto a Home Edition, provêem suporte para multiprocessadores. Contutdo, mesmo a Home
Edition provê suporte para multiprocessamento simétrico (SMP) usando a tecnologia de
hiperthreading (HT) da Intel. A tecnologia HT permite que o sistema operacional veja um
processador físico como dois processadores virtuais. Escalonamento em um sistema SMP é
similar ao escalonamento em um sistema monoprocessador. Geralmente, quando um
processador fica disponível, o despachador tenta escalonar o thread que está na frente da fila
não vazia de threads prontos de prioridades mais altas. Contudo, o sistema também tenta
manter threads nos mesmos processadores para maximizar a quantidade de dados relevantes
armazenados em caches L1 (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 677).
Processos e threads podem especificar os processadores nos quais preferem executar.
Um processo pode especificar uma máscara de afinidade, que é um conjunto de
processadores nos quais seus threads têm permissão para executar. Um thread também pode
especificar uma máscara de afinidade que deve ser um subconjunto da máscara de afinidade
do seu processo (MULTIPLE apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 677)78.
Similarmente, um job também pode ter uma máscara de afinidade que cada um de seus
processos associados deve estabelecer como sua própria máscara de afinidade. Máscaras de
afinidade podem ser utilizadas para restringir processos de alta intensidade de computação a
uns poucos processadores, de modo que aqueles processos não interfiram com processos
interativos mais críticos em relação ao tempo (DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. ,
2005, p. 677).
Além de máscaras de afinidade, cada thread armazena seu processador ideal e seu
último processador. Manipular o valor do processador ideal de um thread permite que
desenvolvedores possam influenciar se threads relacionados devem executar em paralelo
(ajustando processadores ideais de threads relacionados a processadores diferentes) ou no
mesmo processador para compartilhar dados em chache (ajustando os processos ideais dos
threads ao mesmo processador). Por padrão, o Windows XP tenta designar processadores
ideais diferentes a threads do mesmo processo. O despachador usa o último processador em
77
“PRIORITY INVERSION”, MSDN Library, msdn.microsoft.com/library/en‐us/dllproc/base/priority_inversion.asp.
78
“MULTIPLE processors”, MSDN Library, msdn.microsoft.com/library/en‐us/dllproc/base/multiple_processors.asp.
um esforço de escalonar um thread no mesmo processador no qual o thread foi executado da
última vez. Essa estratégia aumenta a probabilidade de que os dados mantidos em cache pelo
thread durante uma execução possam ser acessados durante a próxima execução daquele
thread (SOLOMON, D.; RUSSINOVICH, M., 2000; MULTIPLE apud DEITEL, H. M.; DEITEL, P. J.;
CHOFFNES, D. R. , 2005, p. 677)79, 80.
Quando um processador fica disponível, o despachador escalona threads considerando
a prioridade de cada um, o processador ideal, o último processador e há quanto tempo um
thread está esperando. Conseqüentemente, o thread que está na frente da fila não vazia de
threads prontos de prioridade mais alta pode não ser escalonado para o próximo processador
disponível (mesmo que o processador esteja na máscara de afinidade do thread). Se o thread
não estiver esperando há muito tempo e o processador que estiver disponível não for o seu
processador ideal nem o último processador, o despachador pode escolher um outro thread
daquela fila de threads prontos que atenda a um ou a mais desses outros critérios (SOLOMON,
D.; RUSSINOVICH, M., 2000 apud DEITEL, H. M.; DEITEL, P. J.; CHOFFNES, D. R. , 2005, p. 677)81.
79
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 371.
80
“MULTIPLE processors”, MSDN Library, msdn.microsoft.com/library/en‐us/dllproc/base/multiple_processors.asp.
81
SOLOMON, D.; RUSSINOVICH, M. Inside Windows 2000, 3 ed. Remond: Microsoft Press, 2000, p. 371‐373.
CONCLUSÕES
Os algoritmos devem funcionar de forma justa, ou seja, devem dar chance a
todos os processos de receberem a UCP por uma quantidade de tempo o mais razoável
possível. Dependendo do tipo de processo, cada qual poderá necessitar de mais ou
menos tempo de UCP. Cabe ao escalonador decidir qual processo receberá mais ou
menos UCP de acordo com o que achar mais justo.
Assim, promover a alternância adequada de processos de modo que todos eles
executem seus serviços de maneira eficiente àqueles que os requisitaram, fazem do
escalonador de processos uma importante entidade do SO. Sem ele, os recursos
seriam mal aproveitados, tempos de espera seriam acentuados e os usuários ficariam
insatisfeitos.
REFERÊNCIAS
DEITEL, Harvey M.; DEITEL, Paul J.; CHOFFNES, David R. Sistemas operacionais. 3 ed.
São Paulo: Pearson Prentice Hall, 2005.
LAUDON, Kenneth C.; LAUDON, Jane P. Sistemas de informação gerenciais:
administrando a empresa digital. 5 ed. São Paulo: Pearson Prentice Hall, 2004.
TANENBAUM, Andrew S. Sistemas operacionais modernos. 2 ed. São Paulo: Pearson
Prentice Hall, 2003.