O documento discute os conceitos de escalonamento de threads no Windows, incluindo: (1) O Windows implementa um escalonador multinível do tipo feedback queue; (2) As threads possuem prioridades que variam de 0 a 31, divididas em tempos reais e variáveis; (3) O escalonador ajusta periodicamente as prioridades das threads para evitar postergação indefinida.
SI - Processos, Threads, Virtualização e Migração de Código
Escalonamento no Windows
1. ESCALONAMENTO
no
Alunos:
Felipe de Souza da Costa
Fernanda Gomes
Giselle Nascimento
Nildo Wilpert
2. Tópicos
• Visão geral
• Definição do escalonador
• Níveis de prioridade
• Aumento de prioridade
• Postergação indefinida
• Threads de tempo real
• Estados de uma thread
• Troca de contexto
• Cenários de escalonamento
• Threads Idle
• Sistemas multiprocessador
• Afinidade
3. Visão geral
• O Windows implementa um escalonador do tipo Multilevel Feedback Queue.
• Certas threads executam sempre (threads de mais alta prioridade).
• Por padrão, a thread pode ser executada por qualquer processador dentro de um
grupo de processadores na qual a thread está previamente associada
(normalmente um grupo de até 64 processadores). Porém desenvolvedores
podem alterar a afinidade utilizando API’s adequadas, enquanto que os usuários
podem utilizar ferramentas para alterá-la em tempo de execução. (gerenciador
de tarefas).
• O desenvolvedor pode também optar por criar aplicações que suportam mais de
um grupo de processadores, utilizando API’s extendidas para associar a afinidade
da thread a processadores lógicos de diferentes grupos, passando este a ser um
processo multi-grupo, que teoricamente, pode ser executado por qualquer
processador disponível dentro da máquina.
4. Visão geral
• Depois que a thread é selecionada, ela passa a ser executada por um
tempo determinado (quantum).
• Quantum é um período de tempo que uma thread pode ser
executada antes que outra thread ao mesmo nível de prioridade
passe a ser executada.
• Valores de quantum podem variar de sistema para sistema e de
processo para processo por qualquer uma das três razões:
• Pelas definições do sistema (quantum longo ou curto, quantum variável ou
fixo e filas de prioridades)
• Uso de primeiro e segundo plano para um processo
• Por uma alteração do quantum, causada por uma chamada de sistema.
5. Visão geral
• Uma thread pode não completar seu quantum, pois como vimos, o Windows
implementa um escalonamento Multilevel Feedback Queue, ou seja, se outra
thread com uma prioridade mais alta torna-se pronta para ser executada, a
thread em execução é interrompida (preemptada) antes de completar sua fatia
de tempo.
6. Definição do Escalonador
• A estrutura do kernel do Windows é híbrida.
• Neste modelo de kernel, os seus componentes
principais ( Escalonadores, IPC base)
continuam sendo mantidos no modo kernel
apenas.
• Porém sendo híbrido, ele tem a capacidade de
agregar ou desagregar funcionalidades, sem
perder performance ou estabilidade presentes
na sua estrutura inicial, através dos módulos
chamados “servidores”
7. Definição do Escalonador
• O código do Escalonador do Windows é implementado no kernel.
• Não há um único módulo ou rotina de escalonamento, pois o código é
espalhado por todo o kernel.
• A rotina que desempenha essas tarefas é chamada de Despachador
do kernel.
8. Definição do Escalonador
• Os seguintes eventos podem chamar o Despachador:
• Uma thread fica pronta para executar, por exemplo, uma thread que acabou
de ser criada ou que acabou de sair do estado de Espera.
• Uma thread sai do estado de Execução pois o seu quantum acabou ou quando
entra no estado de Espera.
• A prioridade de uma thread muda, ou devido a uma chamada de sistema ou
porque o próprio Windows muda a sua prioridade.
• Quando a Afinidade do processador em que a thread está executando muda,
fazendo com que a thread não possa mais ser executada naquele núcleo.
9. Definição do Escalonador
• Para cada um desses eventos, o Windows deve determinar qual
thread deve executar em seguida.
• O escalonador mantém uma fila de threads executáveis para cada
nível de prioridade.
• Quando o processador se torna disponível, o sistema realiza a troca
de contexto.
10. Definição do Escalonador
• Como foi visto, o Windows escalona usando as threads.
• Essa abordagem faz sentido quando se considera que os processos não
executam, mas apenas fornecem recursos e um contexto para a execução
das suas threads.
• Como as decisões do Escalonador são baseadas estritamente nas threads,
nenhuma consideração é dada ao processo a qual a thread pertence.
• Por exemplo, se um processo A tiver 10 threads executáveis, um Processo B
tiver 2 threads executáveis, e todas as 12 threads estiverem na mesma
prioridade, cada thread teoricamente receberia 1/12 do tempo da
CPU (Windows não daria 50% do tempo da CPU para o processo A e 50%
para o processo B).
11. Níveis de prioridade
• O Windows usa 32 níveis de
prioridade, variando de 0 a 31,
sendo dividido em:
• Dezesseis níveis de prioridade de
tempo real. (16º nível ao 31º nível)
• Dezesseis níveis de prioridade
variável. (nível 0 ao 15º nível)
• A thread 0 (zero-page) pode ter a
prioridade zero. A thread "zero-page"
é uma thread do sistema
responsável por zerar quaisquer
"páginas livres" quando não há
outras threads para executar.
12. Níveis de prioridade das Threads
• Os níveis de prioridade são atribuídos a partir de duas perspectivas
diferentes: API do Windows e Kernel do Windows.
13. API do Windows
1- Organiza os processos em seis classes de prioridades:
BASE SetPriorityClass
TEMPO REAL 24 REALTIME_PRIORITY_CLASS
ALTA 13 HIGH_PRIORITY_CLASS
ACIMA DO NORMAL 10 ABOVE_NORMAL_PRIORITY_CLASS
NORMAL 8 NORMAL_PRIORITY_CLASS
ABAIXO DO NORMAL 6 BELLOW_PRIORITY_CLASS
BAIXA/ESPERA 4 IDLE_PRIORITY_CLASS
14. API do Windows
2- Atribui uma prioridade relativa a cada thread dentro do processo a qual a mesma
pertence.
valor SetThreadPriority
Inativa -15 THREAD_PRIORITY_IDLE
Mais baixa -2 THREAD_PRIORITY_LOWEST
Abaixo do normal -1 THREAD_PRIORITY_BELOW_NORMAL
Normal 0 THREAD_PRIORITY_NORMAL
Acima do normal 1 THREAD_PRIORITY_ABOVE_NORMAL
Mais alta 2 THREAD_PRIORITY_HIGHEST
Tempo critico 15 THREAD_PRIORITY_TIME_CRITICAL
15. Kernel do Windows
• A prioridade relativa da thread é então aplicada como uma diferença
entre a prioridade base do processo e a prioridade relativa da thread.
• Por exemplo: A thread com prioridade “mais alta”
(THREAD_PRIORITY_HIGHEST) receberá a prioridade base da sua
classe de processo, neste caso 10, supondo
ABOVE_NORMAL_PRIORITY_CLASS, a prioridade relativa da thread
seria 12.
• Considerando que o processo tem apenas um único valor de
prioridade base, cada thread tem dois valores de prioridade: corrente
e base.
• Decisões de escalonamento são feitas com base na prioridade atual.
16.
17.
18. Prioridade base do processo
• Normalmente, em aplicações
usuário, a prioridade do processo
é a NORMAL_PRIORITY_CLASS
(Prioridade 8).
• No entanto isso pode ser
alterado.
19. Aumento de prioridade
• O escalonador do Windows ajusta periodicamente a prioridade atual
das threads do nível variável através de um mecanismo de aumento
de prioridade interna.
• Em muitos casos, ele faz isso para diminuir várias latências e
aumentar a capacidade de resposta mais justa quanto possível.
• Em outros, aplica esses impulsos para evitar cenários de postergação
indefinida.
20. Aumento de prioridade
• O aumento de prioridade pode ser causado por:
• Evento expedido pelo escalonador/despachador;
• Quando uma operação de entrada e saída é completada;
• Depois de uma thread em primeiro plano completar o estado de espera;
• Uma GUI thread (Graphical User Interfaces) é chamada devido a algum evento de janela,
como por exemplo, apertar um botão para cancelar;
• Quando uma thread que está pronta para executar está esperando por muito tempo
(postergação indefinida);
21. Aumento de prioridade
• A intenção desses ajustes é tentar prover uma capacidade de
resposta mais justa possível nos cenários de escalonamento, mas
como qualquer algoritmo de escalonamento, estes ajustes não são
perfeitos e nem sempre beneficia todas as aplicações.
• Vale ressaltar que o Windows nunca ajusta a prioridade de threads do
nível de tempo real (16 a 31), para que elas sempre tenham a mesma
base e prioridade atual.
22. Postergação indefinida
• Imagine a seguinte situação:
• Tem-se uma thread de prioridade 7 que está em execução, impedindo
uma thread de prioridade 4 de receber tempo da CPU;
• Entretanto, uma thread de prioridade 11 está esperando por
algum recurso que a thread de prioridade 4 bloqueou.
• Mas como a thread de prioridade 7 está tomando todo o tempo da
CPU, a thread de prioridade 4 nunca irá executar por tempo suficiente
para terminar o que quer que ela esteja fazendo e disponibilizar o
recurso que bloqueia a thread de prioridade 11.
• Como o Windows lida com essa situação?
23. Postergação indefinida
• Uma vez por segundo, o "Gerenciador de Equilíbrio" (Balance Set Manager), uma thread
de sistema que existe principalmente para realizar funções de gerenciamento de
memória, procura filas prontas de quaisquer threads que estiveram em estado de
"pronto" (isto é, que não executaram ainda) por 4 segundos.
• Se ele encontrar essa thread, o Gerenciador de Equilíbrio aumenta a prioridade da
thread para 15.
• Nos Windows 2000 e XP o quantum da thread é alterado para duas vezes o quantum do
processo.
• No Windows Server 2003, o quantum é alterado para 4 unidades.
• Uma vez que o quantum acabou, a prioridade é imediatamente reduzida para sua
prioridade base original. Se a thread não houver terminado e uma thread de prioridade
maior estiver pronta para executar, a thread que foi reduzida volta para a fila "pronta",
onde, novamente, esta fica disponível para um novo aumento se ficar lá (na fila) por mais
4 segundos.
24. Postergação indefinida
• O Gerenciador de Equilíbrio não verifica realmente todas as threads
sempre que é executado.
• Para minimizar o tempo de CPU utilizado, ele verifica somente 16
threads prontas; se houver mais threads naquele nível de prioridade,
ele se lembra onde parou e inicia o próximo passo.
• Além disso, ele aumentará apenas dez threads em cada passagem -
se ele encontrar dez threads que mereçam esse aumento, ele para a
verificação naquele ponto e inicia na próxima passagem.
25. Postergação indefinida
• Este algoritmo sempre conseguirá resolver o problema da
postergação indefinida?
• Não - ele não é perfeito de forma alguma. Mas ao longo do tempo,
threads sedentas (starved) pela CPU vão conseguir tempo suficiente
para terminar o que estiverem fazendo e voltar ao estado de espera.
26. Processo de tempo real
• Muitos processos do sistema no modo kernel, como aqueles que gerenciam as operações de
I/O, executam na classe de prioridade de tempo real.
• Os sistemas em tempo real têm tempos de resposta previsíveis. Os resultados são bem
sucedidos se forem precisos e oportunos. O tempo de resposta nem sempre tem que ser
rápido. Um sistema é em “tempo real” quando as atividades de processamento têm prazos.
• Um sistema de tempo real "hard" é aquele em que a falha em atender um prazo indica uma
falha total do sistema.
• Com o tempo real "soft", perder um prazo indica que o sistema não está funcionando em seu
pico.
• Os sistemas em tempo real geralmente são reativos, o que significa que se comportam com
base nas condições do ambiente.
27. Processo de tempo real
Observação
• Ao aumentar a prioridade para o intervalo de tempo real, esteja
ciente que muitas threads importantes estão executando nesse
intervalo, caso essa thread fiquem muito tempo executando isso
pode bloquear funções críticas do sistema.
• Usando as APIs padrão do Windows , uma vez que um processo
tenha entrado no intervalo em tempo real, todas suas threads
(mesmo os de repouso) devem ser executadas em um dos níveis de
prioridade de tempo real.
28. Threads idle
• „Existe ainda uma classe especial chamada idle
(IDLE_PRIORITY_CLASS) a de mais baixa prioridade. Threads nesta
classe somente executam quando não existem outras threads aptas
(portanto, threads dessa classe não interferem na performance –não
causam overhead).
29. Estados da thread
No Windows XP:
1. Inicializado: É o momento em que o processo é iniciado para que possa ser
executado pelo processador
2. Pronto: Uma vez criada, a thread aguarda para usar o processador no estado
Pronto
3. Reserva: A thread entra no estado reserva quando é decidido em qual
processador será executada, aguardando por sua vez
4. Execução: Uma vez obtido o processador, a thread entra em estado de
execução. Ela irá sair desse estado quando:
1. Acabar o seu Quantum
2. Término da execução
3. Preempção (quando aparece outra de maior prioridade)
4. For suspenso
5. Estiver esperando um objeto
30. Estados da thread
5. Terminado: Quando a Thread terminou de executar
6. Espera: Quando a thread necessita de uma entrada ou saída
7. Transição: Quando a thread concluir sua espera, ou ela volta para o
estado pronto ou entra no estado de transição. A pilha de núcleo de
uma 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, a thread está pronta para executar.
8. Desconhecido: Quando o sistema não está seguro do estado da
Thread (usualmente devido a um erro).
32. Estados da thread
• A criação de um processo em Windows 2000 corresponde a
instanciar(criar) um objeto do tipo processo, o qual é uma espécie de
“molde” para novos processos.
• Nesse momento, uma série de atributos são inicializados para esse novo
processo, como, por exemplo, um identificador de processo (pid),
descritores de proteção, prioridades, etc.
• A unidade de escalonamento do Windows 2000 é o conceito de thread. A
cada processo está associada pelo menos uma thread.
• Cada thread pode criar outras threads. Essa organização permite a
execução concorrente dos processos, além de possibilitar uma
concorrência entre as threads que pertencem a um mesmo processo.
• A thread pode estar em um dos seis estados:
34. Troca de contexto
• Uma troca de contexto (também conhecido como chaveamento) é o
processo computacional capaz de armazenar e restaurar o estado
(contexto) de uma CPU de forma que múltiplos processos possam
compartilhar uma única instância de CPU.
• É garantido que quando o contexto anterior armazenado seja
restaurado, o ponto de execução volte ao mesmo estado que foi
deixado durante o armazenamento.
35. Troca de contexto
• O escalonador mantém uma fila de threads executáveis para cada nível
de prioridade. Estas são chamadas ready threads (threads prontas).
Quando o processador se torna disponível, o sistema realiza a troca de
contexto. Os passos da troca de contexto são:
• 1- Salvar o contexto da thread que acabou de ser executada
• 2- Colocar a thread que acabou de terminar sua execução no final da
fila de prioridade
• 3- Encontrar a fila de maior prioridade que contém ready threads
• 4- Retirar a thread no início da fila, carregar seu contexto e executá-la
36. Troca de contexto
Threads que não entram na lista de prontos:
• Threads criadas com a flag CREATE_SUSPENDED - A thread é criada
em um estado “suspenso” e não executa até que a função
ResumeThread seja chamada.
• Threads interrompidas durante a execução com função
SuspendThread ou SwitchToThread
• Threads esperando por um objeto de sincronização ou de entrada.
Enquanto threads que estão suspensas ou bloqueadas não ficam
prontas para rodar, o escalonador não aloca nenhum tempo de
processo a elas.
37. Troca de contexto
As razões mais comuns para troca de contexto são:
• Quantum esgotou
• Uma thread de maior prioridade está pronta para executar
• Uma thread em execução entra em espera
39. Troca Voluntária
• Uma thread pode renunciar voluntariamente o uso do processador dando a chance da próxima executar, inserindo um
estado de espera em um objeto (o Windows implementa um modelo de objeto para fornecer acesso consistente e seguro
para os vários serviços internos implantados)
• Objetos podem ser :
• Mutex
• Processor
• Threads
• Semáfaros, entre outros.
• Os mesmos chamam uma das funções de espera do Windows: WaitForSingleObject ou WaitForMultipleObjects (Pode
receber mais de um objeto por parâmetro). Os parâmetros dessas funções é um ou mais objetos e um time-out interval.
Os retornos possíveis são:
• WAIT_ABANDONED : Ocorre quando um processo tenta tomar posse de um mutex que ainda não foi liberado pelo processo que é seu atual dono, pois o
processo que o possui terminou de fazer a execução e esqueceu de liberar o mutex. Então o sistema, em nome do processo dono, libera o mutex para o
processo que pediu a posse e o estado do mesmo é setado para não sinalizado.
• WAIT_OBJECT_0 : Quando o estado do objeto é setado para sinalizado.
• WAIT_TIMEOUT : Quando o time-out interval é esgotado o estado do objeto é setado para não sinalizado.
• WAIT_FAILED : A função falhou, para descobrir o erro existe a função getLastError.
40. Preempção
• Nesse cenário de escalonamento, uma thread com prioridade menor é interrompida
quando uma thread com prioridade maior fica pronta para executar. Essa situação pode
ocorrer pelas seguintes razões:
• Uma thread de prioridade alta completa o estado de espera;
• A prioridade de uma thread é aumentada ou diminuída.
• Em ambos os casos o Windows deve determinar se a thread atual continua executando
ou se deve ser interrompida para permitir a execução da thread de maior prioridade.
• Threads executando no modo usuário podem interromper as threads de modo kernel, o
modo em que a thread executa não importa.
• Apenas a Prioridade é o fator que determina.
• Quando uma thread é interrompida, ela é posta no começo da fila de threads prontas
para a prioridade que estava executando.
41. Preempção
Na figura, a thread com prioridade 18 sai do estado de espera e recupera a CPU, fazendo com que a thread que
estava executando (com prioridade 16) seja interrompida. A thread interrompida não vai para o final da fila e sim
para o inicio, para que quando a thread em execução termine de executar a mesma possa completar seu
quantum.
42. Fim do quantum
• Quando o quantum de uma thread em execução acaba o escalonador
deve determinar duas coisas:
• Se diminuirá o nível de prioridade da thread que estava em execução
• Se irá escalar outra thread para a execução.
43. Fim do quantum - Reduzindo prioridade
• Se a prioridade for diminuída o escalonador irá procurar uma
thread mais apropriada para executar.
• Por exemplo, uma thread, na fila de prontos, com prioridade mais alta que
a nova prioridade da thread que estava em execução
44. Fim do quantum - Sem reduzir prioridade
• Se a prioridade não for reduzida e existirem outras threads com a
mesma prioridade na fila de prontos
• A thread que estava em execução é colocada no final da fila e o
escalonador seleciona a thread que será executada.
45. Gerenciamento do quantum
• O escalonador do Windows usa um contador de ciclos de clock
da CPU para manter os tamanhos dos quantums.
• Ele também utiliza esse contador para determinar se a thread
deverá receber mais um quantum.
46. Imaginando a seguinte situação
• Existem duas threads, 'A' e 'B', com mesmo nível de prioridade.
• Estas threads entram no estado de pronto no meio de um intervalo
de clock.
• A thread 'A' ganha o CPU.
• Durante a execução da thread ocorre uma interrupção de sistema.
• O que irá acontecer ao final do intervalo de clock.
48. Problemas
• Neste cenário a thread 'A' foi penalizada de forma injusta de duas
maneiras diferentes.
• O tempo gasto no tratamento de interrupção é retirado do tempo de
execução da thread.
• O tempo, dentro do intervalo de clock, em que o sistema estava inativo, é
retirado do tempo de execução da thread.
• No final do intervalo de clock o escalonador coloca a thread 'B' para executar.
50. Resultado
• Ao final do intervalo de clock o sistema percebe que o número do
ciclos de clock de CPU gasto executando a thread foi muito menor
que o esperado.
• Desta forma ele imagina que a thread começo a executar no meio de
um intervalo de clock.
• Por isso a thread recebe um acréscimo no quantum igual ao tamanho
de um intervalo de clock. Tendo assim a chance de ser executada por
um intervalo de clock completo.
• No final deste intervalo o escalonador coloca a thread 'B' para
executar.
51. Sistemas MultiProcessador
• Em um sistema com um único processador, o escalonamento é relativamente
simples: a thread de maior prioridade no estado pronto, será executada.
• Em sistemas com múltiplos processadores, o escalonamento é mais complexo,
pois o Windows tenta escolher o processador ideal para escalonar a thread,
levando em conta os processadores que ela havia ocupado previamente, para
aproveitamento de dados da execução anterior que podem se encontrar na
cache, bem como a configuração do sistema multiprocessador.
• O Windows tenta escalonar as threads do estado pronto de maior prioridade em
todas as CPU’s disponíveis, porém garante apenas que, uma das threads de maior
prioridade será executada em algum lugar.
• Windows mantém a trajetória da thread e o estado do processador em sistemas
com múltiplos processadores.
• Suporta três diferentes tipos de sistemas com múltiplos processadores (SMT,
multicore, e NUMA).
52. Sistemas SMT e multicore
• O Windows usa cinco campos na estrutura de dados KPRCB (Kernel Processor Control Block) para determinar corretamente as
decisões de escalonamento quando se trata de topologias de processadores lógicos.
• CoresPerPhysicalProcessor
• Determina se o processador lógico é parte de um pacote multicore.
• Calculado através da CPUID retornado pelo processador, arredondado para potencia de 2.
• LogicalProcessorsPerCore
• Determina se o processador lógico é parte de um conjunto de SMT.
• Processadores Intel com HyperThreading ativado.
• Calculado através da CPUID arredondado.
• PackageProcessorSet
• Preenchido por cada PRCB (Processor Control Block).
• Mascara de afinidade que descreve que os processadores lógicos dentro deste grupo pertencem ao mesmo processador físico.
• Pacotes são limitados a um grupo.
• CoreProcessorSet
• Conecta outros processadores lógicos ao mesmo núcleo.
• Também chamado de Conjunto de SMT.
• GroupSetMember
• Define qual máscara de bits, dentro do grupo atual do processador, identifica o processador lógico.
• Exemplo: Processador Lógico 3 -> GroupSetMember de 8 (2^3).
53. Sistemas NUMA
• NonUniform Memory Access
• Processadores são agrupados em unidades menores chamadas de nós.
• Cada nó possui seus próprios processadores e memória.
• Os nós são ligados a um grande sistema através de um barramento de interligação de
cache-coerente.
• São chamados assim pois cada nó tem a sua própria memória local de alta velocidade.
• Qualquer processador em qualquer nó pode acessar toda a memória disponível.
• A memória local do nó é muito mais rápida para acesso.
• O kernel mantém informações sobre cada nó em uma estrutura de dados chamada
Knode.
• KeNodeBlock é uma variável do kernel que representa uma matriz de ponteiros, para
cada estrutura Knode.
54. Afinidade
• Cada thread tem um mascara de afinidade que especifica os
processadores nos quais a thread pode ser executada.
• A mascara de afinidade da thread é herdada da mascara de afinidade
do processo.
• Por padrão, todos os processos (e portanto todas as threads)
começam com uma máscara de afinidade que é igual ao conjunto de
todos os processadores ativos do seu grupo de processador atribuído.
55. Afinidade
• No entanto, para otimizar o desempenho, os aplicativos podem optar
por alterar a mascara de afinidade de uma thread. Isto pode ser feito
em vários níveis:
• SetThreadAffinityMask
• Para definir a afinidade de uma thread especifica.
• SetProcessAffinityMask
• Para definir a afinidade para as threads de um processo especifico.
• Gerenciador de tarefas.
• No cabeçalho da imagem ao compilar o aplicativo
56. Afinidade
• Cada thread tem três números de CPU armazenadas no bloco de
controle da thread no kernel:
• Processador ideal
• Processador preferido para a execução da thread.
• Último processador
• Processador na qual a thread foi executada pela ultima vez.
• Próximo processador
• Processador na qual a thread vai ser executada.