• Nenhum resultado encontrado

Comunicação entre Processos

No documento Sis to Per 2001 (páginas 38-46)

Durante o seu funcionamento muitos processos precisam trocar informações com outros processos. Para que esta comunicação ocorra, os sistemas distribuídos necessitam de um mecanismo específico. Como já visto, pode–se considerar um sistema distribuído estratificado em diversos níveis de abstração. Nos níveis inferiores temos conexões para o transporte de mensagens, as quais são devidamente empacotadas e roteadas através da malha de comunicação. Estes níveis menos abstratos do sistema, que incluem o sistema operacional e a rede física, devem permanecer transparentes aos processos. Para tanto, no nível da camada de transporte do sistema é oferecido um serviço de troca de mensagens, que se constitui o menor nível de comunicação entre processos de um sistema. Utilizando este mecanismo, no nível de apresentação é implementado um serviço de solicitação e resposta via chamada de rotinas, como se fossem pontos de interface de programas de amplificação para a camada de transporte. Tal mecanismo é conhecido por chamada a procedimentos remotos e serve como mecanismo de condução das transações da camada de aplicação. Traduzindo em outras palavras, transações são seqüências de solicitações e/ou respostas que requerem tratamento atômico da comunicação. Seqüências de solicitações ou respostas acionam chamadas a procedimentos remotos as quais passam as solicitações por meio de

mensagens aos processos comunicantes. Segue-se uma tabela que exemplifica a estratificação mencionada.

Aplicação Transações

Apresentação Chamadas a Procedimentos Remotos Transporte Passagem de Mensagem

S.O. (rede) Conexões de Transporte Comunicação

⇑ ⇓

Enlace/Físico Chaveamento de Pacotes

No texto a seguir serão vistos cada um dos níveis de abstração superiores, que possibilitam a comunicação entre processos. Inicialmente será analisado o mecanismo de passagem de mensagens, sobre o qual se baseiam os demais. Na seqüência serão apresentadas as chamadas a procedimentos remotos e, para concluir esta seção, será visto o mecanismo de transações.

Passagem de Mensagens

Uma mensagem é um conjunto de objetos com estrutura semântica definida pelos processos cooperantes. Somente estes processos interpretam a informação contida no corpo da mensagem. O restante do sistema manipula somente o cabeçalho da mensagem, que contém as informações de controle.

Um processo, para enviar uma mensagem, utiliza primitivas do serviço de transporte. O mais comum são as primitivas send(destino,msg) e receive(fonte,msg), onde msg significa mensagem.

Os processos usam uma de quatro formas para trocar mensagens entre si, quais sejam: por nome, por ligação, utilizando caixas de correio e através de portas.

Quando a troca de mensagens entre processos é feita por nome, ou seja, um processo identifica seu interlocutor pelo seu nome, se estabelece um caminho de comunicação bidirecional entre eles. Este caminho é lógico e o fato de existir não implica nenhuma conexão física. Portanto, só pode haver um caminho por vez entre dois processos. Para ser único, o nome do processo é formado pelo endereço da sua máquina hospedeira concatenado com a identificação do processo. Esta formulação do nome dos processos permite que um processo X que estiver sendo executado em uma máquina A, converse com outro processo X que estiver sendo executado em uma máquina B. Ela também permite que um processo Y sendo executado em uma máquina C, se comunique com qualquer dos processos X mencionados, sem confundi-los.

Sendo a troca de mensagens feita por ligação, elimina-se a limitação de uma conexão por par de processos. Neste caso, podem ser estabelecidos vários caminhos de dados (circuitos virtuais) unidirecionais, os quais são gerenciados pelo núcleo do sistema operacional. Os caminhos podem ser estabelecidos, por exemplo, em tabelas de ligação armazenadas nas máquinas que participam do sistema. Sempre que há uma solicitação de um processo para se comunicar com outro estas tabelas são consultadas e a melhor rota é estabelecida para a comunicação. As mensagens contém o endereço da máquina destino e as tabelas contém informações sobre a(s) melhor(es) rotas para se atingir cada destino possível. Tais tabelas são atualizadas dinamicamente, para incluir informações sobre rotas e máquinas defeituosas, que não podem ser acessadas, e sobre máquinas e rotas que voltaram a fazer parte ativa do sistema. Além destas, outras informações sobre as condições de tráfego são atualizadas dinamicamente nestas tabelas.

No caso de se estabelecerem caixas de correio para serem usadas na comunicação entre os processos a ligação se torna mais flexível, por assim dizer. As caixas de correio seguem o modelo dos produtores e consumidores, permitindo ligações multiponto e multicaminho. No caso de

ligações multiponto, se estabelece uma caixa de correio e informa-se aos processos comunicantes a localização desta caixa. Sempre que um processo desejar transferir uma mensagem ele a coloca na caixa de correio. Quando qualquer um dos demais processos estiver pronto para receber alguma mensagem ele examina a caixa de mensagens e verifica se alguma mensagem lhe foi enviada. Se houver mensagem o processo a lê, caso contrário ele continua seu processamento normal, o qual pode incluir uma fase para o processo aguardar a chegada de uma mensagem. Note-se que este mecanismo permite a divulgação de informações para todos os processos comunicantes, basta que o processo emissor deposite uma mensagem com um endereço genérico que permita sua leitura por todos os demais processos. As ligações multicaminho servem, por exemplo, para aumentar a capacidade de comunicação entre dois processos. Para que isto ocorra, basta que um dos processos comunicantes abra duas (ou mais) caixas de correio para determinada ligação. Enquanto nos dois casos anteriores a comunicação precisa ser feita diretamente entre os dois processos comunicantes, neste caso a comunicação pode ser indireta, devido à possibilidade de se estabelecer ligações multiponto por meio deste mecanismo.

A troca de mensagens feita por intermédio de portas é, em verdade, um caso especial de caixa de correio, que utiliza uma abstração de uma fila FIFO, de tamanho finito, mantida pelo núcleo.

Enquanto as caixas de correio utilizam elementos de armazenamento intermediário que podem ser acessados aleatoriamente, as portas fazem uso de filas cujo acesso deve ser feito ordenadamente. Os sistemas podem ou não utilizar elementos de armazenamento intermediário para as mensagens. Quando estes elementos de armazenamento intermediário são utilizados no sistema, é possível tratar mensagens com outras aguardando na fila e reduzir as diferenças de velocidade entre os processos comunicantes. Aqueles processos que utilizam tais elementos de armazenamento intermediário implementam o modelo dos produtores e consumidores, enquanto aqueles que não utilizam esses elementos implementam o modelo rendezvous.

Além da questão da utilização de elementos para o armazenamento temporário de mensagens, o seu envio pode ser síncrono ou não. As considerações sobre a forma de comunicação feitas a seguir referem-se à Tabela I.

Tabela I – Formas de Comunicação entre Processos

Processo 1 Processo 2

envia núcleo fonte canal de comunicação núcleo destino recebe

1 → 2 → mensagem → 3 → 4 (pedido)

↓(serviço) 8 ← 7 ← reconhecimento ← 6 ← 5 (resposta) O primeiro caso consiste no envio assíncrono, sem bloqueio, ou seja, o processo é liberado tão logo a mensagem seja montada e repassada para o núcleo fonte. Neste caso o processo que origina a mensagem fica envolvido apenas nas etapas 1 e 8 da Tabela I.

Os outros casos referem-se a formas de envio síncrono. Portanto, o segundo caso consiste no envio síncrono com bloqueio simples. Neste caso, o processo só é liberado após a mensagem ser transmitida ao canal de comunicação. Isto porque se houver algum problema com o envio da mensagem e o núcleo do sistema fonte perdê-la antes dela ser transmitida ao canal ela não poderá mais ser recuperada, pois o processo que solicitou o envio a deu por enviada. Então esta abordagem mantém o processo bloqueado até que o núcleo tenha sucesso na sua transmissão para o canal de comunicação. Desta forma o processo que origina a mensagem fica envolvido nas etapas 1, 2, 7 e 8 da Tabela I.

O terceiro caso consiste no envio síncrono com bloqueio, dito, confiável. Agora o processo só é liberado após a mensagem ser recebida pelo núcleo do processo destino. Isto garante que se houver algum problema com o canal de comunicação o processo transmissor poderá retransmitir a mensagem. Este procedimento aumenta a confiabilidade da comunicação, pois o canal de comunicação costuma ser um meio não confiável. Desta feita o processo que origina a mensagem fica envolvido nas etapas 1, 2, 3, 6, 7 e 8 da Tabela I.

O quarto e último caso considerado é o de envio síncrono com bloqueio explícito, quando o processo transmissor fica bloqueado durante toda a transmissão, só sendo liberado após o recebimento da solicitação pelo processo destino e durante o período em que este estiver computado a resposta desejada. Portanto, o processo que origina a mensagem fica paralizado durante todas as etapas da comunicação, envolvendo as etapas 1, 2, 3, 4, 5, 6, 7 e 8 da Tabela I. Há outro mecanismo bloqueante que é o modelo de solicitação/resposta, no qual o processo que origina a mensagem fica bloqueado durante todo o processo de comunicação, inclusive pelo tempo necessário para o processo receptor da mensagem gerar a resposta solicitada, ou seja, durante o cômputo no servidor. Este mecanismo faz com que o processo que origina a mensagem fique paralizado, não somente durante as etapas 1 a 8, mas também durante o período de serviço, indicado na Tabela I.

Até aqui foi visto o mecanismo básico de passagem de mensagens entre processos. Agora serão consideradas duas estruturas de comunicação, através das quais as mensagens podem ser transferidas de um processo para outro. Estas estruturas são interfaces para programas de aplicação conhecidas por pipes e sockets. Elas servem para poupar usuário da preocupação com detalhes de

implementação, tais como o tamanho da memória para armazenamento intermediário das mensagens, a capacidade do canal de comunicação e a sincronização de acessos. Para que este mascaramento do sistema possa ser feito define-se uma interface para utilização pelos programas aplicativos, ficando o gerenciamento da comunicação por conta do núcleo do sistema.

Incialmente, tratar-se-á da materialização do modelo de produtores e consumidores em uma estrutura chamada de pipe. Cria-se uma fila FIFO, de tamanho finito, mantida pelo núcleo. Esta fila

é criada através de uma chamada ao sistema, que devolve dois descritores, um de leitura e outro de escrita na fila. O processo que deseja se comunicar utiliza o descritor de escrita para escrever suas mensagens no final da fila (entrada do pipe). O processo com o qual ele deseja se comunicar utiliza

o descritor de leitura para ler as mensagens depositadas no início da fila (saída do  pipe). Pipes

existem somente enquanto os processos leitores e escritores estiverem ativos, pois, como só permitem comunicação unidirecional e exclusivamente entre os processos chamador e chamado não teria sentido manter filas FIFO ativas no sistema após o encerramento das atividades de

qualquer dos processos comunicantes. É possível criar named-pipes, que são arquivos FIFO

especiais associados a um nome e um caminho, através dos quais processos não relacionados podem utilizar pipes. Os pipes tratam os dados como seqüências de bits, sem interpretá-los. A

interpretação do conteúdo semântico das mensagens fica por conta do processo que as recebe. Por questões de confiabilidade da transmissão, ou se escrevem todos os bytes de uma mensagem no  pipe ou nada se escreve. O uso de pipes restringe-se a um só domínio dentro de um sistema de

arquivos.

Para viabilizar a comunicação entre domínios diversos, onde nem estruturas de dados nem arquivos podem ser compartilhados e nomeados de forma inequívoca, usam-se sockets. Eles são pontos

finais de canais de comunicação, gerenciados pelo serviço de transporte. Através deles as operações de entrada e saída na rede são modeladas como entrada e saída em arquivos. O sistema cria um descritor, que é um ponto de comunicação lógico, local ao processo. Este descritor, que deve ser associado (pelo sistema) a um ponto de comunicação físico, é utilizado para enviar e receber dados através da camada de transporte do sistema. Isto é possível porque é feito um mapeamento entre o descritor lógico, na interface de aplicação, e o descritor físico, na interface de rede. Este mapeamento pode ou não ser feito através de uma conexão. Se for feito por meio de uma

conexão explícita, o processo que inicia a comunicação identifica o processo com quem deseja se comunicar e o sistema estabelece uma ligação entre seu ponto lógico de comunicação (local) e o ponto de comunicação físico do processo remoto. Esta associação permanece enquanto os processos estiverem trocando informações, não sendo mais necessário identificar os processos comunicantes durante esta transmissão. Um problema com este tipo de comunicação é a necessidade de um conjunto de protocolos para manter a segurança da comunicação. Tipicamente é usado um protocolo em duas camadas: uma camada que pretende garantir a privacidade dos dados transmitidos por meio da criptografia simétrica dos dados; e outra para garantir a integridade e a autenticidade dos dados, utilizando mecanismos de verificação de erros e promovendo em clientes e servidores uma criptografia assimétrica com uso de chave pública.

Chamadas a Procedimentos Remotos

O mecanismo de chamadas a procedimentos remotos é uma abstração, no nível de linguagens, do mecanismo de comunicação por solicitação/resposta, baseado na passagem de mensagens. Sintaticamente, uma chamada a um procedimento remoto é idêntica a uma chamada a um procedimento local. Entretanto, a semântica é diferente, pois envolve atrasos e eventualmente algum tipo de tratamento de falhas.

Chamadas a procedimentos remotos escondem dos clientes detalhes de chamadas ao sistema, de conversões de dados e de comunicação na rede. O cliente é aquele que inicia a chamada para execução de algum serviço em uma máquina remota. A solicitação é passada a um ordenador, na máquina do cliente, que agrega os parâmetros necessários à transmissão da mensagem. Após a devida parametrização a mensagem é passada ao núcleo, que se encarrega de sua transmissão utilizando o mecanismo de transporte da rede. A mensagem é enviada ao servidor, que é aquela máquina na qual o procedimento será executado. Ao receber uma mensagem o núcleo do servidor a repassa para o respectivo ordenador, o qual fará a devida extração dos parâmetros e encaminhará a solicitação ao servidor. Após processada, o servidor retornará uma resposta ao cliente, por um caminho simétrico, isto é, a resposta será repassada ao ordenador do servidor, que empacotará a mensagem agregando-lhe parâmetros. Esta mensagem empacotada será repassada ao núcleo do servidor, que se comunicará com o núcleo do cliente e efetivará o transporte da mensagem com a resposta pela rede. Ao recebê-la, o núcleo do cliente a repassará para o ordenador do cliente, que desempacotará a mensagem, finalmente entregando a resposta para quem a solicitou.

Um exemplo de chamada a procedimento remoto é a chamada a uma rotina para a leitura de algum arquivo em uma máquina remota. O cliente solicita o serviço executando a atribuição arquivo = lê (da, pos, nbytes), em que daé um descritor do arquivo, pos é a posição do arquivo onde a leitura deve ser iniciada e nbytesé o número de bytesa serem lidos do arquivo. Ao iniciar o procedimento

os dados são colocados na pilha do servidor, juntamente com as variáveis locais, para serem usadas durante a execução. Ao concluir a execução estes dados são eliminados da pilha.

Para que os dados de controle, como os mencionados no exemplo do parágrafo anterior, possam ser utilizados pelo servidor, permitindo a execução correta do procedimento desejado, é necessário transferir estes parâmetros entre o cliente e o servidor. Esta passagem de parâmetros pode ser efetuada de diversas formas, como as quatro vistas a seguir.

O cliente pode passar diretamente o(s) valor(es) do(s) parâmetro(s) a ser(em) utilizadas pelo servidor. Ao receber o(s) parâmetro(s) o servidor o(s) copia para uma (ou mais) variável local, conforme necessário. Modificações feitas pelo servidor nas variáveis locais não afetam o valor das variáveis do cliente. Desta forma, o servidor pode trabalhar livremente sem interferir no processo que o chamou. Todavia, muitas vezes o que se quer é exatamente a alteração controlada de algumas variáveis. Neste caso, há um duplo trabalho, pois o servidor deverá fazer as alterações necessárias

durante o processamento, enviar uma cópia dos valores obtidos para o cliente, que então deverá efetuar as modificações cabíveis nas cópias locais das variáveis.

Uma alternativa para o problema levantado no parágrafo anterior seria o cliente enviar o nome da variável local, em vez do seu valor. Desta forma, o servidor poderia atuar diretamente sobre as cópias das variáveis existentes no cliente. Entretanto, em um sistema distribuído este acesso a variáveis em máquinas remotas não é um processo fácil, requerendo a avaliação de expressões sintáticas em tempo de execução. Portanto, este método não é muito utilizado na prática de sistemas distribuídos.

Outra alternativa ao problema mencionado nos dois parágrafos anteriores seria a passagem de um ponteiro (endereço) para servir de referência à variável, ou às variáveis, que se deseja tratar. Neste caso o problema é que o espaço de endereçamento em máquinas diferentes é, também, diferente. Portanto, não tem sentido a passagem de parâmetros por referência em um sistema distribuído. A terceira alternativa, e última a ser apresentada neste texto, é uma mescla das chamadas por valor e por referência. Ao fazer uma chamada o cliente cria um vetor com os valores dos parâmetros que deseja transferir ao servidor e passa o endereço deste vetor, e seu tamanho, para o ordenador do cliente. Com estas informações, o ordenador copia os valores adequados em uma mensagem e a envia ao ordenador do servidor. Este, ao recebê-la, copia os valores para um vetor na memória do servidor e chama-o, passando-lhe um ponteiro para o vetor criado. Note-se que este ponteiro nada tem a ver com o utilizado pelo cliente. Durante o processamento no servidor, todas as modificações feitas refletem-se nos valores contidos no vetor presente em sua memória. Ao concluir o procedimento, com as informações no vetor eventualmente modificadas, o servidor aciona seu ordenador, devolvendo-lhe o ponteiro para o vetor utilizado. A mensagem segue então seu caminho de volta ao ordenador do cliente que, de posse do vetor modificado, atualiza o vetor na memória do cliente. Esta alternativa é razoavelmente eficiente para estruturas simples, como arranjos. Todavia, seu tratamento não é trivial para estruturas mais complexas, como árvores e grafos.

Uma vez estabelecidos os mecanismos para a troca de mensagens, outro problema que surge é a localização do servidor. Para solicitar um serviço o cliente deve saber a quem faze-lo. Isto só pode ser feito se ele puder identificar cada servidor existente no sistema. Uma maneira de viabilizar esta espécie de consciência é gravar, em cada cliente, os números das máquinas e as versões de cada um dos programas servidores. Um problema com esta abordagem é que qualquer mudança deve ser comunicada a todos os clientes em potencial. Se um servidor for transportado de uma máquina A para outra máquina B, porque esta está temporariamente menos carregada do que aquela, esta mudança deve ser comunicada a todos os clientes. Se for feita uma cópia de um servidor em uma máquina diferente, para ampliar as possibilidades de serviço e melhorar a distribuição de carga do sistema, esta mudança também deve ser comunicada a todos os clientes. Em fim, qualquer modificação em qualquer dos servidores deve ser comunicada a todos os possíveis clientes. Este procedimento causará um grande congestionamento no sub-sistema de comunicação do sistema distribuído. Uma solução para este problema é apresentada no parágrafo a seguir.

Cria-se um processo de ligação dinâmica, responsável pela coordenação dos serviços do sistema. Um servidor, ao iniciar suas atividades envia para o processo de ligação uma mensagem contendo seu nome, sua versão, um identificador e um manipulador (seu endereço ethernet ou IP, por exemplo). Este processo corresponde ao registro do serviço no processo de ligação. O servidor pode cancelar seu serviço enviando outra mensagem ao processo de ligação com seu nome, sua versão e seu identificador. Quando um cliente solicita um serviço, seu ordenador verifica se já está ligado ao servidor. Se não estiver, manda uma mensagem ao processo de ligação contendo o nome e a versão do servidor desejado. Se o serviço não estiver disponível, o processo de ligação retorna uma indicação de falha ao ordenador do cliente. Se houver um servidor adequado, o processo de ligação retorna o manipulador e o número de identificação do servidor. O ordenador do cliente então envia mensagens ao endereço contido no manipulador, contendo os parâmetros e a identificação do servidor. Esta identificação é necessária para que o núcleo do servidor possa

direcionar a solicitação para o servidor correto. As vantagens na utilização de processos de ligação

No documento Sis to Per 2001 (páginas 38-46)

Documentos relacionados