Introdução
A industria de
computadores sempre buscou e buscara aumentar seu desempenho para um
melhor aproveitamento das outras industrias. Todos aproveitam dos
novos desempenham que cientistas da
computação descobrem e
inventam, sendo eles responsaveis por um importante papel na
evolução da ciencia como um todo.
Para um maior poder de
processamento, começou-se a usar vários processadores dentro de um
mesmo sistema, surgindo assim o “sistemas com múltiplos
processadores”, onde cada CPU executa suas atividades de uma
velocidade normal, mas trabalhando em conjunto aumentam muito o
poder de processamento.
A demanda que requer tais
sistemas com poder de processamento elevado variam muito, desde
modelagem de corrente de ar ao redor das asas das aeronaves até
entendimento das interações na recepção de drogas pelo cerebro.
Com o desenvolvimento da
internet, surgiu a possibilidade de interligar varias computadores
com um mesmo objetivo, surgindo sistemas com multiplos
processadores, mesmo que distante uns dos outros. O obstaculo em
tais conexões é o sistema de comunicação, que ainda impede um
desempenho superior.
O objetivo de todo sistema
com múltiplos processadores, ou qualquer ligação, como de
componetes eletrônicos, codificadas
por bits, é reduzir o tempo de envio de mensagens, reduzir a
distância e aumentar a eficiencia na lógica envolvida.
Existem
três tipos de sistemas com multiplos processadores:
Os mais rápidos, que tem o acesso a memória em normalmente de 2
a 50 ns. (m
1 ns, a luz percorre 29,9792458 cm exatos no vácuo
pela
definição do metro ),
são os que tem a memória
compartilhada(FIGURA
X). Nesse modelo, todo CPU tem acesso igual a qualquer parte da
memória fisica.
Em
seguida, com uma velocidade de 10 a 50 microsegundos (um
microsegundo representa um milionesimo de segundo, ou 10-6
de segundo) aparece o sistema de pares de CPU-memória(FIGURA Y),
onde são conectados por uma interconexão de alta velocidade.
Esses sistemas são conhecido como
multicomputadores,
pois representam várias sistemas de processamento de um CPU
interconectadas. Não existe memória compartilhada, como no caso
anterior, o que torna mais lento o seu nivel de processamento,
formando assim apenas um sistemas de computadores
interligados.Pórem, sua construção é mais fácil do que do
sistema anterior.
O
terceiro sistema, é o modelo constituido pela internet(FIGURA Z).
Nele, temos sistemas completos interligados, por uma rede de longa
distância, também chamados de
sistemas distribuidos.
Esse modelo é muito parecido com o anterior, se diferenciando
pelo espaço-tempo dos seus membros.
1-Multiprocessadores
de memória compartilhada
Um multiprocessador de memória
compartilhada, ou simplesmente multiprocessador, é um sistema onde
dois ou mais cpu´s compartilham a mesma memória RAM. O problema
mais usual, neste tipo de sistema é a diversificação de resultados
de um mesmo dado de entrada, por consequencia da alteração dos
dados por um outro processador. Pórem, essa é a vantagem, quando
bem programada, do uso do sistema de memória compartilhada, um
trabalho é feito por mais de um processador.
Os sistemas operacionais que funcionam
com os multiprocessadores executam as mesmas tarefas dos sistemas
operacionais com sistemas simples, como chamadas de sistemas e
gerenciamento de memória. Contudo, usa seu sistema de memória
compartilhada para aumentar o desempenho em algumas tarefas, como
gerenciamento de recursos e escalonamento.
FIGURA 8.1
1.1 hardware de um sistema com
multiprocessadores
O hardware de um sistema
multiprocessador pode ser dividido em dois classes: os que acessam
qualquer palavra ou bloco de memória no mesmo tempo, chamado de UMA
(UNIFORM MEMORY ACESS- Acesso Uniforme a Memória) e os que acessam
partes da memória com mais rapidez do que outras partes, chamados de
NUMA (NOUNIFORM MEMORY ACESS-Acesso a Memória Não-Uniforme)
-Multiprocessadores NUMA Baseados em
Barramento
Os multiprocessadores geralmente se
comunicam com apenas um barramento. Quando o processador precisa
alocar ou capturar uma palavra que está na memória, e o barramento
não se encontra ocupado, ele envia informações junto com com os
sinais de controle da respectiva função através do barramento. Mas
este barramento geralmente se encontra ocupado, surgindo assim o
principal problema dos multiprocessadores NUMA baseados em
barramento.
Quando há vários processadores,
dificilmente o barramento se encontra disponivel. O fator que limita
o desempenho de processamento neste caso é o tamanho do barramento.
Em casos, há a possibilidade de colocar uma memória cache para cada
CPU, para que algumas leituras usem apenas o cache local, liberando
tráfego no barramento, e possibilitando o sistema a incluir novos
CPU´s em sua estrutura. Na memória cache, é trabalhada o bloco que
encontra a palavra referenciada, levada para dentro do cache local,
chamando esse bloco de palavras dentro do cache local de linha de
cache.
Os blocos de cache
podem ser somente-leitura, caso em que não há limites de cópia
desse bloco em outra memória cache ao mesmo tempo, ou
leitura-escrita, que acontece quando a CPU vai trabalhar os dados
referentes a esse bloco do cache. Se existe uma palavra que está
sendo processada por uma CPU dentro do cache de outra CPU, e essa
cópia ainda não foi trabalhada, ele é instruido a descartar a sua
cópia, e deixar a CPU interessada processar e depois copiarem o
mesmo bloco processado.Se outra cache tem a cópia já trabalhada,
ela é instruida a copiar para o barramento para ser repassada para o
outro CPU interessado ou copiá-lo diretamente na memória. Esse
procedimento, com mais uma série de regras são chamados de
protocolo de coerência de cache.
Há ainda a
possibilidade de cada CPU ter a sua memória local e privada, além
do cache. Para esse recurso ser otimizado, cabe ao compilador colocar
as informações necessarias, como o código do programa e as
constantes dentro dentro da memória local. Com esse recurso, a
memória compartilhada só é acessada para as variavéis
compartilhadas que podem ser escritas. Com isso, o compilador se
torna ainda mais importante, reduzindo assim o tráfego de
barramento.
-Multiprocessadores UMA Baseados em
Chaves Crossbar
O uso de caches ainda não é
suficiente para deixar ilimitado o numero de CPU´s conectados em
barramento, geralmente limitando em 16 ou 32 CPU´s. Para a resolução
deste problema, os sistemas multiprocessadores fazem o uso de chaves
Crossbar, que são usadas há tempos nos sistemas de telefonia, que
possibilitam que qualquer memória se conectar diretamente com
qualquer CPU.
O sistema de chaves Crossbar é
composto por chaves que interligam as partes. Quando essas se
encontram fechadas, formam um caminho direto entre a CPU e a memoria,
tornando usavel seu sistema de comunicação.
O número de combinações é o numero
de memória vezes o numero de CPU´s. Uma grande vantagem desse
sistema é que a conexão nunca é negada, pois independe de outras
partes do sistema. E ainda, o sistema não precisa ser previamente
avisado antes de realizar uma conexão entre
a memoria e a CPU. No caso de duas
CPU´s acessar a mesma memoria ao mesmo tempo, essa é dividida para
atender as duas CPU´s.
A grande desvantagem desse sistema com
o uso de chaves Crossbar é o numero muito elevado das chaves, que
cresce exponencialmente ao acrescentar-mos CPU´s e memorias,
tornando um projeto com mais de mil CPU´s ou memoria impraticavel.
Mas para um projeto de tamanho medio, com media de 30 CPU´s e
memoria, essa opção se torna a mais usual.
-Multiprocessadores UMA Baseados em
Redes de Comutação MultiEstagio.
As redes de comutação multiestagio é
composta por duas opções de entrada e duas opções de saida para
cada trecho de rede. Com a mensagem colocada no trecho, ela ganha
duas possibilidades até seguir seu caminho desejado.
A mensagem é composta de acordo com o
conteudo da mensagem, porem existem quatro campos obrigatorios. O
campo Modulo passa a informação para o próximo trecho do programa
de qual memoria usar, o campo Endereço informa em que trecho do
sistema a mensagem se encontra, e os campos codigoOP e Valor
representam a operação a ser feita e o valor da operação,
respectivamente.
Um modelo muito usado é a rede Omêga,
composta por 8 CPU´s e 8 memórias, usando 12 chaves. Esse modelo,
se projetado com o sistema de chaves Crossbar, usaria 64 chaves.
A grande desvantagem desse modelo Omêga
é que ela é rede bloqueante. Isso acontece quando duas CPU´s
queiram usar a mesma chave, fazendo com que uma tenha que esperar o
encaminhamento para poder seguir seu caminho. As esperas podem
acontecer na tentativa do uso simultaneo da mesma chave ou do mesmo
fio (ou cabo).
-Multiprocessadores NUMA.
Os sistemas apresentados anteriormente
não são usaveis em redes de grande porte, com mais de 100 CPU´s e
100 memórias, por necessitarem de um hardware pesado, o que custaria
muitos recursos financeiros para a sua implementação.
O sistema NUMA se assemelha ao sistema
UMA pois também possui um único espaço de endereçamento para
todas as CPU´s para realizar a sua interconexão, mas diferentemente
do sistema UMA, o sistema NUMA possui o acesso a memória local mais
rapido do que o acesso remoto.
Os sistemas NUMA possui três
caracteristicas principais: Existe um espaço de endereçamento
único, visivel a todas as CPU´s, o acesso a memoria local é feito
via instruções LOAD e STORE, e o acesso a memoria local é mais
rapido do que o acesso a memoria remota. Elas ainda se dividem em
maquinas que possuem memoria cache local (CC NUMA – NUMA
Cache-Coherent ou seja, NUMA com coerencia de Cache) e maquinas que
não possuem cache local (NC NUMA – No Cache NUMA, ou seja, NUMA
sem memoria cache).
O modelo mais usado em sistemas
multiprocessador é o CC NUMA, que toma como base um diretorio que
faz a interconexão entre as maquinas, e faz o gerenciamento dos
processos. Essa base de dados é pesquisada em cada instrução que
referencia a memoria, possuindo um hardware muito veloz para reponder
a pesquisa em uma fração de um ciclo de barramento.
Exemplo de um Multiprocessador baseado
em diretorio.
Multinucleo
Com os transistores cada vez mais
pequenos, aumentou-se o numero de transistores em cada chip. Os
processadores da classe Intel Core2 Duo contêm cerca de 300 milhões
de transistores. Uma alternativa para o aumento de eficiencia é
alocar dois nucleos no mesmo chip.Chips com 3 ou núcleos já estão
presentes no mercado, e futuramente trabalharemos com chips com
centenas de nucleos.
Pórem, eles semprem dividem a mesma
memoria principal, que proporciona cada palavra da memoria com um
valor. Há um processo que, com a modificação de uma palavra, todas
as suas copias que não foram trabalhadas são apagadas, afim de
evitar retrabalho, conhecido como espionagem.
Há diferenças que devem ser
consideradas para a formação do software. Primeiramente, os
multiprocessadores baseados em barramento, cada CPU tem (seguindo o
modelo da Intel) sua cache própria, sendo que a diferença para o
processador que ira usar o software (Intel ou não) deve ser
considerado na hora de sua codificação. No modelo Intel, quando um
CPU necessita de mais memoria cache do que possui, ele “empresta”
de outra CPU. Como desvantagem, um problema apresentado por uma CPU
pode afetar a CPU do mesmo processador, problema que não ocorre nos
processadores tradicionais.
Na classe de processadores multinucleo,
exitem tambem os sistemas em um chip, que possuem uma ou mais CPU
principal, além do nucleo que se dedica exclusivamente as operações
especiais, como os decodificadores.
Mesmo com essa nova ferramenta nas mãos
dos programadores, a programação paralela ainda é escassa.
Sincronização e eliminações de situações de corrida são
problemas presentes hoje. Além da falta de conhecimento de que quais
processos realmente necessitam desse recurso.
Tipos de Sistemas Para
Multiprocessadores
Nos sitemas multiprocessadores, várias
formas do Sistema Operacional são possíveis. As três mais usadas
são onde cada CPU tem seu próprio Sistema Operacional, modelo
“mestre-escravo” e multiprocessadores simétricos.
No modelo onde cada CPU tem seu próprio
Sistema Operacional, a memória é dividida pelo numero de CPU´s do
sistema. Para a otimização, geralmente as CPU´s compartilham os
codigos do Sistema Operacional e fazem suas copias privadas somente
dos dados.
A vantagem que esse esquema leva em
relação ao Sistema Multicomputadores é que nesse modelo as CPU´s
compartilham o mesmo disco rigido, dispositivos de S/E, além da
memoria ser flexivel, mudando de acordo com o processo. Nesse modelo,
quatro caracteristicas devem ser frizadas: Cada chamada de sistema é
feita e tratada em sua própria CPU, como cada Sistema Operacional
tem suas próprias tabelas, cada CPU processa suas tarefas
indepedente de outras, não existindo o compartilhamento de
processos, podendo surgir uma desigualdade de trabalho, tornando um
CPU sobrecarregado enquanto outro fica ocioso. Em terceiro lugar,
não existe compartilhamento de paginas, onde cada CPU tem suas
paginas individualmente. Ainda, existe a possibilidade de existir em
uma cache um bloco de disco das ultimas tarefas feitas, e cada
Sistema Operacional tem sua cache própria, podendo haver
divergencias entre os blocos existentes. Para evitar esse problema,
se faz necessario a eliminação de cache de buffer, o que diminui
seu desempenho.
Esse modelo foi muito utilizado no
começo das estruturas de multiprocessadores, mas já é dificilmente
utilizado.
Multiprocessadores Mestre-Escravo
Nesse modelo, o Sistema Operacional é
presente somente na CPU I, e as restantes se encarregam de executar
processos do usuario, enquanto a CPU I só executa processos de
usuario se houver tempo disponivel.
Uma grade vantagem nesse modelo é que
existe uma única lista de processos a serem feitos, sendo assim, se
uma única CPU fica ociosa, ela pede ao sistema um processo para
executar, além de existir um único cache de buffer, evitando
incosistencias.
A sua desvantagem aparece em grandes
redes. Se uma CPU Mestre gasta 20% do seu tempo com as chamadas de
sistema, 5 CPU´s Escravas já ocupam todo o tempo da CPU Mestre.
Multiprocessadores Simetricos.
Nesse modelo, existe uma copia do
Sistema Operacional na memória, e qualquer CPU pode executa-la.
Nesse modelo, é eliminado a duplicação de tabelas do Sistema
Operacional, como no modelo de vários Sistemas Operacionais.
Mas, em contrapartida, se duas ou mais
CPU´s executam o mesmo trecho do Sistema Operacional, pode ocorrer
ocorrencias. Para evitar esse problema, usa-se o Mutex,
que funciona como um semaforo, fazendo cada CPU executar o Sistema
Operacional por vez.
Um problema que
pode ocorrer se assemelha ao modelo Mestre-Escravo. Caso dois ou mais
CPU´s queiram executar o Sistema Operacional, havera fila. Pórem,
como varias partes do Sistema Operacional são indepedentes uma das
outras, podem ser executadas simultaneamente. O mutex só se
encarrega de liberar ou não o trecho do sistemaop.
Esse é o modelo
mais usado nos sistemas multiprocessadores atuais, e saber separar os
trechos do sistemaop para uma boa programação do mutex se torna o
maior desafio na sua montagem.
Nos processos de
evolução do codigo, pode acontecer que uma região precisa de uma
tabela nova, que só seria criada em um processo depois de um tempo
outro CPU. Por isso, não deve ter rodizio de programadores nessa
estrutura de multiprocessadores.
Sincronização em
Multiprocessadores
Em sistemas
multiprocessadores, a sincronização dos CPU´s é parte de
fundamental importância para um desempenho superior ser alcançado.
A sincronização é
importante para proteger os processos, para serem finalizados
corretamente e não travarem. Se um processo em um processador
realiza uma chamada de sistema que requer o acesso a alguma tabelas
criticas do nucleo, o codigo do nucleo pode desabilitar a interrupção
antes de manipular a tabela. Cada interrupção afeta somente a CPU
que esta executando. O mutex tem que ser seguido por todos os CPU´s
do sistema.
Para a finalização
de um processo ocorrer sem erro e sem a intrometeção de outro
processo, é preciso sincronizar as ações, para que, quando um
processador realiza uma chamada de sistema e precisar acessar alguma
tabela critica do nucleo, o nucleo não a bloqueie. Nesse caso é
usado um protocolo, que nada mais é do que a inspeção da palavra
na memória. Uma instrução TSL é usada para impedir o acesso de
uma parte da memória.
Teste continuo ou
chaveamento
Com os novos sistemas de
multiprocessadores, surgiu a possibilidade de processadores ficarem
esperando a tarefa (thread) a realizar, através da lista de
processos, não tendo autonomia para escolher alguma tarefa a
executar.
Porém, há casos em que essa
autonomia acontece. Se um processador fica ocioso, esperando algum
recurso para prosseguir a sua execução, ele em vez de ficar
esperando, inicia uma nova execução. Problema que não acontece em
sistemas monoprocessadores.
Tanto ficar repetindo (teste continuo)
o uso de uma variavel para verificar se o resultado é valido, quando
após o primeiro teste é verificado seu travamento, ou realizar o
chaveamento (processamento de algo alternativo) desperdiçam ciclos
da CPU, visto que é preciso salvar o estado atual, a variavel de
tratamento atual, além de ter que abrir um novo thread.
Pode acontecer que a variavel esteja
travanto o processamento possa ser liberada mais rapidamente do que o
tempo de salvamento e carregamento de uma nova tarefa, o que valeria
mais a pena ficar esperando a liberação do que iniciar uma nova
execução.
Além do chaveamento e do teste
continuo, uma noção alternativa seria registrar cada impedimento,
para decidir se seria melhor chaveamento ou o teste continuo para
cada parada da CPU. Após alguns estudos (Karlin, 1989 e Outeshout,
1982 ), a maioria dos testes são feitos com um tempo limite para
cada tipo de travamento, se esse tempo é atingido, ocorre um
chaveamento para outra tarefa, esse tempo pode ser fixo ou variar de
acordo com os resultados observados nos testes. Os resultados mais
eficientes são aqueles que tomam como base o tempo limite observando
os tempos anteriores, e estipulando que esse tempo se repetira.
Escalonamento de Multiprocessadores
È importante para o entendimento de
escalonamento observar que há threads do nucleo e de usuario. Se o
thread é de usuário e o núcleo não tiver informações sobre ele,
não conseguiria escalonar. Mas se o thread é de núcleo, com as
informações que ele contém do thread, pode trabalha-lo
completamente, indepedente do processo ao qual pertence.
Se o processador é mono, a questão é
simples, apenas escolher o próximo processo a executar. Porém,
quando se trabalha com multiprocessadores, devem ser escolhidas os
próximos processos e em quais CPU´s serão executados. O
escalonamento deve sincronizar essas informações.
Há processos que se relacionam com
outros, e processos que trabalham sem se relacionar. Os processos que
não contém relacionamentos podem trabalhar sem a interferencia de
outros, pois seu resultado, indepedente de qual seja, não afetara os
demais. Mas os processos que interferem em outros devem ser tratados
com cuidado. Por exemplo, um processo que afeta um dado em algum
arquivo que sera usado por outro programa na sua compilação,
contendo macros, definições de tipo e variaveis. Quando isso
ocorre, é invocado um programa make, que
analisa a situação e recompila os arquivos que sofreram alterações.
Tempo Compartilhado
Os sistemas mais
simples de escalonamento consideram uma única lista de threads para
a realização em todas as CPU´s, com cada thread tendo uma
prioridade diferente.
Por exemplo, se a
CPU 1 fica ociosa, escolhe o próximo processo da fila (no caso a
tarefa A). Em seguida, o processador 4 fica ocioso, e a próxima
tarefa da fila segundo a ordem de prioridade é a B, que é
imediatamente levada a CPU 4, e em seguida a CPU 2 fica livre, é
amarrada com a próxima da lista, neste caso a tarefa C. Esse
processo é simples, e só pode ser usado caso as tarefas não são
relacionaveis.
Essa estrutura é parecida com os do
sistema monoprocessadores, pois dificilmente algum processador ficara
ocioso por muito tempo, e fornece o balanceamento de energia e
processos por CPU. Um ponto fraco neste sistema é a sincronia quando
se tem um numero elevado de CPU´s.
Compartilhamento de espaço
Pode acontecer de num processo, as
threads se comuniquem frequentemente, neste caso, é util faze-las
executar ao mesmo tempo. O escalonamento de multiplos threads ao
mesmo tempo sobre multiplos CPU´s é chamado compartilhamento de
espaço.
Para esse processo ocorrer, tem que
haver um numero minimo de CPU´s livres, para que cada thread fique
em uma CPU. Caso isso não seja possível, o sistema espera até isso
acontecer. O processador seguira sua thread até que todo o bloco
termine, e após o bloco ter terminado, o bloco inteiro é colocado
em disposição. Se uma thread é bloqueada esperando alguma ação
de E/S, ele continua sendo alocado na CPU até que o desbloqueio
possa ser feito.
Acontece ainda dos conjuntos de CPU´s
sejam divididos de acordo com a tarefa realizada. Por exemplo, um
sistema com 20 CPU´s pode realizar 3 blocos de tarefa com o uso de 6
CPU´s cada, e ainda ter 2 CPU´s não usados.
Escalonamento em bando
O compartilhamento de espaço oferece
a opção de não ser mais usado o chaveamento, pois as CPU´s não
esperaria uma variavel que necessite para a conclusão da tarefa em
execução. Porém, pode acontecer de um bloco de tarefa em execução
depender de apenas uma CPU, tornando uma grande do seu poder de
processamento parado, até que essa termine e libere a variavel.
Vamos supor que um conjunto de CPU´s
esteja trabalhando com dois blocos, A e B, e que cada bloco contenha
duas tarefas, A1 e A2, B1 e B2.
CPU 0
A0 B0 A0 B0
CPU 1 B1 A1 B2 A1
LINHA DE TEMPO 1 2 3
4
No espaço de tempo 1, A0 envia uma
mensagem para A1 da CPU 1 e espera até obter sua resposta, o que só
acontece no tempo 3, sendo que a CPU 1 trabalha durante o tempo 2.
Nessa caso, a CPU 1 espera por 2 momentos até que siga sua operação.
Para a resolução deste problema, é
usado o escalonamento em bando, que contém três bandos:
1-Os grupos de threads relacionados são
escalonados como uma unidade chamada bando.
2-Todos os membros do bando executam
suas tarefas simultaneamente, em diferentes CPU´s, com tempo
compartilhado.
3-Todos os membros de um bando iniciam
e finalizam junto suas fatias de tempo.
Nesse modo, em cada quantum é colocada
em uma nova thread nas CPU´s. Se um thread é bloqueado, sua CPU
permanece ociosa apenas até o final do quantum.
CPU 0 1 2
TEMPO A1
A2 A3
0
1 B1 B2 C1
2 A1 A2 A3
No exemplo a cima, todas as tarefas do
bloco A são executadas ao mesmo tempo. Isso ocorre para que as
ligações referentes ao Bloco A sejam feitas com a tarefa em
execução, para uqe sejam requisitadas e respondidas quase que
imediatamente.