Seljuk : Um Ambiente para Suporte ao Desenvolvimento e à
Transcrição
Seljuk : Um Ambiente para Suporte ao Desenvolvimento e à
Seljuk1: Um Ambiente para Suporte ao Desenvolvimento e à Execução de Aplicações Distribuídas Robustas Francisco Vilar Brasileiro [email protected] Universidade Federal da Paraíba - UFPb/Campus II Centro de Ciências e Tecnologia - CCT Departamento de Sistemas e Computação - DSC Laboratório de Sistemas Distribuídos - LSD Av. Aprígio Veloso, 882 58109-970, Campina Grande, Paraíba http://www.dsc.ufpb.br/~lsd RESUMO Construir aplicações distribuídas que satisfaçam requisitos de confiança no funcionamento é uma tarefa difícil. A utilização de ferramentas apropriadas e a escolha da semântica de falha mais restritiva possível para os componentes que formam a infraestrutura de execução da aplicação, são abordagens usualmente seguidas para reduzir esta complexidade. Neste trabalho apresentamos a arquitetura Seljuk, que utiliza estas duas abordagens para especificar um ambiente operacional onde aplicações distribuídas robustas possam ser mais facilmente implementadas e executadas. O artigo apresenta também o ambiente operacional Seljuk-Amoeba, que é uma implementação do Seljuk sobre o sistema operacional distribuído Amoeba. ABSTRACT Building dependable distributed applications is not an easy task. Designers of such systems have used two approaches to reduce design complexity: i) the use of appropriate developing tools; and ii) the choice of the most restrictive possible failure semantics for the components that form the underlying execution layer. In this paper we present the Seljuk architecture, which uses these two approaches to specify an operating environment that facilitates the construction and execution of dependable distributed applications. The paper also presents an implementation of the architecture based on the Amoeba distributed operating system, named Seljuk-Amoeba. 1. INTRODUÇÃO O nível de desenvolvimento tecnológico alcançado pelas áreas de microprocessadores e comunicação tem permitido que sistemas distribuídos, compostos por uma coleção de unidades de processamento conectadas por uma rede de comunicação de alta velocidade, provejam uma relação custo/desempenho melhor do que aquela oferecida por sistemas centralizados tradicionais. Em alguns casos, um sistema distribuído pode até suplantar o desempenho de um mainframe, tornando-se a única solução factível para aplicações que requerem um altíssimo nível de desempenho. Além disso, algumas aplicações que apresentam características inerentemente distribuídas (ex. controle de processos industriais, processamento de transações remotas, trabalho cooperativo, etc.), só podem ser desenvolvidas, de forma eficaz, dentro do contexto de um sistema distribuído. Finalmente, um sistema distribuído, desde que projetado com o cuidado necessário, pode potencialmente 1 Durante os séculos X e XI os seljúquios desenvolveram um exército altamente efetivo, responsável, entre outras conquistas, pela subjulgação da armada Bizantina. Além disso, os seljúquios estabeleceram um Estado altamente coeso e bem administrado. oferecer um nível de confiança no funcionamento 2 muito maior do que aquele oferecido por um sistema centralizado. Estas características são as principais responsáveis pelo acelerado crescimento no número e na diversidade dos sistemas distribuídos desenvolvidos. Entretanto, para que uma determinada aplicação distribuída atinja um alto nível de confiança no funcionamento, falhas isoladas de componentes do sistema no qual ela executa devem causar o menor impacto possível (ou, idealmente, não causar nenhum impacto) na operação da aplicação como um todo. Desta forma, a habilidade de tratar e tolerar efetivamente as faltas 3 que possam vir a ocorrer, é uma característica primordial em um sistema distribuído. Quando a implementação dos mecanismos para tolerância a aplicação, a complexidade desta cresce. De fato, um dos principais implementação de aplicações distribuídas robustas (aquelas que consideráveis de confiança no funcionamento) reside justamente na pela necessidade de se tratar e tolerar faltas [Cris91]. faltas está a cargo da problemas no projeto e apresentam requisitos dificuldade introduzida A literatura mostra que os projetistas de aplicações distribuídas robustas têm buscado reduzir a complexidade da construção de tais sistemas, principalmente, através da utilização de duas abordagens complementares, quais sejam: i) a utilização de ferramentas de apoio apropriadas para a construção destes sistemas (ex. bibliotecas com funções para tolerância a faltas [HK93], toolkits de programação [BJ87, SDP91], construções especiais de linguagens de programação [Maes87], serviços especializados do sistema operacional [Ng90, MRTRS90], etc.); e ii) a escolha da semântica de falha mais restritiva possível para os componentes da infra-estrutura de execução [Cris91]. Neste trabalho, descrevemos a arquitetura do ambiente operacional Seljuk, cujo objetivo principal é facilitar o desenvolvimento e a execução de aplicações distribuídas robustas. Para tal, o ambiente oferece dois níveis de serviços: i) serviços de alto nível que implementam ou auxiliam a implementação dos principais mecanismos para tolerância a faltas requeridos por aplicações distribuídas robustas; e ii) serviços de um nível mais baixo, que permitem restringir a semântica de falha dos componentes que formam a infra-estrutura sobre a qual a aplicação irá executar. Uma das características mais importantes do Seljuk é a sua flexibilidade, que possibilita a coexistência de aplicações distribuídas com diferentes requisitos de confiança no funcionamento, num mesmo sistema distribuído, mas pagando apenas por aqueles serviços para tolerância a faltas por elas requisitados. Além disso, o ambiente também permite que os requisitos de confiança no funcionamento das aplicações e a semântica de falha dos componentes que formam a infra-estrutura de execução, sejam definidos no momento da ativação das mesmas. Isso possibilita, entre outras coisas, o balanceamento entre as medidas de desempenho e de confiança no funcionamento que se deseja alcançar para as aplicações, em tempo de ativação, e sem a necessidade de qualquer mudança no código executável destas. O restante deste artigo é estruturado da seguinte forma. Na Seção 2 serão discutidas as abordagens usuais utilizadas na construção de aplicações distribuídas robustas. A Seção 3 apresenta o modelo utilizado para descrever a arquitetura e os pressupostos assumidos. A arquitetura Seljuk é detalhada na Seção 4, onde serão estudados os serviços que devem ser providos pelo ambiente. A Seção 5 é dedicada à apresentação do Seljuk-Amoeba, uma instanciação da arquitetura, baseada no micro-núcleo distribuído Amoeba [MRTRS90]. Encerra o artigo a Seção 6 que traz as nossas conclusões e direções futuras do trabalho. 2 [LV91] propõe uma terminologia em Português que mapeia aquela sugerida por Laprie em seu conhecido artigo [Lapr89]. No artigo de Lemos e Veríssimo, o termo confiança no funcionamento é usado para traduzir o termo dependability. 3 Ainda seguindo a terminologia proposta em [LV91], usaremos os termos falta, erro e falha como respectivas traduções dos termos fault, error e failure. 2. CONSTRUINDO APLICAÇÕES DISTRIBUÍDAS ROBUSTAS Como mencionado anteriormente, a complexidade da construção de uma aplicação distribuída robusta pode ser reduzida através da utilização de ferramentas apropriadas e da escolha da semântica de falha mais restritiva possível para a infra-estrutura de execução da aplicação. Nesta seção discutiremos como estas estratégias têm sido usadas em sistemas apresentados na literatura. 2.1. Ferramentas para Construção de Aplicações Distribuídas Robustas Os primeiros sistemas tolerantes a faltas construídos eram soluções ad-hoc para problemas específicos (ex. [HSL78, W*78, Lala86]). Com o crescimento do número de aplicações com requisitos de confiança no funcionamento, surge a necessidade de se produzir ferramentas que possibilitem que estes sistemas possam ser construídos com o reaproveitamento do esforço desprendido na construção de outros sistemas com características similares. A seguir discutimos algumas ferramentas apresentadas na literatura que têm sido usadas para tal fim. Bibliotecas de Funções para Tolerância a Faltas Uma forma de se promover re-usabilidade de software é através da utilização de bibliotecas de funções. Com pouco esforço de programação, é possível utilizar mecanismos para tolerância a faltas que já tenham sido usados (e, principalmente, testados) em outras aplicações. Um exemplo desta tecnologia é discutido em [HK93]. Naquele artigo, os autores apresentam vários exemplos de utilização das funções da biblioteca libft, para construir aplicações tolerantes a faltas. A biblioteca contém funções para especificar e realizar checkpoint de dados críticos da aplicação, recuperar a informação de checkpoints, atualizar diário de eventos (logs), localizar e reconectar servidores, realizar tratamento de situações excepcionais [Cris91], além de prover suporte para programação diversificada [Aviz85] e uso de blocos de recuperação [Rand75]. Por exemplo, a função critical() pode ser usada para especificar dados críticos da aplicação que devem fazer parte de um checkpoint que, por sua vez, pode ser salvo através da ativação da função checkpoint(). Construções Especiais de Linguagens de Programação A consolidação do paradigma de orientação a objetos para o desenvolvimento de sistemas de software fez emergir um outro tipo de ferramenta - os toolkits de programação. A idéia aqui é usar os mecanismos de herança das linguagens de programação orientadas a objetos, para disponibilizar classes de objetos que implementam mecanismos para tolerância a faltas. Estes mecanismos podem então ser usados pela aplicação, através da utilização de subclasses derivadas daquelas classes providas pelo toolkit. O sistema Arjuna [SDP91] é um exemplo deste tipo de ferramenta. O modelo computacional do Arjuna baseia-se na utilização do conceito de transações atômicas aninhadas, que controlam as operações realizadas sobre objetos persistentes, para a construção de aplicações distribuídas robustas. A complexidade do tratamento da distribuição, da consistência, e da persistência dos objetos que implementam a aplicação é escondida do programador pela infra-estrutura provida pelo Arjuna. Além da reutilização de componentes inerente ao modelo de orientação a objetos, recentemente o conceito de reflexão computacional [Maes87] também tem sido explorado para auxiliar a construção de aplicações distribuídas robustas. De uma forma simplificada, a idéia é dividir o sistema em uma parte funcional (objetos da aplicação) e outra de controle e gerência (meta-objetos). Em [LC95] esta técnica é utilizada para prover tolerância a faltas de software. Serviços Especializados de Sistemas Operacionais Distribuídos A necessidade de desenvolver aplicações distribuídas que tolerem faltas que possam afetar a infra-estrutura de execução destas aplicações tem influenciado o projeto dos sistemas operacionais modernos, notadamente dos sistemas operacionais distribuídos. A maior parte destes sistemas implementa serviços de comunicação com variável grau de confiabilidade (ex. [CZ83, MRTRS90, Rozi92]). Outros vão além e oferecem serviços de processamento confiável [Ng90]. No sistema ROSE [Ng90] um serviço de detecção de falhas é provido pelo núcleo do sistema. Um processo pode usar este serviço para detectar a falha de um outro processo, possivelmente executando em um outro processador do sistema distribuído. Além deste serviço, o sistema também oferece um serviço de replicação de espaço de endereçamento, que permite a replicação de dados em processadores diferentes. Com base nestes dois serviços, o sistema possibilita a execução de processos usando tanto replicação ativa como passiva [Powe92]. 2.2. Impondo Restrições à Semântica de Falhas dos Componentes A complexidade dos mecanismos para tolerância a faltas reflete as considerações feitas sobre a semântica de falha dos componentes do sistema, necessários para a execução destes mecanismos [Cris91], ou seja, as considerações feitas sobre que tipo de falha os componentes poderão apresentar. Quanto mais restritiva a semântica de falha de tais componentes, mais simples será a implementação dos mecanismos. Levando-se esta consideração ao extremo, se os componentes nunca falham (semântica de falha mais restritiva possível), não há necessidade de adicionar à aplicação qualquer mecanismo para tolerância a faltas. Uma abordagem tomada pelos projetistas de um considerável número de sistemas distribuídos apresentados na literatura (ex. [Bart81, KM85, BJ87, Ng90, MRTRS90, SDP91, Powe92]), é desenvolver os sistemas assumindo que os processadores e canais de comunicação que compõem a infra-estrutura sobre a qual estes serão construídos, possuem uma semântica de falha controlada (fail-controlled [Lapr89]), ou seja, apresentam uma semântica de falha bem definida e previsível. Muitos sistemas tolerantes a faltas descritos na literatura, foram construídos assumindo que eles executam em unidades de processamento - doravante denominadas de nodos - que falham de forma segura (fail-safe nodes) [Lapr89], ou seja, o nodo quando falha, pára, ao invés de realizar uma transição não especificada. Dois tipos de nodos representativos desta classe de nodos com semântica de falha segura são os nodos com semântica de falha estável (fail-stop ou fail-stable nodes) [Schn84, Bras95] e os nodos com semântica de falha silenciosa (fail-silent nodes) [Bern88, WB91, SESTT92, RS93, BESST96]. Outros sistemas assumem que os nodos nunca falham, desta forma eles devem executar em nodos que possam garantir este comportamento por toda a duração da execução da aplicação (tempo de missão). Nodos que mascaram falhas (failure-masking nodes) [HSL78, W*78, S*78, Lala86, Theu86, SESTT92, Powe92, STBES93, BES95b] formam uma outra classe de nodos, que são capazes de oferecer um serviço correto, com uma probabilidade arbitrariamente alta. Quando a infra-estrutura disponível não pode prover, com probabilidade suficientemente alta, a semântica de falha assumida, faz-se necessário construir nodos, que, de fato, apresentem a semântica de falha requerida. 2.2.1. Construindo Nodos com Semântica de Falha Controlada Nodos com semântica de falha controlada podem ser construídos através do agrupamento de processadores convencionais redundantes, que falham de maneira independente, e de forma arbitrária (falhas Bizantinas). A computação é replicada e executada simultaneamente em cada um dos processadores formando o nodo. Os resultados da computação de cada um dos processadores são validados por um mecanismo apropriado (ex. comparação, votação majoritária, etc.), que garante a semântica de falha do nodo. A maioria dos nodos com semântica de falha controlada apresentados na literatura são implementados em hardware [HSL78, Lala86, Bern88, WB91]. Os processadores formando o nodo são sincronizados a cada ciclo de relógio, e a cada ciclo os resultados das operações de cada um dos processadores são validados por um circuito que implementa a semântica de falha do nodo. A principal vantagem desta abordagem é a reduzida perda de desempenho introduzida pelos circuitos especiais. Entretanto, existem alguns problemas: primeiro, os processadores precisam ser construídos de tal maneira que eles tenham um comportamento determinista em cada ciclo de relógio; segundo, a introdução de circuitos especiais aumenta a complexidade do projeto, o que, em última instância, pode reduzir a confiabilidade do nodo como um todo; terceiro, é difícil incorporar avanços tecnológicos, sem que seja necessário um considerável esforço de (re)projeto do nodo; quarto, este tipo de abordagem não tolera faltas de projeto nos processadores; finalmente, o fato de se ter os mecanismos para tolerância a faltas fisicamente incorporados ao nodo, implica na forçosa distribuição do custo associado a estes mecanismos por todas as aplicações executando no nodo, inclusive por aquelas que não necessitam de serviços para tolerância a faltas [Bras95]. Em contraposição à abordagem baseada em hardware, foi proposta uma abordagem baseada em software [W*78], que tenta resolver a maior parte dos problemas citados acima. Nesta abordagem, os protocolos necessários para sincronizar os processadores redundantes do nodo e validar o resultado das operações realizadas por estes processadores, garantindo então a semântica de falha do nodo, são implementados em software. Esta abordagem é muito mais flexível do que a anterior, permitindo inclusive a utilização de diferentes tipos de processadores (design diversity) em um mesmo nodo, o que possibilita o tratamento de faltas de projeto na construção dos processadores. Além disso, a atualização tecnológica destes nodos é trivial, pois os protocolos implementados em software precisam apenas ser recompilados para a nova plataforma. Por outro lado, o principal problema da abordagem de software é a possibilidade de uma queda acentuada no desempenho do sistema. [Bras95] apresenta uma forma eficiente de construir diversos tipos de nodos com semântica de falha controlada, implementados em software, e identifica classes de aplicações para as quais a redução de desempenho do sistema, devido aos mecanismos para tolerância a faltas, é bastante reduzida. 3. MODELO E PRESSUPOSTOS 3.1. Componentes da Arquitetura Uma arquitetura pode ser descrita em função dos seus componentes e das relações de dependência entre estes componentes [Cris91]. Um componente implementa um conjunto de operações que podem ser ativadas através de requisições explícitas por parte dos usuários do componente ou simplesmente através da passagem do tempo. Os componentes de um sistema podem ser implementados em hardware ou em software. O conjunto de operações implementadas pelo componente especifica o serviço por ele oferecido. Por exemplo, o serviço oferecido por um interpretador Java [Flan96] consiste do processamento de todas as operações definidas pelo byte-code da linguagem. Por sua vez, para fornecer o seu serviço, o interpretador Java utiliza os serviços oferecidos por outros componentes do sistema (ex. os serviços de alocação de memória e escalonamento de processos de um sistema operacional, o serviço de processamento de um processador, etc.). Podemos definir uma relação de dependência entre os componentes de um sistema, da seguinte forma: um componente A depende de um outro componente B, quando a corretude do comportamento de A depende da corretude do comportamento de B. Por exemplo, componentes implementados em software dependem dos serviços de processamento oferecidos pelo sistema operacional (criação e escalonamento de processos, gerência de memória, etc.). Estes, por sua vez, dependem do serviço de processamento oferecido por um processador. Dentro do contexto do nosso trabalho, uma aplicação distribuída é implementada por uma coleção de componentes (tipicamente processos) que se comunicam exclusivamente através da troca de mensagens. Os componentes da aplicação dependem dos serviços oferecidos pelos outros componentes do ambiente operacional para proverem o serviço da aplicação. Os componentes das aplicações podem apresentar comportamento determinístico ou não. Quando o comportamento apresentado por um determinado componente é não determinístico, nem todos os serviços oferecidos pelo ambiente operacional poderão ser usados. Adiante discutiremos detalhadamente quais as restrições que se aplicam a componentes com este tipo de comportamento, e como os componentes da aplicação podem ser construídos para, mesmo apresentando comportamento não determinístico, poderem fazer uso de todos os serviços oferecidos pelo Seljuk. 3.2. Faltas e Semântica de Falhas O comportamento de um componente após a ativação de uma de suas operações pode ser observado através das mudanças internas de seu estado e dos resultados emitidos por ele. A especificação do componente define, para todas as possíveis ativações do componente, quais as mudanças do seu estado interno e quais os resultados que ele deve emitir. O comportamento de um componente é correto quando está de acordo com a sua especificação. Faltas no sistema podem fazer com que um componente execute uma transição diferente daquela definida pela sua especificação e desta forma falhe. As faltas que podem ocorrer em um sistema podem ser divididas em dois grandes grupos: i) faltas físicas, ou seja, aquelas geradas por fenômenos físicos internos ao sistema; e ii) faltas humanas, as quais englobam faltas de projeto - quer seja de hardware ou de software - cometidas durante a fase de construção do sistema, e faltas de interação, que são violações dos procedimentos de operação e manutenção do sistema [SS92]. Dentro do escopo deste trabalho, o nosso interesse será principalmente na tolerância a faltas físicas e faltas de projeto de hardware e de software. Para que se possa tolerar os efeitos causados por uma falta em um componente é importante conhecer os possíveis comportamentos faltosos que o componente pode exibir, ou seja, qual é a sua semântica de falha. Considere um cliente c que envia uma requisição de serviço rs para um servidor s, através de um canal de comunicação cc, e que o atraso máximo na transmissão de rs de c para s através de cc é a. Se o projetista assume uma semântica de falha por omissão para cc, ou seja a probabilidade de cc atrasar ou corromper uma mensagem é desprezível, então pode-se usar um mecanismo simples baseado em time-outs para detectar a falha no envio de rs por c ou na resposta a rs por s. Para tolerar faltas em cc, basta que c reenvie rs, sempre que a resposta a rs não for recebida dentro de um período de 2a unidades de tempo após o envio de rs por c. Note que se a semântica de falha de cc fosse outra, mecanismos diferentes teriam que ser utilizados para tolerar possíveis faltas [Cris91]. O que se percebe da discussão acima é que a semântica de falha de um componente deve ser escolhida pelo projetista do sistema, a quem cabe julgar o que se constitui uma probabilidade de ocorrência desprezível para um dado comportamento. Em outras palavras, o projetista é responsável pela identificação de quando é justificável assumir que a probabilidade de um determinado comportamento ocorrer é tão pequena que pode-se desprezar a sua ocorrência. Portanto, a semântica de falha de um componente depende não somente das características do componente propriamente, mas também, entre outros fatores, do tempo de missão e dos requisitos de confiança no funcionamento exigidos pela aplicação que o usa. Tomando processadores como exemplo, embora se tenha conhecimento que processadores convencionais possam falhar de uma maneira arbitrária [Lala86, HLD88], a probabilidade deste tipo de falha ocorrer é tão pequena, que é possível assumir uma semântica de falha controlada para estes processadores, e ainda assim, atender aos requisitos de confiabilidade de um grande número de aplicações. Existe, entretanto, um número crescente de aplicações, cujos requisitos de confiabilidade são suficientemente altos a ponto de, para estas aplicações, não ser possível assumir que processadores convencionais apresentam semântica de falha controlada. No Seljuk a semântica de falha dos componentes de hardware (processadores e canais de comunicação) e de software é escolhida no momento da ativação da aplicação, dentro de um leque de opções que contemplam desde a semântica de falha arbitrária (menos restritiva possível) até a semântica de falha silenciosa (mais restritiva possível). Quando a semântica de falha escolhida para os componentes de software é mais restritiva do que aquela escolhida para os componentes de hardware, o ambiente operacional, desde que haja recursos suficientes, se encarrega de implementar a semântica de falha requerida para os componentes de software, através da replicação e gerência dos componentes de hardware disponíveis. Quando os recursos são insuficientes para atender ao serviço de processamento ou comunicação com a semântica de falha requerida pelo componente requisitante, o serviço é negado. 4. A ARQUITETURA DO AMBIENTE OPERACIONAL S ELJUK A execução de uma aplicação distribuída robusta no ambiente operacional Seljuk utiliza basicamente três serviços distintos: i) o serviço de processamento, que é responsável pela execução das instruções dos componentes da aplicação em nodos (possivelmente replicados); ii) o serviço de comunicação, que provê os mecanismos necessários para que dois ou mais componentes possam trocar mensagens de forma confiável; e iii) o serviço de gerência da redundância, que implementa os protocolos que garantem os requisitos de confiança no funcionamento requisitados pela aplicação. Os componentes do Seljuk podem ser acomodados em 4 camadas distintas, conforme ilustrado na Figura 1 abaixo. Aplicações Middleware Sistema Operacional Distribuído Processadores e Canais de Comunicação Figura 1: estrutura em camadas do ambiente operacional Seljuk As três camadas superiores da estrutura apresentada na Figura 1 são formadas por componentes implementados em software. Já a camada inferior é formada por componentes implementados em hardware; destes, destacaremos com maior ênfase os processadores e os canais de comunicação. Os componentes que formam as duas camadas inferiores são responsáveis pela implementação dos serviços de processamento e de comunicação. A camada intermediária denominada de middleware, por sua vez, utiliza os serviços oferecidos pelas camadas inferiores para implementar os serviços de gerência da redundância necessários para implementar os mecanismos para tolerância a faltas usados pelos componentes das aplicações. Por fim, na camada superior ficam os componentes que implementam o serviço da aplicação. A estrutura apresentada acima explicita as relações de dependência entre os principais componentes da arquitetura. Vê-se que além dos serviços normalmente oferecidos por um ambiente operacional tradicional, o ambiente operacional Seljuk oferece serviços de processamento e comunicação confiáveis, e sobre estes, serviços especializados para tolerância a faltas. Como será visto mais adiante, estes serviços são oferecidos de forma flexível e com ônus apenas para aquelas aplicações que se utilizam deles. Sistemas operacionais distribuídos baseados em micro-núcleos parecem ser o tipo de tecnologia capaz de implementar tais serviços. O princípio básico da tecnologia de micro- núcleos é minimizar o tamanho da parte do sistema operacional que executa em modo supervisor (o micro-núcleo propriamente dito) com o objetivo de aumentar a flexibilidade do sistema. Toda a funcionalidade do sistema operacional que não é provida pelo micro-núcleo fica a cargo de processos servidores que executam em modo usuário. Desta forma, os serviços providos por estes servidores podem ser modificados/adaptados/configurados muito mais facilmente, atendendo às diferentes exigências das aplicações. Esta é essencialmente uma forma de dividir, de uma maneira elegante, mecanismos de políticas. Uma quantidade mínima de mecanismos básicos é implementada pelo micro-núcleo que executa em modo supervisor, enquanto que a política de gerência dos recursos do sistema é implementada pelos servidores executando em modo usuário. Na realidade, dado a importância que a comunicação entre processos tem nestes sistemas, a maioria dos micro-núcleos distribuídos descritos na literatura [CZ83, MRTRS90, Rozi92], oferece um serviço de comunicação com algum grau de confiabilidade. No entanto, serviços de processamento confiáveis são praticamente inexistentes. Nossa abordagem, além de introduzir estes serviços, agrega novos serviços para tolerância a faltas àqueles tradicionalmente fornecidos por micro-núcleos distribuídos, de forma a possibilitar a concepção de plataformas de desenvolvimento que ofereçam os serviços mencionados acima. 4.1. Serviço de Processamento Confiável Os micro-núcleos distribuídos existentes praticamente não possuem serviços de processamento confiável, ou seja, o processamento nestes sistemas é, no máximo, tão confiável quanto os nodos nos quais ele é executado. Os protocolos desenvolvidos em [Bras95] podem ser usados para implementar a execução replicada de processos, e assim prover um serviço de processamento confiável para os componentes das aplicações que requisitarem tal serviço. Em [Bras95], os protocolos são implementados por uma coleção de funções pertencentes a uma biblioteca, que são ligadas ao código da aplicação. A aplicação deve então ser executada no número de processadores necessários para garantir o grau de confiança no funcionamento requerido. Grande parte da gerência dos mecanismos para tolerância a faltas está a cargo do programador, o que, além de ser trabalhoso, é susceptível à introdução de faltas de projeto. A arquitetura Seljuk elimina estes problemas introduzindo no micro-núcleo de um sistema operacional distribuído os mecanismos que tornam o serviço de processamento confiável transparente para as aplicações. O processamento confiável no Seljuk é obtido através de replicação ativa dos componentes da aplicação em processadores independentes; cada processador do nodo replicado executa uma coleção de threads do núcleo que gerem a redundância do nodo (detalhes são apresentados em [Bras95] e [GB97]). Entre as operações executadas por estas threads podemos destacar a ordenação das mensagens que são entregues às réplicas dos componentes da aplicação, que garante que todas as réplicas corretas processem as mesmas mensagens na mesma ordem, e a validação das mensagens produzidas pelas réplicas, que em última instância vai definir a semântica de falha do nodo. A ordenação das mensagens entregues às réplicas por si só não garante o correto funcionamento da replicação ativa. O potencial comportamento não determinístico das réplicas pode levar à divergência de estados entre as várias réplicas de um determinado componente. É preciso, portanto, evitar, ou pelo menos controlar, possíveis comportamentos não determinísticos das réplicas. O comportamento não determinístico de uma réplica pode ser conseqüência de duas situações: i) não determinismo de componentes do sistema operacional (ex. tratamento de eventos assíncronos); e ii) processamento não deterministico da aplicação (ex. cálculo feito a partir dos dados lidos de um sensor). O comportamento não determinístico do sistema operacional pode ser decorrente de uma chamada síncrona ao sistema que tem um comportamento não determinístico (ex. uma chamada do tipo gettimeofday() que retorna a hora do sistema no momento da sua ativação) e portanto pode retornar valores diferentes para as várias réplicas de um componente; ou de um evento assíncrono (ex. fim de um time-out, recepção de um sinal, etc.) que pode ser tratado em diferentes pontos da execução das réplicas e em conseqüência produzir diferentes resultados. Estes problemas são resolvidos no Seljuk da seguinte forma. Todas as chamadas síncronas ao sistema que tenham comportamento não determinístico devem ser modificadas, de forma que antes de retornar o valor calculado à aplicação, primeiro o valor calculado é enviado às outras réplicas do componente através de mensagens; estas mensagens serão ordenadas normalmente pelo mecanismo de ordenação que implementa o nodo replicado, e desta forma apenas um dos valores calculados será utilizado por todas as réplicas (aquele calculado pela réplica que conseguiu ordenar seu valor em primeiro lugar); os outros valores calculados serão descartados como duplicatas de uma mensagem já recebida pelo nodo. Note que um mecanismo similar pode ser usado pelas componentes das aplicações que tenham um comportamento não determinístico, permitindo que estes possam ser replicados ativamente, e portanto possam utilizar todos os serviços oferecidos pelo Seljuk, sem quaisquer restrições. No caso dos eventos assíncronos, basta transformar estes eventos em mensagens especiais, e enviar estas mensagens para as outras réplicas do nodo. Os handlers destes eventos são implementados na forma de threads que bloqueiam a espera de uma mensagem sinalizando o evento correspondente. Novamente, o mecanismo de ordenação do nodo garante que os mesmos eventos serão tratados no mesmo ponto de execução por todas as réplicas. Um seqüenciador de eventos é usado para possibilitar o descarte de eventos duplicados, já tratados. 4.2. Serviço de Comunicação Confiável Do ponto de vista da comunicação, existem basicamente três tipos de falhas possíveis: perda ou atraso de mensagens; duplicação de mensagens; e corrupção do conteúdo das mensagens. Muitos sistemas oferecem um serviço de comunicação sujeito a todos os tipos de falhas citados acima. Nestes sistemas, aplicações com requisitos de confiança no funcionamento devem implementar protocolos que garantam que estas falhas sejam toleradas. Outros sistemas oferecem serviços de comunicação um pouco mais confiáveis, com detecção de duplicatas e de mensagens corrompidas, sobre o qual outros serviços de comunicação mais sofisticados podem ser mais facilmente implementados. Entre estes serviços mais sofisticados destacam-se as chamadas de procedimento remotos (RPC) [BN84] e os serviços de comunicação em grupo [BJ87]. Um outro fator que aumenta a complexidade dos serviços de comunicação diz respeito à ordem na qual as mensagens recebidas por um nodo são entregues às aplicações executando naquele nodo. O serviço de ordenação mais simples de ser implementado é aquele que garante ordenação fifo (first-in-first-out) das mensagens trocadas por um par de processos. Outros serviços de comunicação que atendem requisitos de ordenação de mensagens mais sofisticados incluem os serviços de disseminação com preservação de causalidade (causal and total order multicast) [BJ87] e os serviços de disseminação atômica (atomic broadcast) [BES95a, BE95]. A arquitetura Seljuk prevê a disponibilização de todos os serviços discutidos acima. A oferta de uma grande quantidade de serviços de comunicação, com variado grau de confiabilidade, proporciona uma maior flexibilidade para o desenvolvedor, deixando a cargo deste escolher o serviço mais apropriado, levando em consideração fatores como o desempenho e a redução da complexidade de desenvolvimento alcançados, quando um determinado serviço é utilizado. 4.3. Serviços para Tolerância a Faltas Existe um considerável número de protocolos desenvolvidos com o intuito de possibilitar a implementação de serviços para tolerância a faltas. A principal diferença entre estes protocolos reside nas considerações feitas sobre a semântica de falha de nodos e canais de comunicação, e no grau de confiabilidade que se deseja alcançar. Tolerância a faltas a nível de processamento é normalmente alcançada através da replicação do processamento em um número N de nodos, onde N é uma função do número f de falhas que se quer tolerar e da semântica de falha dos nodos. Há basicamente dois tipos de replicação: ativa e passiva [Powe92]. Na replicação ativa, os processos que implementam a aplicação distribuída são executados simultaneamente em todos os N nodos. Se os nodos possuem semântica de falha segura, é necessário apenas que N seja maior que f, para que f faltas sejam toleradas; por outro lado, se os nodos possuem semântica de falha arbitrária, então N deve ser pelo menos maior que 2f (assumindo que os nodos são capazes de assinar mensagens). No primeiro caso, o resultado da computação será o primeiro resultado produzido por qualquer uma das réplicas; já no segundo caso, faz-se necessário submeter os resultados produzidos pelas réplicas a uma votação majoritária, que mascare os resultados produzidos por replicas executando em nodos que falharem. Para que a replicação ativa funcione é fundamental que as réplicas executem a mesma seqüência de comandos, evitando assim divergência nos seus estados internos. Para tal, as réplicas devem executar um algoritmo determinista, e devem receber a mesma seqüência de mensagens de entrada. Na replicação passiva, apenas uma réplica dos processos, a primária, executa. As outras réplicas, as secundárias, simplesmente mantêm informações sobre o progresso da réplica primária. De tempos em tempos a réplica primária envia informações (checkpoints) às réplicas secundárias, informando sobre o seu progresso. Se a réplica primária falha, e portanto deixa de enviar um checkpoint, uma nova réplica primária é escolhida entre as réplicas secundárias e o processamento é continuado com base nas informações recebidas no último checkpoint. Replicação passiva só é possível se os nodos apresentam semântica de falha segura e, neste caso, para se tolerar f faltas, é suficiente que N seja maior que f. Como só uma réplica executa em cada instante, não há necessidade dos algoritmos dos processos que implementam a aplicação serem deterministas. Outros serviços importantes são o diagnóstico e a recuperação de nodos que falham. O diagnóstico destes nodos é feito dentro do contexto de um grupo de nodos que se relacionam de alguma forma (ex. implementam um serviço de forma conjunta). Um serviço de diagnóstico completo deve permitir que todos os nodos que falharam no grupo sejam diagnosticados e que todos os nodos corretos do grupo concordem sobre quais nodos falharam. Quando a semântica de falha dos nodos é segura, este diagnóstico pode ser alcançado de maneira razoavelmente simples. Por outro lado, se os nodos podem falhar de forma arbitrária, não se conhece nenhum protocolo que possa garantir um diagnóstico completo. Nestes casos, entretanto, é possível se implementar um serviço de diagnóstico parcial [SR87]. Nodos cuja falha tenha sido diagnosticada devem ser isolados ou substituídos por nodos corretos. A exclusão dos nodos que falham durante o tempo de missão de uma aplicação vai, paulatinamente, degradando os serviços para tolerância a faltas usados pela aplicação. Quando o tempo de missão da aplicação não pode ser predeterminado ou é muito longo, fazse necessário reconfigurar o sistema de tempos em tempos, introduzindo outros nodos que possam assumir a carga de processamento dos nodos que falharam e, desta forma renovar o nível de tolerância a faltas dos serviços. Os nodos adicionados podem ser nodos suplentes ou os próprios nodos que falharam, desde que seja possível recuperá-los e colocá-los novamente em operação. No Seljuk, os serviços apresentados acima, são colocados à disposição das aplicações na forma de chamadas ao sistema, de processos servidores, e de forma transparente como os serviço de processamento discutido anteriormente (na próxima seção daremos mais detalhes sobre a forma como estes serviços são oferecidos em uma plataforma específica). 5. SELJUK-AMOEBA Dentre os micro-núcleos apresentados na literatura, o Amoeba [MRTRS90], destaca-se pela sua penetração na comunidade científica e pela grande quantidade de informação disponível. Por estes motivos, o Amoeba foi escolhido como sistema hospedeiro para a nossa primeira implementação de um ambiente operacional Seljuk, o qual chamamos de SeljukAmoeba. Os detalhes da especificação deste ambiente são relatados em [VB97], [GB97], [CB97] e [FB97]; aqui nos limitaremos a apresentar um resumo dos serviços oferecidos pelo ambiente. 5.1. Serviços de Processamento no Seljuk-Amoeba O serviço de processamento confiável do Seljuk-Amoeba é oferecido por um servidor chamado de FT Run Server. Para usar o serviço, primeiro um processo faz uma RPC com o FT Run Server. Um dos parâmetros contidos nesta chamada é a semântica de falha do nodo. Ao disparar a aplicação, pode-se escolher qual a semântica de falha assumida para o nodo onde ela executará; as semânticas de falha possíveis são a semântica de falha silenciosa e a semântica de falha mascarada. O fator de replicação também é passado pelo processo. Este fator é um valor numérico que informa ao FT Run Server quantos processadores independentes formarão o nodo. Um outro parâmetro fornecido pelo processo, é uma lista contendo quais os processadores que não se deseja utilizar na execução daquela aplicação (este parâmetro pode ser utilizado, por exemplo, se o projetista da aplicação achar que o processador de uma determinada arquitetura não é confiável, e não desejar submeter nenhuma tarefa a este; pode acontecer também, do processo disparar duas aplicações e necessitar que estas executem em conjuntos de processadores distintos, por exemplo, para implementar uma replicação de mais alto nível [VB97]). Dois outros parâmetros passados na RPC são um objeto contendo o código a ser executado e a semântica de falha que deve ser assumida para os processadores do sistemas. Finalmente, um último parâmetro a ser passado na RPC é uma variável que sinaliza se a aplicação será executada com diversidade de projeto ou não. Com isto, pode-se conseguir tolerância a faltas de projeto de software, já que cada réplica disparada pelo processo executará uma implementação diferente da mesma aplicação. Detalhes sobre as ações que são executados pelo Seljuk-Amoeba quando um processo utiliza o serviço do FT Run Server podem ser vistos em [GB97]. No Amoeba pode-se ter processadores heterogêneos. Por este motivo, é possível obterse tolerância a faltas a nível de projeto de hardware quando o mesmo processo é replicado em vários processadores de arquiteturas diferentes. Para permitir também tolerância a faltas de projeto de software, a organização dos binários no Amoeba foi adaptada da maneira mostrada na Figura 2 abaixo. /bin pd.i80386 versão1 sort dir i80386 pd.VAX versão2 versão3 Figura 2: Sistema de arquivos do Seljuk-Amoeba Em sua versão original, o Amoeba só trata de executáveis para arquiteturas diferentes. O Seljuk-Amoeba manipula também várias versões de um mesmo programa para várias arquiteturas. A Figura 2 mostra um exemplo da organização atual do diretório onde se encontram os executáveis no Seljuk-Amoeba (/bin). Nesta figura, o /bin contém dois comandos: dir e sort. O comando dir está disponível apenas para a arquitetura VAX; o comando sort por sua vez, encontra-se disponível na arquitetura 80386 além de conter outras n implementações dele disponíveis para a mesma arquitetura. Esta diversidade de implementações do mesmo programa, permite que o projetista opte pela execução da aplicação em nodos replicados, de forma que cada réplica do nodo execute uma versão diferente do programa. Note que com a diversidade de arquiteturas de processadores e com a disponibilidade de n versões para cada uma destas arquiteturas, aumenta-se o grau de tolerância a faltas para esta aplicação. 5.2. Serviços de Comunicação no Seljuk-Amoeba O sistema de comunicação do Amoeba é implementado no núcleo em duas camadas. A camada inferior implementa o Fast Local Internet Protocol (FLIP), que é um protocolo que opera em modo datagrama, não orientado à conexão. Comunicação confiável é fornecida pelo Amoeba pelos dois serviços da camada superior - comunicação em grupo e RPC (Remote Procedure Call), que utilizam o serviço não confiável fornecido pelo FLIP para enviar mensagens. Portanto, o Amoeba já oferece primitivas para comunicação um-a-um confiável, além de primitivas para comunicação em grupo que provêem entrega atômica de mensagens a todos os membros do grupo, mesmo na presença de falhas dos canais de comunicação e, opcionalmente, dos processadores. Ou seja, a comunicação em grupo do Amoeba garante entrega confiável e ordenada de mensagens a todos os membros corretos do grupo. Alguns serviços do Seljuk-Amoeba, entretanto, necessitam de serviços de comunicação em grupo que não são atendidos pelo Amoeba, fazendo-se necessário incrementar o conjunto de primitivas oferecidas. As primitivas a serem acrescentadas pelo Seljuk-Amoeba deverão ser flexíveis, permitindo que se possa balancear desempenho contra requisitos de tolerância a faltas. Três novas primitivas que podem ser ativadas por um processo que não faz parte de um grupo são necessárias: CreateRepGroup(), que permite a criação de um grupo de réplicas; JoinRepGroup() que permite que novos membros sejam adicionados a um grupo criado por CreateRepGroup(); e ResetRepGroup(), que permite a inicialização do processo de recuperação do grupo. 5.3. Serviços para Tolerância a Faltas no Seljuk-Amoeba O serviço de replicação de processamento no Seljuk-Amoeba é oferecido por um servidor de replicação - o RepServer - implementado na camada middleware da arquitetura Seljuk. A fim de criar um componente de software replicado, a seguinte invocação é feita ao RepServer, através de uma RPC: Replicate(file, rep-degree, failure-semantics) Esta chamada solicita ao RepServer a criação de um processo replicado para executar o código contido em file. O grau de replicação (isto é, o número de réplicas a serem criadas) é determinado por rep-degree e a semântica de falha real considerada para os processadores do sistema é definida em failure-semantics. Se a semântica de falha definida para os processadores é menos restritiva do que a semântica de falha silenciosa (que é a exigida por esta implementação do RepServer), ao receber tal pedido, o RepServer deve requisitar ao FT Run Server, também via RPC, a criação de rep-degree nodos com semântica de falha silenciosa. Além da primitiva para criação de um grupo de réplicas, o Seljuk-Amoeba disponibiliza uma biblioteca de funções que podem ser usadas para facilitar a implementação de aplicações replicadas de forma passiva. Essas funções executam tarefas relacionadas com a eleição de réplicas primárias, manutenção de checkpoints e controle de logs de mensagens recebidas e enviadas pelas réplicas [VB97]. Finalmente, os mecanismos de detecção e reconfiguração do sistema são definidos pela aplicação no momento da sua ativação e são executados de forma transparente pelo ambiente operacional, utilizando as primitivas adicionais de manipulação de grupos (CreateRepGroup(); JoinRepGroup() e ResetRepGroup()). 6. CONCLUSÕES E TRABALHOS FUTUROS Construir aplicações distribuídas capazes de tolerar a ocorrência de faltas é uma tarefa difícil. Acreditamos que a plataforma de desenvolvimento proposta neste projeto possibilitará uma sensível redução na complexidade de desenvolvimento destas aplicações. Este sentimento se baseia nas seguintes considerações: i) as aplicações poderão escolher a semântica de falha dos nodos onde executarão sem a preocupação de implementar qualquer mecanismo que garanta esta semântica - isso será feito de forma transparente pela plataforma. Além disso, já que esta escolha é feita em tempo de ativação, este serviço também oferece uma maior flexibilidade para as aplicações, que podem a qualquer momento mudar as considerações feitas sobre a semântica de falha dos nodos, e/ou dos processadores do sistema, sem que seja necessário qualquer modificação no código executável da aplicação; e ii) a plataforma oferece um número de serviços para tolerância a faltas que poderão ser usados de várias formas no desenvolvimento de novas aplicações, facilitando a implementação destas. Poucos sistemas tolerantes a faltas descritos na literatura têm a habilidade de tolerar faltas arbitrárias de seus componentes. Os que existem são normalmente voltados à solução de problemas bastante específicos. A maioria dos sistemas tolerantes a faltas assume uma semântica de falha bastante restritiva para os seus componentes (principalmente unidades de processamento). A plataforma descrita neste artigo implementa nodos com diversas semânticas de falha controlada. Tais nodos são implementados a partir dos processadores disponíveis no sistema, cuja semântica de falha é especificada por cada aplicação, quando esta é ativada. Portanto, a plataforma é capaz de tolerar falhas arbitrárias nos processadores. Todos os serviços para tolerância a faltas oferecidos pela plataforma, são implementados inteiramente em software, sem a necessidade de qualquer componente de hardware especial. Esta característica possibilita a utilização de diversidade de projeto, tanto no hardware, quanto no software, para tolerância a faltas de projeto do hardware e do software. Finalmente, embora diversos protocolos tenham sido desenvolvidos para possibilitar a construção de uma variedade de mecanismos para tolerância a faltas em sistemas distribuídos, a maneira de integrar estes mecanismos no sentido de garantir o provimento dos requisitos de confiança no funcionamento de aplicações distribuídas ainda não é clara. A flexibilidade oferecida pela plataforma de desenvolvimento Seljuk será útil na avaliação dos custos desta integração. Atualmente estamos iniciando a implementação do ambiente Seljuk-Amoeba. No futuro pretendemos desenvolver várias aplicações distribuídas com diferentes requisitos de confiabilidade, para podermos avaliar melhor o potencial, bem como as limitações, do ambiente. Agradecimentos O autor agradece o apoio financeiro do CNPq (processo 300.646/96-8). Referências [Aviz85] A. Avizienis, “The N-Version Approach to Fault Tolerant Software,” IEEE Transaction on Software Engineering, Vol. 11, N. 12, pp. 1491-1501, December 1985. [Bart81] J.F. Bartlett, “A NonStop Kernel,” ACM 8th Symposium on Operating Systems Principles, Pacific Grove, USA, Vol. 15, No. 5, pp. 22-29, December 1981. [Bern88] P.A. Bernstein, “Sequoia: A Fault-Tolerant Tightly Coupled Multiprocessor for Transaction Processing,” IEEE Computer, Vol. 21, No. 2, pp. 37-45, February 1988. [BJ87] K.P. Birman and T.A. Joseph, “Reliable Communication in the Presence of Failures,” ACM Transactions on Computer Systems, Vol. 5, No. 1, pp. 47-76, February 1987. [BN84] A.D. Birrell and B.J. Nelson, “Implementing Remote Procedure Calls,” ACM Transactions on Computer Systems, Vol. 2, N. 1, pp. 39-59, February 84. [Bras95] F.V. Brasileiro, “A Software Approach to the Construction of Fail-Controlled Nodes for Distributed Systems,” Anais do VI Simpósio de Computadores Tolerantes a Falhas, Canela, Brasil, pp. 385-404, agosto de 1995. [BES95a] F.V. Brasileiro, P.D. Ezhilchelvan, and N.A. Speirs, “Authenticated Agreement Protocols without Explicit Clock Synchronisation,” Proceedings of the European Research Seminar on Advances in Distributed Systems, France, pp. 151-157, April 1995. [BES95b] F.V. Brasileiro, P.D. Ezhilchelvan, and N.A. Speirs, “TMR Processing without Explicit Clock Synchronisation,” Proceedings of the 14th Symposium on Reliable Distributed Systems, Germany, pp. 186-195, September 1995. [BE95] F.V. Brasileiro e P.D. Ezhilchelvan, “Atomic Broadcast Using Time-outs instead of Synchronised Time,” Anais do VI Simpósio de Computadores Tolerantes a Falhas, Canela, Brasil, pp. 223-238, agosto de 1995. [BESST96] F.V. Brasileiro, P.D. Ezhilchelvan, S.K. Shrivastava, N.A. Speirs, and S. Tao, “Efficient Protocols for Fail-Silent Nodes in Distributed Systems,” IEEE Transactions on Computers, Vol. 45, N. 11, pp. 1226-1238, November 1996. [CB97] V.S. Catão e F.V. Brasileiro, “Serviço de Comunicação Síncrona para Nodos Replicados,” Anais do VII Simpósio de Computadores Tolerantes a Falhas, Campina Grande, Brasil, julho de 1997. [CZ83] D.R. Cheriton and W. Zwaenpoel, “The Distributed V Kernel and its Performance for Diskless Workstations,” Operating Systems Review, Vol. 17, N. 5, pp. 129-140, May 1983. [Cris91] F. Cristian, “Understanding Fault-Tolerant Distributed Systems,” Communications of the ACM, Vol. 34, N. 2, pp. 56-78, February 1991. [FB97] R.M. Faria e F.V. Brasileiro, “Serviço de Assinatura Digital para Nodos Replicados,” submetido ao VII Simpósio de Computadores Tolerantes a Falhas, fevereiro de 1997. [Flan96] D. Flanagan, Java in a Nutshell, O’Reilley&Associates, Inc., USA, 1996, ISBN 1-56592183-6. [GB97] E.L. Gallindo e F.V. Brasileiro, “Processamento Confiável no Ambiente Operacional Seljuk-Amoeba,” Anais do VII Simpósio de Computadores Tolerantes a Falhas, Campina Grande, Brasil, julho de 1997. [HLD88] R.E. Harper, J.H. Lala, and J.J. Deyst, “Fault Tolerant Processor Architecture Overview,” Digest of Papers, FTCS-18, Tokyo, Japan, pp. 252-257, June 1988. [HSL78] A.L. Hopkins, T.B. Smith, and J.H. Lala, “FTMP - A Highly Reliable Fault-Tolerant Multiprocessor for Aircraft,” Proceedings of the IEEE, Vol. 66, No. 10, pp. 1221-1239, October 1978. [HK93] Y. Huang and C. Kintala, “Software Implemented Fault Tolerance: Technologies and Experience,” Digest of Papers, FTCS-23, Toulose, France, pp. 2-9, 1993. [KM85] H. Kopetz, and W. Merker., “The Architecture of MARS,” Digest of Papers, FTCS-15, Ann Arbor, USA, pp. 274-279, June 1985. [Lala86] J.H. Lala, “A Byzantine Resilient Fault Tolerant Computer for Nuclear Power Plant Applications,” Digest of papers, FTCS-16, Vienna, Austria, pp. 338-343, July 1986. [Lapr89] J-C. Laprie, “Dependability: a Unifying Concept for Reliable Computing and Fault Tolerance,” in Dependability of Resilient Computers, T. Anderson (Ed.), BSP Professional Books, 1989, ISBN 0-632-02054-7. [LV91] R. de Lemos e Paulo Veríssimo, “Confiança no Funcionamento - Proposta para uma Terminologia em Português,” comunicação pessoal, dezembro de 1991. [LC95] M.L.B. Lisbôa e G.G.H. Cavalheiro, “Reflexão Computacional sobre Técnicas de Tolerância a Falhas em Software,” Anais do VI Simpósio de Computadores Tolerantes a Falhas, Canela, RS, pp. 405-416, agôsto de 1995. [Maes87] P. Maes, “Concepts and Experiments in Computational Reflexion,” Proceedings of OOPLAS’87, ACM SIGPLAN Notices, Vol. 22, N. 12, pp. 147-155, October 1987. [MRTRS90] S.J. Mullender, G. van Rossum, A.S. Tanembaum, R. van Renesse, and H. van Staveren, “Amoeba: A Distributed Operating System for the 1990's,” IEEE Computer, Vol. 23, No. 5, pp. 44-53, May 1990. [Ng90] T.P. Ng, “The Design and Implementation of a Reliable Distributed Operating System ROSE,” Proceedings of IEEE ICDCS, pp. 2-11, 1990. [Powe92] D. Powell (Ed.), Delta-4 - A Generic Architecture for Dependable Distributed Computing, Spring-Verlag, 1992, ISBN 3-540-54985-4. [Rand75] B. Randell, “System Structure for Software Fault Tolerance,” IEEE Transactions on Software Engineering, Vol. 1, N. 2, pp. 220-232, June 1975. [RS93] J. Reisinger, and A. Steininger, “The Design of a Fail-Silent Processing Node for the Predictable Hard Real-Time System MARS,” IEE Distributed System Engineering, Vol. 1, No. 2, pp. 104-111, December 1993. [Rozi92] M. Rozier, “Chorus,” Proceedings of USENIX Workshop on Microkernels and other Kernel Architectures, USENIX Association, 1992. [Schn84] F.B. Schneider, “Byzantine Generals in Action: Implementing Fail-Stop Processors,” ACM Transactions on Computer Systems, Vol. 2, No. 2, pp. 145-154, May 1984. [SR87] K.G. Shin, and P. Ramanathan, “Diagnosis of Processors with Byzantine Faults in a Distributed Computing System,” Digest of Papers, FTCS-17, Pittsburgh, USA, pp. 5560, June 1987. [SDP91] S.K. Shrivastava, G.N. Dixon and G.D. Parrington, “An Overview of the Arjuna: A Proggraming System for Reliable Distributed Computing,” IEEE Software, Vol. 8, No. 1. Pp. 63-73, January 1991. [SESTT92] S.K. Shrivastava, P.D. Ezhilchelvan, N.A. Speirs, S. Tao, and A. Tully, “Principal Features of the VOLTAN Family of Reliable Node Architectures for Distributed Systems,” IEEE Transactions on Computers, Vol. 41, No. 5, pp. 452-549, May 1992. [S*78] D. Siewiorek et al., “A Case Study of C.mmp, Cm*, and C.vmp: Part I - Experiences with Fault Tolerance in Multiprocessor Systems,” Proceedings of the IEEE, Vol. 66, No. 10, pp. 1178-1199, October 1978. [SS92] D.P. Siewiorek, and R.S. Swarz, Reliable Computer Systems: Design and Evaluation (second edition), Digital Press, USA, 1992, ISBN 0-13-772021-1. [STBES93] N.A. Speirs, S. Tao, F.V. Brasileiro, P.D. Ezhilchelvan and S.K. Shrivastava, “The Design and Implementation of VOLTAN Fault-Tolerant Nodes for Distributed Systems,” Transputer Communications, Vol. 1, No. 2, pp. 93-109, November 1993. [Theu86] N. Theuretzbacher, “`VOTRICS': Voting Triple Modular Computing System,” Digest of Papers, FTCS-16, Vienna, Austria, pp. 144-150, July 1986. [VB97] S.R.A. Vasconcelos e F.V. Brasileiro, “Serviços para Tolerância a Faltas no Ambiente Operacional Seljuk-Amoeba,” Anais do VII Simpósio de Computadores Tolerantes a Falhas, Campina Grande, Brasil, fevereiro de 1997. [WB91] S. Webber, and J. Beirne, “The Stratus Architecture,” Digest of Papers, FTCS-21, Montréal, Canada, pp. 79-85, June 1991. [W*78] J.H. Wensley et al., “SIFT: Design and Analysis of a Fault-Tolerant Computer for Aircraft Control,” Proceedings of the IEEE, Vol. 66, No. 10, pp. 1240-1255, October 1978.
Documentos relacionados
Processamento Confiável no Amoeba
desta cresce. De fato, um dos principais problemas no projeto e implementação de aplicações distribuídas, reside justamente na dificuldade introduzida pela necessidade de se tratar e tolerar falhas...
Leia mais