exclusão mútua distribuídaendler/courses/da/transp/mexclus.pdf · 2018-04-09 · © markus...

56
© Markus Endler 1 Exclusão Mútua Distribuída Principais Referências: • Livro Kshemkalyani/ Singhal: Cap 9 (algumas seções) • Livro: Chow, Johnson, Cap. 10 • Livro: M.Raynal: Distributed Algorithms and Protocols, Cap. 2 •Velazquez, M., A Survey of Distributed Mutual Exclusion Algorithms, Technical Report CS-93-116, Colorado State University, 1993

Upload: others

Post on 27-Jun-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

© Markus Endler 1

Exclusão Mútua Distribuída

Principais Referências: •  Livro Kshemkalyani/ Singhal: Cap 9 (algumas seções) •  Livro: Chow, Johnson, Cap. 10 •  Livro: M.Raynal: Distributed Algorithms and Protocols, Cap. 2 • Velazquez, M., A Survey of Distributed Mutual Exclusion Algorithms, Technical Report CS-93-116, Colorado State University, 1993

© Markus Endler 2

Observações sobre Notação A maioria dos Algoritmos serão descritos em pseudo-código, onde: •  a variável “me” (ou equivalente) contém o índice do processo corrente e não pode ser alterada •  as demais variáveis são globais no processo •  não há compartilhamento de variáveis entre processos, mas estes podem ter várias threads que compartilham as variáveis globais •  cada thread será composta de “clausulas reativas” da forma:

[when] Evento [&& Condição] => { Ações } Onde:

Evento: pode ser de controle (init/start/stop), chamada da aplicação, chegada de mensagem (recv()), ou de temporização (timeout T); Condição: sobre as variáveis que habilita/desabilita uma clausula; Ações: sequência de instruções executadas atomicamente; incluindo operações locais, send(), ou set-timer T

•  assume-se que referências para os processos pares já são conhecidos (topologia de interconexão pré-estabelecida) •  assume-se a existência de procedimentos auxiliares (operações sobre filas, pilhas, etc.)

© Markus Endler 3

Exclusão Mútua Distribuída Usado para sincronizar/coordenar as ações de processos distribuídos em um sistema de forma descentralizada. Exemplos:

•  garantir o acesso exclusivo a dados/arquivos compartilhados •  garantir que ações sobre o ambiente externo sejam coordenadas (p.ex. robôs cooperativos) •  garantir a consistência de páginas em uma Memória compartilhada distribuída (DSM)

Exclusão Mútua Distribuída: •  consiste em garantir o acesso exclusivo de um processo (dentre vários com requisições concorrentes) a uma seção crítica (para acesso a um recurso/ dado/serviço compartilhado).

© Markus Endler 4

Exclusão Mútua Distribuída A principal diferença do problema para Sist.Distribuidos, e Sistemas centralizados (p.ex.: sincronização em um Sist.Operacional) è não há o compartilhamento de memória; toda sincronização/coordenacão precisa ser feita através do envio de mensagens Exclusão Mútua é caso específico de um Problema de Acordo (Agreement) è Todos os processos concordam sobre a vez de entrar na

seção crítica è Coordenam a atualizacão de uma varável local

booleana: posso_entrar_SC = TRUE/FALSE è Não necessariamente os processos sabem qual outro

processo está na SC

© Markus Endler 5

Exclusão Mútua Distribuída: Definição do Problema Sejam N processos Pi, i =1,..,N que interagem através de mensagens e devem sincronizar os seus acessos exclusivos a um recurso compartilhado (pode ser trivialmente generalizado para >1 recurso): No código de cada Pi:

enter(); // entrada na seção crítica acesso exclusivo ao recurso compartilhado exit(); // saida da seção crítica

Para a exclusão mútua, não é viável lidar com falhas de nós! è Pois todos os processos precisam chegar a um acordo sobre a entrada de um processo na seção crítica. As Premissas mais comuns: •  processos não falham •  comunicação é confiável e •  mensagens não são duplicadas ou corrompidas •  Não há premissas sobre temporização (è sistemas assíncronos)

© Markus Endler 6

Requisitos Essenciais e Opcionais Segurança (Safety) <essencial>

•  No máximo, um processo está na seção crítica a cada momento Vivacidade (Liveness) <essencial>

•  Todo pedido de entrada e/ou saida da seção crítica é atendido em algum momento

è com isto, evita-se deadlock e starvation Atendimento em Ordem: <opcional>

•  Os pedidos para entrar na seção crítica são atendidos em conformidade com a ordem causal.

è isto garante tratamento justo (Fairness)

© Markus Endler 7

Exclusão Mútua Distribuída

Existem três abordagens:

•  Algoritmos baseados em token

•  Algorítmos baseados em timestamps

•  Algoritmos baseados em votação

© Markus Endler 8

Algoritmos baseados em Token

Ideia geral: •  uma mensagem especial (token) é passada entre os processos •  o processo que possuir o token é aquele com direito de executar na seção crítica •  ao deixar a seção crítica, o processo detentor do token deve repassa-lo a um outro processo que esteja esperando entrar na seção

“Traduzindo” os principais requisitos essenciais: •  deve existir um único token (Segurança) •  o token deve ser capaz de chegar a qualquer processo participando da sincronização (Vivacidade)

Para isto, todos os algoritmos nesta abordagem definem uma estrutura lógica de comunicação para passagem do token. As estruturas mais comuns:

• anel • arvore

© Markus Endler 9

Algoritmo de Token Circulante Neste caso, a estrutura lógica é um anel conectando todos os processos participantes da sincronização. Modelo de sistema (Premissas): •  execução assíncrona •  topologia de interconexão é fixa (anel), número de processos é arbitrário,

mas é fixo •  comunicação é confiável (token não é perdido) •  comunicação é segura (token não é duplicado nem modificado) •  processos não falham Cada processo que deseja entrar na seção crítica:

•  espera a chegada do token do antecessor (vizinho à esq/dir), •  durante a sessão crítica, mantém o token •  após sair da seção crítica passa o token para o próximo processo (vizinho do qual não recebeu o token)

Processos que não precisam entrar na SC, passam o token adiante imediatamente Nota-se que satisfeitas as premissas, este algoritmo garante: segurança, vivacidade, mas não garante atendimento em ordem è Algoritmo tem simetria textual, mas o envio do token é um overhead independente da freqüência de requisições para a SC

© Markus Endler 10

Algoritmo do Token Circulante

•  O controle do repasse do token é feito por um processo, que recebe requisições enter_region e exit_region da aplicação (naquele nó), e tokens circulantes do processo predecessor prev no anel.

Bool waiting; when recv(appl, enter_region) & ¬ waiting => waiting:= true; when recv(prev, token) => {

if (waiting) { reply(appl, enter_region); recv(appl, exit_region); waiting:= false; } send(next, token);

}

Proc(i) prev next

enter_region exit_region

appl Estados: waiting e ¬ waiting

Pseudo-código de Proc(i):

© Markus Endler 11

Algoritmo de Token Circulante

Mas e se houver a possibilidade de perda do token, enquanto os processos não falham. P.ex. Por falha no meio de transmissão (i.e. wireless) Seria necessário que os processos pudessem detectar a perda. Todos os processos, ou um único processo. Como?

© Markus Endler 12

Algoritmo de Token Circulante

Idéia central do Algoritmo de [Misra83] : •  além do token principal t1 (usado para controlar o acesso à SC), usar um token circulante complementar t2, de valor oposto a t1 (isto é, t2 = - t1) •  o token t2 circula no mesmo sentido que token t1 •  a cada vez que os tokens se encontram em um processo, seus valores são atualizados (p.ex. t1++ e t2-- ) •  se um processo P receber qualquer um dos tokens com o mesmo valor mais de uma vez è isto significa que o outro token se perdeu •  o token perdido pode ser reconstruído a partir do token complementar •  o 1º processo capaz de fazer esta detecção é o processo que deveria ter recebido o token perdido (este será o responável por recriar o token perdido)

[Misra83] Misra, J. Detecting Termination of Distributed Computations using Markers, 2nd ACM Conf. On Principles of Distributed Computing, August 83, pp.290-294.

© Markus Endler 13

Algoritmo de Token Circulante

Seja mi uma variável em Pi que guarda o último valor de um token (t1 ou t2) visto. O atributo t.val armazena o contador associado ao token when recv (i - 1, t1) {

if (mi == t1.val) { // t2 foi perdido mi = ++(t1.val); new(t2); // recriando t2 t2.val = - t1.val; send(i+1, t2} else mi = t1.val; if (waiting) then (como no algoritmo original) else send(i+1, t1);

} when recv (i -1, t2) {

// análogo ao caso anterior, trocando t2 por t1 e vice-versa } when encontro (t1, t2) {

t1.val++; t2.val--;

}

© Markus Endler 14

Premissas & Problemas Modelo de sistema do algoritmo de [Misra83] : •  sistema assíncrono •  canais de comunicação são seguros •  processos não falham •  canal de comunicação tem falhas de omissão: perdas de mensagem são esporádicas (mas não há partição no anel) e •  não ocorrem falhas simultâneas em mais de um canal (ou seja, no máximo um token é perdido, ou seja f=1)

Características: •  Simetria de papéis do algoritmo

Problemas: •  Overhead: além do token principal, t1, um segundo token sempre precisa circular •  Para evitar falsa detecção de perda de token, os valores absolutos assumidos por t1 e t2 precisam ser diferentes de todos os valores mi, è seus incrementos precisam ser no mínimo mod N+1 (pois tokens podem se encontrar até N vezes)

© Markus Endler 16

Generalização do algoritmo de Misra

•  Generalização para k > 2 perdas de mensagens simultâneas.

•  Dica: usar k+1 tokens circulantes (token T0 controla a entrada na sessão crítica) e cada par de tokens Ti e Ti+1 se controlam mutuamente;

•  Pares de tokens “vizinhos” geram valores “opostos” sempre que se encontram

•  Então, cada token precisa carregar dois valores •  Então, se ocorrerem k perdas simultâneas, existirá um

token Tj que será usado para detectar a falta de seus dois vizinhos. Estes poderão ser recriados, e assim por diante.

•  Assim, aos poucos os (a falta) dos demais tokens poderá ser detectada e eles podem ser recriados.

© Markus Endler 17

Generalização de Misra83 Idéia Central: Tokens se controlam 2-a-2: T0 <>T1; T1<>T2,…, Tk<>T0 usando o mesmo princípio do algoritmo original (controle dos valores de sinal oposto). Para isto, cada token carrega 2 valores, Ti.val1 e Ti.val2

T0 T1

M:

Algoritmo original

T0 T1 T2 T3 val1 val2

val2 val1

val1 val2 T0

M:

Algoritmo modificado

Em M[0] processo detecta perda de T0 (através de T1), ou vice-versa, em M[1] de T1 e T2, etc. Mesmo se restar apenas um unico token, digamos T0, será possível identificar perda de (e regenerar) tokens T3 e T1. Mas cuidado: re-geração de tokens deve ser feita em um único processo, por exemplo, quando detectado pelo token à esquerda.

© Markus Endler 18

Algoritmo de Token em Árvores A fim de evitar que um token seja transferido independente de haver uma requisição (ex.: topologia de anel), impõe-se uma estrutura de árvore no conjunto de processos (raiz = o atual detentor do token). Ideia básica do Algoritmo de [Raymond89]: •  o processo que está com o token é a raiz da árvore •  cada processo mantém uma referência (curr_dir) do seu vizinho mais próximo no nó raiz. •  cada vez que o token é transferido, a árvore é atualizada •  processo que quer entrar na CS envia mensagem REQ para vizinho indicado por curr_dir •  este vizinho gera outro REQ (em seu nome), que envia para o seu curr_dir (somente 1ª vez) •  processos intermediários armazenam requisições pendentes em fila (as cabeças das filas em cada processo intermediário indicam o caminho do token até o requisitante)

[Raymond89] Raymond, K. A tree-based Algorithm for Distributed Mutual Exclusion, ACM Transactions on Computer Systems, vol.7, no. 1, 1989.

© Markus Endler 19

Algoritmo de Raymond

Exemplo:

C

B

D

A

REQ D

(D)

D faz requisição

C

B

D

A

B consulta lista e repassa TOKEN p/ D

C

B

D

A REQ B

(D) B repassa REQ

(D)

C

B

D

A

(D)

A envia TOKEN p/ B

(D)

→  representa curr_dir, (X) requisição pendente do processo X

© Markus Endler 20

O Algoritmo de Raymond Variáveis em cada processo Pi: bool token // true, se Pi é detentor do token bool InCS // true, se Pi esta na seção crítica Addr curr_dir // referência para vizinho “na direção” do detentor do token AddrQueue reqQ // fila que contém os endereços dos vizinhos com

// requisições pendentes // obs: operação ‘rem‘ remove e retorna o 1o da fila)

when EnterCS { if (token ==FALSE) { if empty(reqQ) send(curr_dir, REQ me); reqQ.add(me); receive(any, TOKEN) //wait until token arrives (from any) to enter CS } InCS = true;

} when ExitCS {

InCS = false; if ( not empty(reqQ)) { curr_dir = reqQ.rem(); // send token to next in its queue send(curr_dir, TOKEN); token = false; if (not empty(reqQ)) send( curr_dir, REQ me) }

}

© Markus Endler 21

Algoritmo de Raymond

MonitorCS { loop

when recvd (sender, REQ) => { if (token == TRUE) { if (InCS) reqQ.add(sender); else { curr_dir = sender; send(sender, TOKEN); token = FALSE; }

} else { // token == FALSE

if (empty(reqQ)) send (curr_dir, REQ me); reqQ.add(sender);

}} when recvd(sender, TOKEN) => { curr_dir = reqQ.rem(); if (curr_dir == me) token = TRUE; else { // repassa token na direção de requisitante send(curr_dir, TOKEN); if (not empty(reqQ)) send (curr_dir, REQ me); // se houver >1 na fila } }

endloop }

Cada processo Pi executa também a thread, MonitorCS, para receber e repassar REQs e TOKENs de outros processos

© Markus Endler 22

Algoritmo de Raymond

Qual é o modelo de sistema?

•  Sincronismo da execução? •  Topologia de

interconexão? •  Canais de comunicação?

•  Nós?

Ø  Sist. assíncrono Ø Grafo (não direcionado)

conexo qualquer Ø  Seguros e confiáveis

Ø  Sem falha

© Markus Endler 23

Compressão de Caminhos (Path Compression)

O principal problema do algoritmo de Raymond é que o token precisa ser passado por vários processos intermediários até chegar ao requisitante (isto se deve à rota de encaminhamento imposto pelos ponteiros curr_dir)

è em vez disto, pode-se fazer com que a árvore tenha uma forma arbitrária e dinâmica, à medida que requisições vão sendo repassadas pelos processos.

Isto é realizado no Algoritmo de Li e Hudak (1989), usado para garantir a coerência de páginas em memória virtual distribuída.

[Li&Hudak89] K.Li e P.Hudak. Memory Coherence in shared virtual memory systems ACM Transactions on Computer Systems, 7(4), 1989.

© Markus Endler 24

Compressão de Caminhos (Path Compression)

A ideia basica do Algoritmo de [Li&Hudak89]: •  processo requisitante Pr manda msg (REQ Pr) para seu vizinho indicado por curr_dir (= endereço do detentor do token ou do último a requisitar) •  quando processo Q recebe msg (REQ Pr) de processo Pr:

•  se Q estiver com o token (e não estiver na SC), então passa o token diretamente para Pr, •  se não estiver com o token, faz um forward de msg (REQ Pr) para Q.curr_dir (mas modifica o próprio Q.curr_dir para apontar diretamente para Pr)

è  portanto, se uma requisição passar por vários intermediários, o novo curr_dir destes todos irá apontar para o “próximo futuro detentor do token”. •  Enquanto o futuro detentor do token Pr não recebe o token, este pode receber outras requisições, que serão tratadas quando este sair da SC. •  Obs: O token é repassado diretamente para o próximo da fila, e junto com o token vai a lista de requisições pendentes •  Ao receber o token, um nó faz um merge de sua lista com a lista vinda com o token.

© Markus Endler 25

Compressão de Caminhos (Path Compression)

Exemplo: A faz requisição, mas antes de A receber token surgem novas requisições (de processos B e E)

A

B

C

D

REQ A

REQ A

REQ A (A,E)

A

B

C

D (A,E)

A

B

C

D () REQ E

(B) → (E,B)

TOKEN, E

REQ B

A e E fazem requisição

E E E

•  B e C trocaram curr_dir=A •  B já envia sua REQ para A

D envia o token com lista de pendentes, atualizadas em A

(B)

© Markus Endler 26

Compressão de Caminhos (Path Compression)

Problema: à medida que o número de processos requisitantes cresce, a lista enviada com o token também aumenta

A complexidade de espaço (tamanho de mensagem) é Ο(N), e portanto o algoritmo tem problemas de escalabilidade.

Solução: manter uma lista encadeada entre os processos requisitantes (usando um ponteiro adicional, next) Seja Pu o processo mais recente a ter requisitado o token: •  um novo requisitante, processo Pn, seta next=NIL e envia o seu pedido para curr_dir •  pedido é repassado pelos processos intermediários ao longo do caminho definido por curr_dir na direção de Pu •  em cada intermediário, curr_dir é atualizado para Pn •  ao chegar em Pu, este seta curr_dir = Pn e next = Pn •  quando Pu sair da CS, irá saber para onde mandar o token (next)

© Markus Endler 27

Exemplo Compressão de Caminhos

Legenda: processos amarelos estão requisitando SC curr_dir next

D A

B C

REQ D ⊥

D A

B C REQ D

D A

B C REQ D

D A

B C

Seja C o último que requisitou token; D faz requisição, seta next=⊥ e envia REQ D

A repassa REQ D e troca seu curr_dir para D

B repassa REQ D e troca seu curr_dir para D

C (ultimo requisitante) seta next e curr_dir

© Markus Endler 28

Compressão de Caminhos (Path Compression)

when ExitCS { InCS = FALSE; IsRequesting = FALSE; if (next != NIL) { send(next, TOKEN); // envia o token para processo apontado por next token = false; next = NIL; }

}

Variáveis em cada processo Pi: bool token // TRUE sse Pi é detentor do token bool InCS // TRUE sse Pi esta na seção crítica IsRequesting // TRUE sse Pi está requisitando a SC curr_dir // dica atual sobre o processo no final da fila de espera next // o próximo processo a receber o token, ou NIL, Pi é o último when EnterCS {

IsRequesting = TRUE; if (!token) { // não possui o token send(curr_dir, REQ me); curr_dir = me; next = NIL; wait until (token==TRUE) to enter CS } InCS = true;

}

© Markus Endler 29

Compressão de Caminhos (Path Compression)

MonitorCS { // thread monitor no processo Pi loop

when recv (sender, REQ) => { if (IsRequesting == TRUE ) // Pi requisitou o token if (next ==NIL) next = sender else send(curr_dir, “REQ sender”); elseif (token == TRUE) { // Pi está com token, mas não requisitou token = FALSE; send(sender, “TOKEN, sender”)

} else { // Pi não está com token e tb não requisitou send (curr_dir, REQ sender); } curr_dir = sender; } when receive(sender, TOKEN) => { token = TRUE }

endloop }

© Markus Endler 30

Algoritmo de Li & Hudack

Qual é o modelo de sistema?

•  Sincronismo? •  Topologia de interconexão?

•  Canais de comunicação?

•  Nós?

Ø  Sistema Assíncrono

Ø  Grafo completo (todo nó envia diretamente para outro nó)

Ø  Seguros e confiáveis, e FIFO

Ø  Sem falha, e entrega FIFO das mensagens

Qual é o problema se entrega não for FIFO? Dica: considere que outro nó E (ligado a D) requisitou a seção crítica logo depois que D enviou o seu req D, mas que req E “utrapassa” req D ao longo do itinerário A-B-C. Ø  Alguns nós terão curr_dir apontando para D, outros para E

© Markus Endler 31

Algoritmos baseados em Tokens Principais Diferenças das três categorias: •  Token Circulante:

Ø  ordem de repasse previamente definida Ø  atendimento independente da ordem de requisição Ø  existe um overhead intrínseco independente do número de requisições Ø  custo máximo: N-1 mensagens, custo médio N/2 mensagens

•  Repasse do token em topologia de árvore: Ø  caminho de repasse definido pela árvore e pela direção do detentor do token Ø  atendimento na ordem de requisição (a menos de “atrasos” na transmissão de req concorrentes) Ø  custo: número de saltos por requisição: O(log N) mensagens Ø  e tamanho da mensagem O(N)

•  Repasse do token em lista encadeada: Ø  caminho de repasse definido dinamicamente Ø  atendimento na ordem de requisição (definida pelo ponteiro next) Ø  custo: número de saltos por requisição: O(log N) mensagens e Ø  tamanho da mensagem O(1)

© Markus Endler 32

Algoritmos baseados em Timestamp Sejam N processos em uma topologia de grafo completo: Idéia Central: •  um processo só entra na sessão crítica se obtém o consentimento de todos os demais processos (èconsenso)

•  usa-se broadcast para requisitar a entrada na seção crítica è todos os processos participantes são capazes de ordenar a requisição

•  usa-se o relógio lógico de Lamport para estabelecer uma ordem total dos pedidos

•  processo só envia resposta (OK) para uma requisição recebida se esta é anterior (precede causalmente) a requisição de sua própria aplicação (se houver)

•  então, se dois (ou mais) processos requisitarem a SC ao mesmo tempo, só um deles receberá a OK de N-1 processos

© Markus Endler 33

Algoritmos baseados em Timestamp Apresentaremos aqui o Algoritmo de [Ricart & Agrawala81], que usa o conceito de Relógios Lógico de L. Lamport Funcionamento básico do Algoritmo: •  processo requisitante Pr difunde um (REQ, ts) para todos os demais processos Pi

•  se o processo Pi possui um pedido pendente anterior ao ts recebido, (ou está na SC) então adia a resposta ao broadcast,

senão, retorna um REPLY para Pr •  quando Pr recebeu REPLY de todos os Pi´s, sabe-se que não existem outras requisições anteriores, e Pr pode entrar na SC

•  ao sair da SC, Pr envia todos os REPLYs pendentes (e.g. de requisições não respondidas)

Algoritmo garante: •  acesso exclusivo à Sessão Crítica •  acesso justo à SC (segundo a ordem total estabelecida)

© Markus Endler 34

Algoritmo de Ricart & Agrawala

P1

P4 P2

P3

Req, TS Rep Req, TS

Req, TS Rep

Replydeferred[1]=T

Replies_pending=1

•  se P4 está na sessão crítica (ou fez requisição anterior a P1), P4 deixa de responder à requisição de P1 •  P1 saberá que pode entrar na SC assim que tiver recebido os replies de todos os demais processos

Exemplo:

© Markus Endler 35

Algoritmo de Ricart & Agrawala

O Modelo de sistema: •  sincronismo •  conjunto/topologia de interconexão? •  canais de comunicação •  processos

Propriedades do Algoritmo: •  simplicidade e simetria de papéis •  para entrar na SC, precisa-se trocar 2*(N-1) mensagens •  como todas as requisições e liberações de SC são difundidas entre todos os processos, estes compartilham a mesma visão da lista de prioridades

•  sistema assíncrono •  grupo fixo de processos, grafo completo •  comunicação é confiável e segura •  processos sem falha

•  A confiabilidade da comunicação é essencial. Por que? •  O uso do relógico lógico é fundamental: Por que?

© Markus Endler 36

O Algoritmo de Ricart & Agrawala Variáveis do processo Pi: (assumindo M processos) TS current_time // current Lamport time TS my_timestamp // timestamp do próprio pedido int replies_pending // contador dos replies que ainda precisa receber bool is_requesting // TRUE ⇔ Pi está requisitando entrada na SC bool reply_deferred[M] // para cada processo, indica se reply foi adiado

Enter_CS() { my_timestamp = current_time is_requesting = TRUE replies_pending = N -1 send (all, REQ, my_timestamp) // difusão da requisição wait until (reply_pending == 0)

}

Exit_CS() { is_requesting = FALSE for (j=1; j <= N, j++) if (reply_deferred[j] == TRUE) { send(j,REPLY, current_time) reply_deferred[j]=FALSE }

}

© Markus Endler 37

O Algoritmo de Ricart & Agrawala

Monitor_CS() { // thread executando em todo processo Pi loop

when recv(j, REQ, req_TS) => { current_time = max(current_time, req_TS) + 1 if (not_requesting || my_timestamp > req_TS) send(j, REPLY, current_time) else reply_deferred[j] = TRUE } when recv(j, REPLY, rep_TS) => { replies_pending = replies_pending -1 current_time = max(current_time, rep_TS) +1 }

endloop }

Precisa-se ter uma thread independente para tratar do recebimento de mensagens dos demais processos (Monitor_CS), e que compartilha variáveis com procedimentos Enter_SC() e Exit_SC().

© Markus Endler 39

Algoritmos baseados em Votação (Quorum) Ideia Central:

•  analogia a uma votação política, em que geralmente um pequeno grupo de eleitores (“os indecisos”) decide uma eleição

•  em vez de consultar todos os demais processos, o requisitante da SC consulta apenas um sub-grupo de processos, e

•  caso obtenha todos as confirmações, pode entrar na SC, senão espera

•  principal vantagem: reduz o número de mensagens

Apresentaremos o algotitmo proposto por [Maekawa85]

[Maekawa85] A √N Algorithm for Mutual Exclusion in Decentralized Systems, ACM Transactions on Computer Systems, Vol 3, No. 2.

© Markus Endler 40

Algoritmos baseados em Votação (Quorum)

Seja {P1, P2, …, PM } o conjunto de processos O conceito de distrito (coterie): •  A cada processo Pi está associado um distrito (= subconjunto de

processos) Si •  Tal que: Pi, Pj, ∃ k,m, tal que Pi ∈ Sk, Pj ∈ Sm e

Sk ∩ Sm ≠ ∅ •  O conjunto de distritos Si deve ser escolhido assim: (sejam i,j =

1,..,M ) –  Pi ∈ Si // o processo pertence ao seu distrito –  Si ∩ Sj ≠ ∅ // há pelo menos 1 processo comum em

qualquer 2 distritos

E se possível respeitando: –  ⎜Si ⎜ = k // distritos devem ter número próximo de elementos –  Cada Pj pertence a D distritos Si –  que um um distrito seja conjunto próprio de outro

© Markus Endler 41

Exemplo

Ideia: Cada distrito Si é a união dos conjuntos de processos de uma linha e uma coluna, onde o Pi é a intersecção

P1

P3

P2

P4

S1 = {p1,p2, p3} S2 = {p1, p2, p4} S3 = {p1, p3, p4} S4 = {p2, p3, p4}

© Markus Endler 42

Exemplo

•  Cada Pi pertence a três distritos è D =3 •  Cada distrito tem três elementos è k = 3 •  Requisito essencial:

Para Si ∩ Sj ≠ ∅

P1

P3

P2

P4

S1 = {p1,p2, p3} S2 = {p1, p2, p4} S3 = {p1, p3, p4} S4 = {p2, p3, p4}

© Markus Endler 43

Exemplo

•  Considere N = 7 processos •  Como voce construiria os distritos?

P1

P7 P4

P2

P6

P3

P5

•  k = 4 a 5 •  D = 2 a 3

Mas será que essa é a melhor escolha para os distritos? Que minimiza o K?

© Markus Endler 45

Algoritmos baseados em Votação No caso geral (para qualquer N) não é trivial achar os

distritos Si ótimos. Uma aproximação é usar k = Ο(√N): colocar os N processos em uma matriz (√N x √N) e usar como Si = os processos da linha e da coluna que contenham Pi . (Neste caso k ≈ 2 √N)

7

4

1

8

5

2 3

6

9

S4 Note: ⎜S4 ⎜ = 5 P4 ∈{S4,S1,S7,S5,S6} D = 5

© Markus Endler 46

Algoritmos baseados em Votação Uma vez determinados todos os distritos Si e seus

processos membros, como seria o algoritmo para garantir a exclusã mútua?

Um algoritmo naïve: •  para entrar na SC, um processo Pi envia REQ para todos em Si •  cada processo em Si responde com YES, caso já não tenha dado o

seu voto para outro pedido •  ao sair da SC, Pi envia RELEASE para todos em Si

Problema?

Possibilidade de deadlock, pois não há garantia de que todos elementos em Si irão receber todos os REQs na mesma ordem

© Markus Endler 47

Situação de Deadlock

•  P2 recebe REQ1, seguida de REQ4 e dá voto a P1 •  P3 recebe REQ4, seguida de REQ1e dá voto a P4 •  P1 fica aquardando OK de P3 •  P4 fica aguardando OK de P2

P1 P2 P3 P4

Precisa-se : –  uma forma para decidir qual requisição é a anterior –  Permitir que processos re-definam os seus votos

REQ1 REQ4

Sa Sb

© Markus Endler 48

Algoritmos baseados em Votação Solução do problema [Sanders87+96] usando Relógios Lógicos:

•  junto com cada REQ envia-se o timestamp de relógio lógico (Lamport) •  se um P ∈ Si ∩ Sj ja deu o seu voto para REQi e depois recebe a REQj com

um timestamp menor, P vai enviar msg INQuire para Pi para anular o seu voto

•  Ao receber INQ* e se Pi ainda não tiver obtido todos os votos de seu distrito Si, então Pi “devolve os votos”, adiando a sua entrada na SC

O relógio lógico impõe uma ordem total aos pedidos, evitando deadlock, pois: •  ou a requisição com menor timestamp recebe todos os votos (talvez depois

de pedir de volta o voto), •  ou então, se INQ chegar atrasado, o requisitante que já obteve todos os

votos e entra na SC (*) INQ vem com o timestamp da requisição cujo voto está sendo solicitado de

volta

[Sanders87] The information structure of Distributed Mutual Exclusion Algorithms, ACM Trans. On Computer Systems, 5(3), 1987. [Sanders96] Data Refinement of mixed specification: A Generalization of UNITY, Dept. of CISE, University of Florida, Tech. Report 96-010

© Markus Endler 49

Exemplo: Algoritmo de Sanders

a b c d

A

B

REQB, 5

REQA, 4

a b c d

A

B

YES

Deferred = (A,4) Deferred = (A,4)

Situação 1: Apesar da requisição de A ser anterior a de B, B entra na SC (e ignora todos os pedidos de INQ) porque já obteve os votos de SB = {b,c, d}

INQ INQ

Sejam requisições REQA e REQB, com REQA_TS < REQB_TS e SA = {a,b,d} SB = {b,c,d}

Processos na intersecção dos distritos

© Markus Endler 50

Exemplo: Algoritmo de Sanders

a b c d

A

B

REQB, 5

REQA, 4

a b c d

A

B

YES

Deferred = (B,5)

Situação 2: Como B ainda não entrou na SC, o INQ de d é tratado, B devolve o voto de d, que envia o seu voto para A, e A entra na SC.

INQ

RELINQUISH

YES YES

Ainda REQA e REQB, com REQA_TS < REQB_TS com SA = {a,b,d} SB = {b,c,d}

© Markus Endler 51

Algoritmo de Sanders

Variáveis em cada processo Pi: Si // o distrito associado a Pi InCS // TRUE se Pi está na sessão crítica curr_TS // o relógio lógico corrente my_TS // timestamp do próprio pedido de entrada na SC yes_votes // # de processos que responderam YES has_voted // TRUE se Pi ja deu seu voto para algum candidato cand // ID do candidato para o qual foi dado o voto cand_TS // timestamp do pedido do candidato cand inquired // TRUE se Pi tentou anular o seu voto deferredQ // fila de pedidos pendentes, com as seguintes operações

add({P, TS}), // adiciona o par {processo, TS} da requisição rem_min() // remove o par {processo,TS} tq. TS é o menor valor notempty() // retorna TRUE se a fila não está vazia

Tipos de Mensagem: •  REQ + TS // pedido de entrada na SC •  RELEASE // notificação de saida da SC •  RELINQUISH // devolução dos votos •  YES // voto para entrada na SC •  INQ + TS // solicitação de devolução do voto

© Markus Endler 52

Algoritmo de Sanders

Enter_CS { my_TS = curr_TS forall r in Si send(r, REQ, my_TS) // multicast to its coterie/district while (yes_votes < ⎜Si ⎜) {

when recvd(sender,YES) => yes_votes := yes_votes+1 when recvd(sender, INQ, inq_TS) => if (my_TS == inq_TS) { send (sender, RELINQUISH); yes_votes := yes_votes-1 }

} InCS = TRUE }

Exit_CS { InCS = FALSE; forall r in Si send(r, REL)

}

© Markus Endler 53

Algoritmo de Sanders Monitor_CS { loop when recvd(sender, REQ, req_TS) => {

if (NOT has_voted) { send(sender,YES) cand = sender; cand_TS = req_TS; // registrando para quem mandou has_voted = TRUE } else { deferredQ.add({sender, req_TS}) if (req_TS < cand_TS) && (NOT inquired) { send(cand, INQ, cand_TS); // pede anulação do voto inquired = TRUE }

} } when recvd(sender,RELINQUISH) => {

deferredQ.add({cand, cand_TS}) {s, r_TS} = deferredQ.remove_min(); // resgata requisição anterior send(s, YES); cand = s; cand_TS = r_TS; inquired = FALSE;

} when recvd(sender,RELEASE) => {

if (deferredQ.notempty()) { {s, r_TS} = deferredQ.remove_min(); send(s, YES); cand = s; cand_TS = r_TS; } else has_voted = FALSE; inquired = FALSE

} endloop }

© Markus Endler 54

O Algoritmo de Sanders

Usando o TS e o PID, define-se uma ordem total (precedência) entre os pedidos.

Como nem A nem B já receberam todos os YES de seus distritos, pelo menos um dos processos na intersecção irá pedir de volta os seu voto quando conhecer o pedido do outro processo. Pois PIDA < PIDB

A B

© Markus Endler 55

Algoritmo de Sanders

Premissas: •  sistema assíncrono •  grafo completo de interconexão •  comunicação é confiável e entrega é FIFO •  comunicação é segura (timestamps e PiDs não podem

ser corrompidos) •  requer uma associação prévia entre distritos e

processos (existem outros algoritmos que permitem uma associação dinâmica)

•  um processo pode falhar (temporariamente), contanto que não esteja envolvido em um processo de votação

© Markus Endler 56

Corretude do Algoritmo Argumentação informal de que o algoritmo garante a exclusão mútua (safety):

1.  Cada processo a cada momento dá o seu voto para no máximo um processo requisitante: mesmo se P tiver mudado o seu voto, isto só acontece após ter recebido RELEASE ou RELINQUISH, que pressupõem que o processo que tinha o voto de P abriu mão do voto.

2.  Dado que: •  Si ∩ Sj para i,j; •  a entrada de um P na SC só ocorre após receber YES de todos

os processos em seu distrito; •  usando o fato no item 1, e que •  P só libera os votos obtidos ao sair da SC

tem-se que não é possível que dois ou mais processos entrem (e permaneçam) simultaneamente na SC.

© Markus Endler 57

Corretude do Algoritmo Argumentação informal sobre a garantia de vivacidade (liveness), ausência de deadlocks

Suponha (por absurdo) que: •  em algum momento exista um (ou mais) processo bloqueado por

ainda não ter obtido todos os votos de seu distrito e •  que ao longo de qualquer possível execução a partir deste estado,

(um estado causalmente dependente) algum outro processo também bloqueie tentando entrar na SC (ou seja, o sistema esteja em deadlock).

Seja A processo bloqueado requisitando a SC com o menor time-stamp. Este processo está esperando o voto de um processo P de seu distrito, que deu o seu voto a um processo, p.ex. do qual recebeu a mensagem REQ primeiro. Mas como o REQA.TS < REQB.TS, P deve ter enviado um INQ para B. A única razão para B não responder ao INQ é se B já obteve todos os votos e está na SC. è Mas isto contradiz a nossa hipótese de que nenhum está na SC.

© Markus Endler 58

Trabalho Prático T1

•  Implemente em Sinalgo, o algoritmo de Sanders87 para exclusão mútua usando distritos.

•  Defina que todos os processos não se movem e conseguem se comunicar com todos os demais processos.

•  Leia de um arquivo de configurção quais são os distritos Si

0 1 2

S2

[Sanders87] The information structure of Distributed Mutual Exclusion Algorithms, ACM Trans. On Computer Systems, 5(3), 1987.

© Markus Endler 59

Trabalho Prático T1

•  Testar o algoritmo para 25 e 64 processos, (k= 9 e 15, respectivamente)

•  Os canais de comunicacão entregam mensagens em cordem FIFO, mas podem sofrer atrasos fixos com probabilidade PDelay

•  Seja PSC a probabilidade de cada nó requisitar a entrada a secão crítica a cada momento (caso já tenha solicitado e ainda não tenha entrado, a ação é inócua).

•  Então, medir o número de vezes em que RELINQUISH acontece, para duas ou três probabilidades (e.g. PSC = 0.2, PSC = 0.5 e PSC = 0.8)

•  Bonus: comparar com o algoritmo de Sanders com o de Ricart e Agravala

Prazo: 6/Maio – 23:59 – apresentacão em 7/Maio em aula