Troca de Mensagens - TerraLAB

Propaganda
Troca de Mensagens
_ Notas de Aula _
Prof. Tiago Garcia de Senna Carneiro
DECOM / UFOP
Num sistema distribuído não há memória compartilhada. Portanto, toda a comunicação
entre processos deve ser realizada através de troca de mensagens.
•
Pilha de Protocolos
Os processos de um sistema distribuído comunicam-se através da pilha de protocolos da
rede que interliga as máquinas sobre as quais estão executando. Entretanto, este fato pode
impor sérias restrições ao desempenho do sistema.
Quando uma mensagem é recebida ou enviada através de uma pilha de protocolos, cada
camada da pilha tende a anexar às mensagens enviada pelos processos um cabeçalho
contendo informações de controle.A existência de todos esses cabeçalhos cria uma
sobrecarga considerável no sistema. Nas redes de longa distância, onde a banda passante é
limitada a algums kbps (tipicamente, 64kbps), esta sobrecarga não chega a ser um problema
sério. O fator limitante neste caso é a capacidade das linhas de transmissão. Mesmo com
toda a manipulação de cabeçalhos, os processadores são suficientemente rápidos para
manter as linhas trabalhando à máxima velocidade. Então, um sistema distribuído de longa
distância pode usar pilhas de protocolos, tais como o TCP/IP, sem comprometer de forma
considerável o desempenho da comunicação entre processos. Entretanto, num sistema
distribuído baseado em rede local a sobrecarga introduzida pela pilha de protocolos é
desastrosa. Os processadores consomem um tempo muito grande processando protocolos
que acabam transformando a vazão (throughput) da rede em apenas uma pequena fração da
sua capacidade de transmissão nominal.
Portanto, a maioria dos sistema distribuídos baseados em rede local não utilizam protocolos
em camadas ou, no caso de usarem, implementam somente um subconjunto da pilha de
protocolos.
Além disso, para alcaçarem melhor desempenho os sistemas distribuídos tendem a evitar
protocolos baseados em conexão com o TCP/IP. O modelo-cliente servidor é basedo em um
protocolo muito simples, sem conexão, do tipo solicitação/resposta. O cliente envia uma
mensagem ao servidor solicitando algum serviço, o servidor faz o trabalho e envia para o
cliente o resultado ou um código de erro. Este esquema apresenta como vantagens a
simplicidade e a eficiência. Assumindo um sistema homogêneo, este esquema de
comunicação poderia ser implementado diretamente sobre a camada de enlace da pilha de
protocolos TCP/IP evitando-se as demais camadas.
•
Endereçamento
Para que um processo possa enviar mensagens a um processo remoto, ele precisa conhecer
o endereço desse processo. O endereço, end, de um processo deve indentificar numa
primeira instância a máquina na qual ele está executando e depois, quem é ele entre os
vários processos executando naquela máquina. De posse desse endereço, qualquer processo
pode invocar a primitiva send( end, &msg ) do sistema para eviar mensagens ao outro
processo ou então a primitiva receive( end, &msg) quando desejar receber mensagens desse
processo.
Vários esquemas de endereçamento podem ser utilizados, cada um apresenta vantagens e
desvantagens:
a) Endereçamento máquina.processo:neste esquema o endereço de um processo é
formado por duas partes, a primeira parte é o endereço da máquina onde o processo está
executando e a segunda parte é o endereço do processo dentro da máquina. Supondo a
existência de uma máquina cujo endereço IP seja 200.131.209.193 e a existência de um
número que identifica um processo dentro daquela máquina como o processo 5291, o
endereço do processo poderia ser expresso como 200.131.209.193.5291.
Este esquema apresenta dois problemas. O primeiro diz respeito à transparêcia à
migração que todo sistemas distribuído deve prover. O fato de usarmos o endereço da
máquina como parte do endereço quebra tal transparência uma vez que o processo 5291 não
poderia migrar para outra máquina sem causar problema, pois os processos que precisam
comunicar-se com ele não o encontrariam mais. Além disso, para garantir a transparência á
migração deveríamos garantir que nehum outro processo em todo o sistema fosse
identificado pelo número 5291, ou seja, que o identificador 5291 tivesse um escopo global,
pois só dessa forma o processo 5291 podera ser encontrado caso migrasse para uma outra
máquina do sistema. Neste momento é que surge o segundo problema. Que método seria
utilizado para gerar identificadores universais para todos os processos do sistema? Este
método iria interferir na confiabilidade ou desempenho do sistema distribuído? Por
exemplo, centralizar a reponsabilidade de gerar esses identificadores em um único processo
do sistema, com certeza, iria criar um novo gargalo no sistema além de fazer com que todo
o sistema dependa do bom funcionamento de um único processo.
Grande parte do esquema de comunicação do UNIX de Berkley utiliza um esquema um
pouquinho diferente desse: endereçamento máquina.local-ident. O campo máquina é o
endereço IP de 32 bits da máquina e o campo local-ident é um número de 16 bits escolhido
radômicamente pelo próprio processo. Um processo começa sua execução com uma
chamada ao núcleo do sistema para informá-lo que seu identificador é local-ident. A
probabilidade de um processo escolhar um ident-local que esteja sendo utilizado é remota,
mas caso isso aconteça o processo deverá escolher outro identificador.
b) Endereçamento de processo em broadcast: neste método um processo possui um
endereço que não depende do endereço da máquina onde está rodando. Quando um
processo A precisa comunicar-se com um processo B, o processo precisa antes de qualquer
coisa enviar uma mensagem broadcast na rede perguntado "Que máquina está executando
o processo B?". Todas as máquinas receberão esta mensagem, mas somente a máquina que
estiver o processo B responderá enviando o seu endereço para o processo A.
O problema com este esquema é que ele pode causar muita sobrecarga na rede, pois todo
processo para comunicar-se com outro precisará executar um broadcast. Além disso, um
processo terá sempre que esperar pelo endereço da máquina que executa o processo com
quem deseja se comunicar, esta fato torna-se ainda mais grave a penalização que a
comunicação entre processos impõe ao desempenho dos sistemas distribuídos.
c) Endereçamento de através de servidores de nome: no esquema de endereçamento
através de servidores de nome um processo extra é utilizado para mapear o nome de
serviços escritos em alto nível (ASCII), para endereços de máquina. Este processo é
chamado servidor de nome.
Serviços são facilidades que processos servidores oferecem a outros processos do sistema.
Um serviço é está sempre associado a um porto padronizado por convenção, por exemplo, o
serviço de ftp é associado ao porto 21 e o serviço de e-mail é associado ao porto 25. Desta
forma, quando o processo deseja solicitar um serviço, ele primeiro deve consultar o
servidor de nome para saber o endereço da máquina que executa o processo que fornece o
serviço e depois ele deve conectar-se a esta máquina informando o porto que corresponde
ao serviço que ele deseja. O porto é um número que permite ao sistema operacional
multiplexar a rede entre vários processos. Todo processo que deseja fornecer algum serviço
através da rede deve associar-se a um porto, assim quando uma mensagem chegar através
da rede o sistema operacional terá como saber para qual processo a mensagem deverá ser
entregue.
O problema deste esquema de endereçamento é que ele insere um componente centralizado
no sistema: o servidor de nome.
•
Comunicação Síncrona versus Comunicação Assíncrona
A comunicação entre processos pode ocorrer de duas maneiras diferentes. Na comunicação
síncrona quando um processo invoca a primitiva send(addr, &buffer) para enviar uma
mensagem a outro processo, a sua execução fica suspensa até que a mensagem realmente
chegue ao processo destinatário. Quando um processo invoca a primitiva receive( addr,
&buffer), a execução desse processo fica suspensa até que uma mensagem vinda do
processo addr chegue. A comunicação síncrona também é conhecida como comunicação
com bloqueio ou bloqueante. Na comunicação assíncrona a execução de um processo não
é bloqueda quando o processo invoca a primitiva send ou receive.
A comunicação assíncrona é a que apresenta um menor custo em termos de desempenho,
pois permite ao sistema distribuído explorar ao máximo o paralelismo de processamento e
comunicação. Entretanto, é a forma de comunição que apresenta maiores dificuldades de
implementação, além de criar novos problemas para o programador. Por exemplo, suponha
que um processo tenha invocado de forma assíncrona a primitiva send(addr, &buffer) e que
portanto, sua execução tenha continuado a ser realizada. Quando este processo terá certeza
que pode alterar o conteúdo da variável buffer sem alterar a informação a ser trocada
durante a comunicação? A resposta é simples, não há como.
Duas soluções podem ser dadas para o problema acima:
1) antes de ser enviada toda mensagem deve ser copiada para uma área de armazenamento
temporário interna ao núcleo do sistema. Assim, qualquer alteração no conteúdo da variável
buffer não influenciará a comunicação em andamento. Fatores críticos para esta estratégia é
a determinação do tamanho dessa área de armazenamento temporário e o número máximo
permitido de mensagens assíncronas sendo enviadas.
2) sempre que uma mensagem terminar de ser entregue ao seu destino, o processo que a
gerou deverá ser interrompido e informado do fato, ou seja, uma interrupção será gerada na
máquina do processo que originou a mensagem. Porém, o uso de interrupções torna o
programação dos processos comunicantes mais difícil, além de quase impossibilitar a
depuração dos mesmos.
Um aspecto importante da comunicação síncrona é a necessidade do uso de
temporizadores. Este temporizadores deverão ser utilizados para evitar que processos
fiquem eternamente esperando pela confirmação do envio de uma mensagem ou pela
chegada de uma mensagem gerada por outro processo.
•
Comunicação com Bufferização versus Comunicação SemBufferização
Suponha que um processo A envie uma mensagem M para um processo B, mas que o
processo B ainda não tenha executado a primitiva receive(addrA, &buffer), ou seja, que o
processo B ainda não esteja esperando por uma mensagem. Quando a mensagem chegar à
máquina onde o processo B está executando o sistema operacional deverá descartar a
mensagem ou esperar que o processo B a solicite? O que acontecerá se o processo B nunca
requisitar a mensagem M?
Duas soluções podem ser adotadas para resolver este problema. A primeira e a menos
desejada é simplesmente proibir que um processo invoque a primitiva send se não houver
um lugar para armazená-la na máquina destino. Isto é, o processo transmissor ficará
bloqueado até o processo destino invoque a primitiva receive. A segunda solução faz uso de
um buffer interno ao núcleo do sistema operacional chamado mailbox e de temporizadores.
A medida que as mensagens são recebidas elas vão sendo inseridas no mailbox que
funciona como uma fila de mensagens e para cada mensagem um novo temporizador é
iniciado com o objetivo de medir o tempo de permanência da mensagem no mailbox.
Conforme os processos forem requisitando as mensagens, estas vão sendo retiradas do
mailbox. Se nehum processo requisitar uma determinada mensagem e seu temporizador
atingir um valor máximo permitido, a mensagem é descartada.O problema com a segunda
solução é a sobrecarga gerada pela manutenção dos mailboxes.
•
Comunicação Confiável versus Comunicação Não-Confiável
Muitas vezes o envio de uma mensagem deve ser confiável, isto é, o processo que enviou a
mensagem deve receber uma confimação do processo destino informando o perfeito
recebimento da mensagem. Este mecanismo evitaria que uma mensagem perdida ou corrompida
atrapalhe o funcionamento do sistema.
Num arquitetura cliente-servidor a confirmação pode ser necessária tanto para garantir que um
servidor recebeu a solicitação de um cliente como para garantir que um cliente recebeu a
resposta de um servidor. Como tanto a solicitação do cliente como a resposta do servidor são
recebidas pelo núcleo do sistema operacional na forma mensagens a serem entregues aos
processos servidor e cliente, respectivamente, o próprio núcleo do sistema poderia confirmar o
recebimento dessas mensagens evitando-se que esses processos tivessem que fazê-lo, resultando
num melhor desempenho do sistema.
Para melhorar ainda mais o desempenho do sistema, quando o processamento solicitado por um
cliente for muito pequeno, em termos computacionais, a própria resposta a esta solicitação
poderia ser utilizada como confirmação. Desta forma, não sera necessário o envio de duas
mensagens para o mesmo processo onde um intervalo de tempo muito pequeno separaria uma
mensagem da outra, evitando-se sobrecargas na rede de comunicação do sistema distribuído.
Download