SO-03 Comunicação e Sincronização de Processos
-
Upload
nicola-zagari -
Category
Technology
-
view
13.792 -
download
1
description
Transcript of SO-03 Comunicação e Sincronização de Processos
Processos Comunicação e Sincronização
2 Comunicação e Sincronização Eduardo Nicola F. Zagari
Processos Concorrentes Comunicação Interprocessos O Problema da Seção Crítica Sincronização por Hardware e por Software Semáforos Problemas Clássicos de Sincronização
3 Comunicação e Sincronização Eduardo Nicola F. Zagari
Processos que executam ao mesmo tempo podem existir de maneira independente um dos outros ou podem requerer sincronização ocasional ou cooperação.
Processos Independentes versus Processos Cooperantes Independentes: não afetam nem são afetados pela execução de um
outro processo Cooperantes: podem afetar ou serem afetados pela execução de
outros processos Vantagens da cooperação:
compartilhamento de informação aumento da velocidade de computação modularidade conveniência
4 Comunicação e Sincronização Eduardo Nicola F. Zagari
Paradigma de processos cooperantes: producer produz informação que é consumida pelo consumer
Buffer sem limite Buffer limitado
Processo Produtor
Processo Consumidor
Sincronização
buffer tamanho ilimitado x limitado
dados dados
5 Comunicação e Sincronização Eduardo Nicola F. Zagari
public interface Buffer{ // producers call this method public abstract void insert(Object item);
// consumers call this method public abstract Object remove();}
6 Comunicação e Sincronização Eduardo Nicola F. Zagari
import java.util.*;public class BoundedBuffer implements
Buffer{
private static final int BUFFER SIZE = 5;private int count; // number of items in
the bufferprivate int in; // points to the next
free positionprivate int out; // points to the next
full positionprivate Object[] buffer;public BoundedBuffer() {
// buffer is initially emptycount = 0;in = 0;out = 0;buffer = new Object[BUFFER SIZE];
}// producers calls this methodpublic void insert(Object item) { // Slide 7}// consumers calls this methodpublic Object remove() { // Slide 8}
}
7 Comunicação e Sincronização Eduardo Nicola F. Zagari
public void insert(Object item) { while (count == BUFFER SIZE) ; // do nothing -- no free buffers // add an item to the buffer ++count; buffer[in] = item; in = (in + 1) % BUFFER SIZE;}
8 Comunicação e Sincronização Eduardo Nicola F. Zagari
public Object remove() { Object item; while (count == 0) ; // do nothing -- nothing to consume // remove an item from the buffer --count; item = buffer[out]; out = (out + 1) % BUFFER SIZE; return item;}
9 Comunicação e Sincronização Eduardo Nicola F. Zagari
Mecanismo do SO que permite que processos se comuniquem e sincronizem suas ações
Sistema de Mensagens – processos se comunicam entre si sem a necessidade de compartilhar variáveis
IPC fornece duas operações: send(message) – mensagem de tamanho fixo ou variável receive(message)
Se P e Q desejam se comunicar, eles precisam: Estabelecer um canal de comunicação entre eles Trocar mensagens via send/receive
Implementação do canal de comunicação: físico (p.ex.: memória compartilhada) lógico (p.ex.: propriedades lógicas)
10 Comunicação e Sincronização Eduardo Nicola F. Zagari
Como a ligação é estabelecida? Pode ser associada a mais de 2 processos? Quantas ligações podem existir entre 2 processos? Qual a capacidade? Tamanho da mensagem fixo ou variável? Unidirecional ou bidirecional?
11 Comunicação e Sincronização Eduardo Nicola F. Zagari
send(P, mensagem) receive(Q, mensagem) ligação automática ligação entre 2 processos só existe uma ligação uni ou bidirecional desvantagem: se mudar o ID do
processo?
send(A, mensagem) receive(A, mensagem) ligação entre processos que
compartilham A ligação entre 2 ou mais processos podem existir várias ligações uni ou bidirecional Processos têm que compartilhar o
mailbox (ID único)
Comunicação direta x indireta
mailbox
12 Comunicação e Sincronização Eduardo Nicola F. Zagari
Troca de Mensagens pode ser bloqueante ou não-bloqueante Bloqueante é considerada síncrona
send bloqueante – quem envia espera até que uma mensagem seja recebida
receive bloqueante – quem recebe espera até que uma mensagem esteja disponível
Não-bloqueante é considerada assíncrona send não-bloqueante – o transmissor envia a mensagem e
continua receive não-bloqueante – o receptor recebe uma mensagem
válida ou um null
13 Comunicação e Sincronização Eduardo Nicola F. Zagari
Filas de mensagens implementam uma das três capacidades: 1. Zero – 0 mensagens
não pode haver mensagem esperando, quem envia deve esperar até que a mensagem seja recebida (sincronizada)
2. Limitada – número finito de n mensagensse está cheio, quem envia deve esperar
5. Ilimitada – tamanho infinito quem envia nunca espera
14 Comunicação e Sincronização Eduardo Nicola F. Zagari
Acesso concorrente a dados compartilhados podem resultar em inconsistência dos mesmos
Manter a consistência dos dados requer mecanismos para assegurar a execução ordenada dos processos cooperantes
Race condition : situação em que dois ou mais processos querem partilhar um recurso (escrever e ler dados compartilhados) e o resultado final depende de quem executou quando (ordem de escalonamento)
Processos concorrentes necessitam de mecanismos de sincronização para evitar que outro processo tenha acesso a uma variável comum, quando esta está sendo modificada por um processo
15 Comunicação e Sincronização Eduardo Nicola F. Zagari
Produtor while (1) { while (count ==BUFFER_SIZE) ; // não faz nada // produz um item e o coloca
em nextProduced buffer[in] = nextProduced; in = (in + 1) % BUFFER_SIZE; count++; }
Consumidor while (1) { while (count == 0) ; // não faz nada nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; // consome o item em nextConsumed }
A solução de Shared-memory para o problema do buffer limitado tem uma condição de corrida na classe de dados count.
16 Comunicação e Sincronização Eduardo Nicola F. Zagari
count++ poderia ser implementado como
register1 = count register1 = register1 + 1 count = register1
count– poderia ser implementado como
register2 = count register2 = register2 - 1 count = register2
Considere esta intercalação de execução: S0: produtor executa register1 = count {register1 = 5}
S1: produtor executa register1 = register1 + 1 {register1 = 6} S2: consumidor executa register2 = count {register2 = 5} S3: consumidor executa register2 = register2 - 1 {register2 = 4} S4: produtor executa count = register1 {count = 6 } S5: consumidor executa count = register2 {count = 4}
17 Comunicação e Sincronização Eduardo Nicola F. Zagari
Como evitar condições de corrida? Evitar que os processos leiam e escrevam dados compartilhados ao
mesmo tempo Exclusão Mútua: se um processo estiver usando uma variável ou
arquivo, o outro processo está excluído do uso Isto é, garantia de acesso exclusivo a um recurso
Parte do tempo o processo executa computação que não leva a condições de corrida a parte do programa em que memória compartilhada é acessada é chamada de Região Crítica ou Seção Crítica
18 Comunicação e Sincronização Eduardo Nicola F. Zagari
1. Exclusão Mútua – Se o processo Pi está executando dentro da sua seção crítica, então nenhum outro processo pode estar executando em sua seção crítica correspondente Dois processos não podem estar simultaneamente dentro de suas regiões
críticas correpondentes 3. Progresso – Se nenhum processo está executando dentro de sua
seção crítica e existem alguns processos que desejam adentrar suas seções críticas, então a seleção do próximo processo que irá entrar em sua seção crítica não pode ser adiada Nenhum processo que esteja rodando fora de sua seção crítica pode
bloquear a execução de outro processo 5. Espera Limitada - Nenhum processo pode ser obrigado a esperar
indefinidamente para entrar em sua região crítica Nenhuma suposição pode ser feita com relação à velocidade de execução
dos processos ou ao número de processadores disponíveis no sistema
19 Comunicação e Sincronização Eduardo Nicola F. Zagari
Mecanismos de Hardware: Inibição de Interrupções Instrução TSL (Test and Set Lock)
Mecanismos de Software: Com Espera Ocupada:
Variáveis de Travamento (Lock Variables) Alternância Estrita Solução de Peterson
Sem Espera Ocupada: Dormir e Acordar (Sleep and Wakeup) Semáforos Contadores de Eventos Monitores Troca de Mensagens
20 Comunicação e Sincronização Eduardo Nicola F. Zagari
Solução mais simples Desabilitam-se todas as interrupções após se entrar na região
crítica ao sair, reabilitam-nas Se as interrupções não ocorrem, o processo não pode sofrer
preempção Problema 1: ... e se o processo não reabilitar as interrupções? Problema 2: se o sistema tem 2 CPUs e somente uma tem as
interrupções inibidas, a outra pode acessar a memória compartilhada
Por outro lado, é muito conveniente que o kernel possa desabilitar as interrupções para atualizar variáveis ou listas
Conclusão: solução adequada para o kernel, mas não para processos de usuário
21 Comunicação e Sincronização Eduardo Nicola F. Zagari
Instrução especial que permite ler uma variável, armazenar seu conteúdo em uma outra área e atribuir um novo valor a esta variável (hardware)
É uma instrução indivisível: executada sem interrupção Variável compartilhada flag: quando flag = 0, qualquer processo
pode fazê-la igual a 1 (instrução TSL)
22 Comunicação e Sincronização Eduardo Nicola F. Zagari
enter_region:
tsl register, flag | copia flag p/
| registrador e faz flag = 1
cmp register, #0 | o flag é zero?
jnz enter_region | se não,lock e setado;loop
ret | retorna, entrou na R.C.
leave_region:
mov flag, #0 | guarda um 0 em flag
ret | retorna a quem chamou
23 Comunicação e Sincronização Eduardo Nicola F. Zagari
Define-se uma única variável compartilhada, inicialmente com valor zero
Se um processo deseja entrar na região crítica, ele testa a variável. Se ela for 1 (recurso liberado), ela é feita 0 (recurso trancado) e o processo entra. Caso contrário, o processo aguarda até que o valor da variável seja 1 (um)
24 Comunicação e Sincronização Eduardo Nicola F. Zagari
x = 0, recurso trancado x = 1, recurso liberado
while (x == 0) do /* nada */; Entrada x = 0; Entrada
... Região Crítica x = 1; Saída
Problema: Este mecanismo apresenta a falha de que a alteração de valor para “trancado”ʼ, após o teste, permite que dois processos executem a Região Crítica (RC) ao mesmo tempo
O teste e a alteração necessitam ser feitos de forma indivisível (atômica)
25 Comunicação e Sincronização Eduardo Nicola F. Zagari
Inicialmente a variável turn é feita igual a 0 (zero) Assim, o Processo A consegue entrar na região crítica Enquanto isto, o Processo B ficará continuamente testando a
variável (Espera Ocupada)
while (TRUE) {
while (turn != 0);
Reg_Crítica();
turn = 1;
Reg_Não_Crítica();
}
while (TRUE) {
while (turn != 1);
Reg_Crítica();
turn = 0;
Reg_Não_Crítica();
}
Processo A Processo B
26 Comunicação e Sincronização Eduardo Nicola F. Zagari
Problema 1: o fato do Processo B ficar fazendo “Espera Ocupada” deve ser evitado, pois consome tempo de CPU
Problema 2: não é uma boa solução quando um processo é muito mais lento que o outro
Problema 3: viola a condição de que um processo que não esteja em sua região crítica não bloqueie outro
27 Comunicação e Sincronização Eduardo Nicola F. Zagari
Antes de entrar na Região Crítica (RC), cada processo chama enter_region com seu no (0 ou 1) como parâmetro. Ao sair, leave_region
#include “prototypes.h”
#define FALSE 0
#define TRUE 1
#define N 2 /* no de processos */
int turn; /* de quem é a vez */
int interested[N]; /* todos valores inici- */
/* almente 0 (FALSE) */
28 Comunicação e Sincronização Eduardo Nicola F. Zagari
void enter_region (int process) { /* process = quem
está entrando (0 ou 1) */
int other; /* no do outro processo */
other = 1 - process; /* o outro processo */
interested[process] = TRUE; /* mostra interesse */
turn = process; /* define flag */
while (turn==process && interested[other]==TRUE);
}
void leave_region (int process) { /* process = quem
está saindo */
interested[process] = FALSE; /* indica saída RC */
}
29 Comunicação e Sincronização Eduardo Nicola F. Zagari
Duas tarefas, T0 e T1 (Ti e Tj) Três “soluções” são apresentadas. Todas elas implementam a
interface MutualExclusion :
public interface MutualExclusion { public static final int TURN_0 = 0; public static final int TURN_1 = 1;
public abstract void enteringCriticalSection(int turn); public asbtract void leavingCriticalSection(int turn); }
30 Comunicação e Sincronização Eduardo Nicola F. Zagari
Usado para criar duas threads e testar cada algoritmo public class AlgorithmFactory { public static void main(String args[]) { MutualExclusion alg = new Algorithm_1(); Thread first = new Thread( new Worker("Worker 0", 0, alg)); Thread second = new Thread(new Worker("Worker 1", 1, alg));
first.start(); second.start(); } }
31 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class Worker implements Runnable {
private String name; private int id; private MutualExclusion mutex;
public Worker(String name, int id, MutualExclusion mutex) { this.name = name; this.id = id; this.mutex = mutex; } public void run() { while (true) { mutex.enteringCriticalSection(id); MutualExclusionUtilities.criticalSection(name); mutex.leavingCriticalSection(id); MutualExclusionUtilities.nonCriticalSection(name); } } }
32 Comunicação e Sincronização Eduardo Nicola F. Zagari
Threads compartilham uma variável inteira comum (turn) Se turn==i, thread i é permitda a executar Não satisfaz o requisito “Progresso”
Por quê?
33 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class Algorithm_1 implements MutualExclusion { private volatile int turn;
public Algorithm_1() { turn = TURN_0; } public void enteringCriticalSection(int t) { while (turn != t) Thread.yield(); } public void leavingCriticalSection(int t) { turn = 1 - t; } }
34 Comunicação e Sincronização Eduardo Nicola F. Zagari
Adicionar mais informação de estado Flags booleanas para indicar o interesse das threads em
entrar na seção crítica O requisito “Progresso” ainda não é satisfeito
Por quê?
35 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class Algorithm_2 implements MutualExclusion { private volatile boolean flag0, flag1; public Algorithm_2() { flag0 = false; flag1 = false; } public void enteringCriticalSection(int t) { if (t == 0) { flag0 = true; while(flag1 == true) Thread.yield(); } else { flag1 = true; while (flag0 == true) Thread.yield(); } } // Continued On Next Slide
36 Comunicação e Sincronização Eduardo Nicola F. Zagari
public void leavingCriticalSection(int t) { if (t == 0) flag0 = false; else flag1 = false; } }
37 Comunicação e Sincronização Eduardo Nicola F. Zagari
Combina as idéias de 1 e 2 Ele satisfaz os requisitos da solução do problema da seção
crítica?
38 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class Algorithm_3 implements MutualExclusion { private volatile boolean flag0; private volatile boolean flag1; private volatile int turn; public Algorithm_3() { flag0 = false; flag1 = false; turn = TURN_0; } // Continued on Next Slide
39 Comunicação e Sincronização Eduardo Nicola F. Zagari
public void enteringCriticalSection(int t) { int other = 1 - t; turn = other; if (t == 0) { flag0 = true; while(flag1 == true && turn == other) Thread.yield(); } else { flag1 = true; while (flag0 == true && turn == other) Thread.yield(); } } // Continued on Next Slide
40 Comunicação e Sincronização Eduardo Nicola F. Zagari
public void leavingCriticalSection(int t) { if (t == 0) flag0 = false; else flag1 = false; } }
41 Comunicação e Sincronização Eduardo Nicola F. Zagari
Problema da Solução de Peterson e da Instrução TSL: o processo que não consegue acesso à R.C. permanece em “espera ocupada” (busy waiting): gasta tempo de processador inutilmente pode provocar o Problema da Inversão de Prioridade: Dois processos A (alta prioridade) e B (baixa prioridade)
» B entra na Região Crítica; » A vai para estado de pronto; » A passa para estado de execução; » A fica testando indefinidamente.
Chamadas de sistema: SLEEP e WAKEUP
42 Comunicação e Sincronização Eduardo Nicola F. Zagari
Dois processos compartilham um buffer de tamanho fixo. Um dos processos, o produtor, coloca informação no buffer, e o outro, o consumidor, retira informação do buffer.
Se o buffer estiver cheio, o produtor dorme e é acordado quando o consumidor remover um item
Se o buffer estiver vazio, o consumidor dorme até que seja produzido e armazenado algum item
43 Comunicação e Sincronização Eduardo Nicola F. Zagari
#define N 100 /* no máximo de ítens */
int count = 0; /* no de ítens no buffer */
void producer (void) {
int item;
while (TRUE) {
produce_item(&item);
if (count==N) sleep();
enter_item(item);
count += 1;
if (count==1) wakeup(consumer);
}
}
void consumer(void) {
int item;
while (TRUE) {
if (count==0) sleep();
remove_item(&item);
count -= 1;
if (count==N-1) wakeup(producer);
consume_item(item);
}
}
Produtor Consumidor
44 Comunicação e Sincronização Eduardo Nicola F. Zagari
Problema: Se o buffer estiver vazio e o consumidor sofrer preempção antes de
dormir o produtor produz e envia um wakeup para o consumidor que ainda não está dormindo o sinal se perde e o consumidor dormirá para sempre...
45 Comunicação e Sincronização Eduardo Nicola F. Zagari
Mecanismo de sincronização que não requer espera ocupada Dijkstra propôs usar uma variável inteira para contar o no de
WAKEUPs para uso futuro Menos complicado Esta variável, denominada semáforo, pode ter valor 0 (nenhum
WAKEUP pendente) ou um valor inteiro positivo
Apenas duas operações são definidas sobre estas variáveis: as operações P (Down) e V (Up)
46 Comunicação e Sincronização Eduardo Nicola F. Zagari
P(s) (Down) - checa o valor do semáforo. Se o valor é maior que 0 (zero), decrementa e continua. Se for igual a 0, o processo é posto para dormir Ação atômica: é garantido que, uma vez iniciada a operação, nenhum
outro processo tem acesso ao semáforo (essencial para resolver problemas de sincronização e evitar condições de corrida)
P(s) equivale a: Se s > 0 então s := s - 1 senão bloqueia o processo até s > 0 (= wait(s))
V(s) (Up) - se um ou mais processos estão dormindo no semáforo, um deles é escolhido aleatoriamente pelo SO e continua sua operação Down (o valor zero continua). Se não há ninguém “dormindo” no semáforo, incrementa o valor dele Operação também é indivisível
V(s) equivale a: Verifica se existe uma lista com processos bloqueados por causa
de s, se existe escolhe um e o “acorda”, deixando-o pronto para seguir sua
execução de P(s) (= signal(s)) senão s := s + 1
47 Comunicação e Sincronização Eduardo Nicola F. Zagari
Semáforo Contador – valor inteiro positivo pode variar sem limites
Semáforo Binário – valor inteiro só pode variar entre 0 e 1; Também conhecido como mutex locks
Para fornecer exclusão mútua:
Semaphore S; // initialized to 1
P(S); criticalSection(); V(S);
48 Comunicação e Sincronização Eduardo Nicola F. Zagari
#define N 100 /* no máximo de ítens */ typedef int semaphore;
semaphore mutex = 1; /* controla acesso à RC */ semaphore empty = N; /* conta slots vazios */ semaphore full = 0; /* conta slots ocupados */
void producer (void) {
int item;
while (TRUE) {
produce_item(&item);
P(&empty);
P(&mutex);
enter_item(item);
V(&mutex);
V(&full);
}
}
void consumer(void) {
int item;
while (TRUE) {
P(&full);
P(&mutex);
remove_item(&item);
V(&mutex);
V(&empty);
consume_item(item);
}
}
49 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class Worker implements Runnable { private Semaphore sem; private String name; public Worker(Semaphore sem, String name) { this.sem = sem; this.name = name; } public void run() { while (true) { sem.acquire(); MutualExclusionUtilities.criticalSection(name); sem.release(); MutualExclusionUtilities.nonCriticalSection(name); } } }
50 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class SemaphoreFactory { public static void main(String args[]) { Semaphore sem = new Semaphore(1); Thread[] bees = new Thread[5]; for (int i = 0; i < 5; i++) bees[i] = new Thread(new Worker (sem, "Worker " + (new Integer(i)).toString() )); for (int i = 0; i < 5; i++) bees[i].start(); } }
51 Comunicação e Sincronização Eduardo Nicola F. Zagari
Deadlock – dois ou mais processos ficam esperando indefinidamente por um evento que pode ser causado apenas por um dos processos bloqueados
Sejam S e Q dois semáforos inicializados com 1 P0 P1
P(S); P(Q); P(Q); P(S); . . . . . . V(S); V(Q); V(Q); V(S); Starvation – bloqueio indefinido. Um processo pode nunca ser
removido da fila de semáforos na qual ele está bloqueado.
52 Comunicação e Sincronização Eduardo Nicola F. Zagari
Problema do Buffer Limitado Problema dos Leitores e Escritores Problema do Jantar dos Filósofos
53 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class BoundedBuffer implements Buffer { private static final int BUFFER SIZE = 5; private Object[] buffer; private int in, out; private Semaphore mutex; private Semaphore empty; private Semaphore full;
// Continued on next Slide
54 Comunicação e Sincronização Eduardo Nicola F. Zagari
public BoundedBuffer() { // buffer is initially empty in = 0; out = 0; buffer = new Object[BUFFER SIZE]; mutex = new Semaphore(1); empty = new Semaphore(BUFFER SIZE); full = new Semaphore(0); } public void insert(Object item) { /* next slides */ }
public Object remove() { /* next slides */ } }
55 Comunicação e Sincronização Eduardo Nicola F. Zagari
public void insert(Object item) { empty.acquire(); mutex.acquire(); // add an item to the buffer buffer[in] = item; in = (in + 1) % BUFFER SIZE; mutex.release(); full.release(); }
56 Comunicação e Sincronização Eduardo Nicola F. Zagari
public Object remove() { full.acquire(); mutex.acquire(); // remove an item from the buffer Object item = buffer[out]; out = (out + 1) % BUFFER SIZE; mutex.release(); empty.release(); return item; }
57 Comunicação e Sincronização Eduardo Nicola F. Zagari
import java.util.Date; public class Producer implements Runnable { private Buffer buffer; public Producer(Buffer buffer) { this.buffer = buffer; } public void run() { Date message; while (true) { // nap for awhile SleepUtilities.nap(); // produce an item & enter it into the buffer message = new Date(); buffer.insert(message); } } }
58 Comunicação e Sincronização Eduardo Nicola F. Zagari
import java.util.Date; public class Consumer implements Runnable { private Buffer buffer; public Consumer(Buffer buffer) { this.buffer = buffer; } public void run() { Date message; while (true) { // nap for awhile SleepUtilities.nap(); // consume an item from the buffer message = (Date)buffer.remove(); } } }
59 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class Factory { public static void main(String args[]) { Buffer buffer = new BoundedBuffer(); // now create the producer and consumer threads Thread producer = new Thread(new Producer(buffer)); Thread consumer = new Thread(new Consumer(buffer)); producer.start(); consumer.start(); } }
60 Comunicação e Sincronização Eduardo Nicola F. Zagari
Acesso a uma base de dados É aceitável que haja mais de um processo lendo a base de
dados ao mesmo tempo, mas... ... se um processo estiver escrevendo na base, nenhum outro
processo, nem mesmo os leitores, podem ter acesso a ela, enquanto o escritor não tiver terminado
61 Comunicação e Sincronização Eduardo Nicola F. Zagari
Program Solução; typedef int semaphore; semaphore mutex = 1; semaphore db = 1; int rc = 0;
void writer (void) {
while (TRUE) {
think_up_data();
P(&db);
write_data_base();
V(&db);
}
}
void reader (void) {
while (TRUE) {
P(&mutex);
rc++;
if (rc == 1)
P(&db);
V(&mutex);
read_data_base();
P(&mutex);
rc--;
if (rc == 0)
V(&db);
V(&mutex);
use_data_read();
}
}
62 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class Reader implements Runnable { private RWLock db; public Reader(RWLock db) { this.db = db; } public void run() { while (true) { // nap for awhile db.acquireReadLock();
// you now have access to read from the database // read from the database
db.releaseReadLock(); } } }
63 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class Writer implements Runnable { private RWLock db; public Writer(RWLock db) { this.db = db; } public void run() { while (true) { db.acquireWriteLock(); // you have access to write to the database
// write to the database
db.releaseWriteLock(); } } }
64 Comunicação e Sincronização Eduardo Nicola F. Zagari
public interface RWLock { public abstract void acquireReadLock(); public abstract void acquireWriteLock(); public abstract void releaseReadLock(); public abstract void releaseWriteLock(); }
65 Comunicação e Sincronização Eduardo Nicola F. Zagari
public class Database implements RWLock { private int readerCount; private Semaphore mutex; private Semaphore db; public Database() { readerCount = 0; mutex = new Semaphore(1); db = new Semaphore(1); } public int acquireReadLock() { /* next slides */ } public int releaseReadLock() {/* next slides */ } public void acquireWriteLock() {/* next slides */ } public void releaseWriteLock() {/* next slides */ } }
66 Comunicação e Sincronização Eduardo Nicola F. Zagari
public void acquireReadLock() { mutex.acquire(); ++readerCount; // if I am the first reader tell all others // that the database is being read if (readerCount == 1) db.acquire(); mutex.release(); } public void releaseReadLock() { mutex.acquire(); --readerCount; // if I am the last reader tell all others // that the database is no longer being read if (readerCount == 0) db.release(); mutex.release(); }
67 Comunicação e Sincronização Eduardo Nicola F. Zagari
public void acquireWriteLock() { db.acquire(); }
public void releaseWriteLock() { db.release(); }
68 Comunicação e Sincronização Eduardo Nicola F. Zagari
69 Comunicação e Sincronização Eduardo Nicola F. Zagari
Cinco filósofos passam a vida meditando em suas salas e, quando têm fome, vão ao refeitório, comem e voltam a meditar
Para um filósofo comer é necessário conseguir os dois chopsticks (pauzinhos chineses) que, originalmente, estão à esquerda e à direita do lugar onde se sentou
Um filósofo pode sentar em qualquer dos cinco lugares O estoque de comida é interminável Deve-se garantir:
exclusão mútua não ocorrência de deadlock não ocorrência de adiamento indefinido (starvation) não existência de prioridade para algum filósofo possibilidade de velocidades diferenciadas
70 Comunicação e Sincronização Eduardo Nicola F. Zagari
Procedure filosofo(i:integer)
begin
while TRUE do
begin
Pensando;
P(pauzinho[i]);
P(pauzinho[(i+1)mod N]);
Comendo;
V(pauzinho[i]);
V(pauzinho[(i+1)mod N]);
end;
end;
Program Tentativa_Um; var pauzinho: array[1..5] of semaforo; i: integer;
Begin for i:= 1 to 5 do
pauzinho[i] := 1;
Cobegin
filosofo(1);
filosofo(2);
filosofo(3);
filosofo(4);
filosofo(5);
Coend;
End.
71 Comunicação e Sincronização Eduardo Nicola F. Zagari
Não funciona se os filósofos resolverem comer ao mesmo tempo e cada um pegar o pauzinho esquerdo
não haverá pauzinho direito (deadlock)
Pode-se modificar o programa de tal forma que, após pegar pauzinho esquerdo, verifica-se se o pauzinho direto está livre se não, põe-se de volta o pauzinho
suponha que todos peguem e testem juntos adiamento indefinido (starvation)
Se cada filósofo aguardar um tempo randômico tempo de resposta variável (além de
ainda ser susceptível à falha de inanição)
72 Comunicação e Sincronização Eduardo Nicola F. Zagari
Procedure filosofo;
begin
while TRUE do
begin
Pensando;
P(lugar);
Comendo;
V(lugar);
end;
end;
Program Tentativa_Dois; var lugar: semaforo;
Begin lugar := 1;
Cobegin
filosofo;
filosofo;
filosofo;
filosofo;
filosofo;
Coend;
End.
73 Comunicação e Sincronização Eduardo Nicola F. Zagari
Tentativa Dois: proteção através de semáforo binário antes de começar a comer
demais filósofos ficam esperando o primeiro liberar o pauzinho
Na prática, não funciona a cada instante, apenas um filósofo senta-se à mesa para comer
sub-utilização dos recursos do sistema
74 Comunicação e Sincronização Eduardo Nicola F. Zagari
Procedure filosofo(i:integer)
begin
while TRUE do begin
Pensando;
P(lugar);
P(pauzinho[i]);
P(pauzinho[(i+1)mod N]);
Comendo;
V(pauzinho[i]);
V(pauzinho[(i+1)mod N]);
V(lugar);
end;
end;
Program Tentativa_Três; var pauzinho: array[1..5] of semaforo; lugar: semaforo, i: integer;
Begin lugar := 4;
for i:= 1 to 5 do
pauzinho[i] := 1;
Cobegin
filosofo(1);
filosofo(2);
filosofo(3);
filosofo(4);
filosofo(5);
Coend;
End.
75 Comunicação e Sincronização Eduardo Nicola F. Zagari
Tentativa Três: no máximo quatro filósofos sentam-se à mesa ao mesmo tempo
Para 5 garfos disponíveis, devemos permitir que dois filósofos comam ao mesmo tempo
Não funciona apenas um irá comer por vez (posse de dois pauzinhos)
Solução não controlar os recursos, mas os estados dos filósofos...
76 Comunicação e Sincronização Eduardo Nicola F. Zagari
Procedure filosofo(i:integer)
begin
while TRUE do begin
Pensando;
Pega_pauzinho(i);
Comendo;
Coloca_pauzinho(i);
end;
end;
Program Solução; Cons N = 5; Pensando = 0; Com_fome = 1; Comendo = 2; Type situacao = [Pensando, Com_fome, Comendo]; var pode_sentar: array[1..5] of semaforo; estado: array[1..5] of situacao; mutex: semaforo; i: integer;
Begin mutex := 1; for i:= 1 to 5 do begin estado[i] := Pensando; pode_sentar[i] := 0; end;
Cobegin
filosofo(1); filosofo(2);
filosofo(3); filosofo(4);
filosofo(5);
Coend;
End.
77 Comunicação e Sincronização Eduardo Nicola F. Zagari
Procedure pega_pauzinho(i:integer);
begin
P(mutex);
estado[i] := Com_fome;
testa(i);
V(mutex);
P(pode_sentar[i]);
end;
Procedure testa(i:integer); begin
if (estado[i]= Com_fome and estado[(i-1)mod N] <> Comendo and estado[(i+1) mod N] <> Comendo) then begin
estado[i] := Comendo;
V(pode_sentar[i]); end; end.
Procedure coloca_pauzinho(i:integer);
begin
P(mutex);
estado[i] := Pensando;
testa((i-1) mod N);
testa((i+1) mod N);
V(mutex);
end;
78 Comunicação e Sincronização Eduardo Nicola F. Zagari
Dados compartilhados Semaphore chopStick[] = new Semaphore[5]; Filósofo i:
while (true) { // get left chopstick chopStick[i].acquire(); // get right chopstick chopStick[(i + 1) % 5].acquire(); eating(); // return left chopstick chopStick[i].release(); // return right chopstick chopStick[(i + 1) % 5].release(); thinking(); }
79 Comunicação e Sincronização Eduardo Nicola F. Zagari
Considere que, no projeto da solução do produtor-consumidor com semáforos, os 2 “downs” tenham sido trocados Se o buffer estiver cheio deadlock
Monitor = coleção de variáveis, estruturas e procedimentos que são agrupados em um módulo ou pacote (tipo abstrato de dados com operações bem definidas para atuarem sobre sua estrutura de dados)
Um monitor é uma abstração de alto nível que provê exclusão mútua
Os procedimentos de um monitor só executam alguma tarefa quando chamados por processos (são passivos) e operam sobre variáveis comuns que constituem sua estrutura de dados
80 Comunicação e Sincronização Eduardo Nicola F. Zagari
Os processos podem chamar procedimentos dos monitores, mas não têm acesso à estrutura interna dos mesmos
Somente um processo pode estar ativo em um monitor em cada instante, sendo assim, definem regiões críticas criando exclusão mútua para a estrutura de dados do monitor
Quando um processo chama um procedimento num monitor, verifica-se se outro processo está ativo no monitor Sim: processo suspenso Não: processo pode entrar na Região Crítica
A sincronização entre processos concorrentes se faz através de operações WAIT e SIGNAL sobre variáveis do tipo evento, definidas dentro do monitor Uma thread (ou procedimento) que invoca x.wait é suspensa até
que outra(o) invoque x.signal Implementação: precisa de suporte da linguagem
81 Comunicação e Sincronização Eduardo Nicola F. Zagari
Somente uma thread (ou procedimento) pode estar ativo dentro do monitor por vez
monitor monitor-name { // variable declarations public entry p1(…) { … } public entry p2(…) { … } }
82 Comunicação e Sincronização Eduardo Nicola F. Zagari
83 Comunicação e Sincronização Eduardo Nicola F. Zagari
procedure producer;
begin
while true do
begin
produce_item;
ProduceConsumer.enter;
end;
end;
procedure consumer;
begin
while true do
begin
ProduceConsumer.remove;
consume_item;
end;
end;
84 Comunicação e Sincronização Eduardo Nicola F. Zagari
monitor ProducerConsumer condition full, empty; integer count;
procedure enter; begin if count = N then wait(full); enter_item; count := count + 1; if count = 1 then signal(empty); end; procedure remove; begin if count = 0 then wait(empty); remove_item; count := count - 1; if count = N - 1 then signal(full); end; count := 0;
end monitor;
85 Comunicação e Sincronização Eduardo Nicola F. Zagari
monitor DiningPhilosophers { int[] state = new int[5]; static final int THINKING = 0; static final int HUNGRY = 1; static final int EATING = 2; condition[] self = new condition[5]; public diningPhilosophers { for (int i = 0; i < 5; i++) state[i] = THINKING; } public entry pickUp(int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) self[i].wait; } // Continued on Next Slide
86 Comunicação e Sincronização Eduardo Nicola F. Zagari
public entry putDown(int i) { state[i] = THINKING; // test left and right neighbors test((i + 4) % 5); test((i + 1) % 5); } private test(int i) { if ( (state[(i + 4) % 5] != EATING) && (state[i] == HUNGRY) && (state[(i + 1) % 5] != EATING) ) { state[i] = EATING; self[i].signal; } } }
87 Comunicação e Sincronização Eduardo Nicola F. Zagari
Os mecanismos anteriores permitem comunicação indireta O mecanismo de troca de mensagens entre um processo gerente,
destinado a disciplinar a utilização de determinado recurso, e os processos concorrentes pode ser usado para se obter exclusão mútua
Duas primitivas -não há compartilhamento de memória SEND(destino, msg)
RECEIVE(fonte, &msg)
– Caso não exista mensagem, o processo pode ser bloqueado até a existência de uma
destino: processo destinatário (ex.: PID) msg: mensagem que se quer enviar
fonte: processo que enviou msg: mensagem retornada, se existir
88 Comunicação e Sincronização Eduardo Nicola F. Zagari
Identificação dos processos: processo@máquina Nomes de máquinas iguais domínio (proc@máq.domínio)
O armazenamento e a sincronização são feitos pelas primitivas, ou seja, pelo SO, que é o responsável
A mensagem pode se perder na rede: mensagem de reconhecimento (acknowledgement)
se ACK não chegar, retransmite e se a msg chega, mas o ACK se perde?
fonte envia msg novamente no de identificação de msg
Confiabilidade autenticação Se transmissor e receptor estão na mesma máquina, o
rendimento cai pelo excesso de cópias de dados
89 Comunicação e Sincronização Eduardo Nicola F. Zagari
void producer(void) {
int item;
message m;
while (TRUE) {
produce_item(&item);
receive(cons, &m);
build_msg(&m, item);
send(cons, &m);
}
}
void consumer(void) {
int item, i;
message m;
for(i=0; i<N; i++)
send(prod, &m);
while (TRUE) {
receive(prod, &m);
extract_msg(&m,item);
send(prod, &m);
consume_item(item);
}
}
#include “prototypes.h” #define N 100 #define MSIZE 4 typedef int message[4];