Capítulo 6: Sincronização de Processos Operating System Concepts – 8th Edition Silberschatz, Galvin and Gagne ©2009 Background Acesso concorrente a um dado compartilhado pode resultar em inconsistência Para manter a consistência, é preciso garantir uma execução ordenada dos processos cooperativos (que estão interagindo por meio de uma estrutura compartilhada) Estudo da concorrência entre processos através de modelos Exemplo: Produtor-consumidor Operating System Concepts – 8th Edition 6.2 Silberschatz, Galvin and Gagne ©2009 Problema do Produtor-Consumidor Produtor e consumidor são dois processos distintos O produtor produz e coloca o produto no buffer O consumidor consome produtos que estão no buffer Consequências O buffer passa a ser uma estrutura compartilhada O produtor não pode colocar um produto em um buffer cheio O consumidor não pode consumir um produto se o buffer estiver vazio A variável compartilhada count pode ser usada para saber o número de elementos no momento dentro do buffer Eventualmente, o count pode ser atualizado simultaneamente pelo produtor e pelo consumidor – Atualização não é uma tarefa atômica (indivisível) Operating System Concepts – 8th Edition 6.3 Silberschatz, Galvin and Gagne ©2009 Produtor def produtor(): seed() global count i=0 while (True): sleep(random())#Tempo para simular o processo produzindo a informação if count<BUFFER_SIZE: buffer.append(str(i)) count = count +1 i=i+1 else: while (count>=BUFFER_SIZE): pass Operating System Concepts – 8th Edition 6.4 Silberschatz, Galvin and Gagne ©2009 Consumidor def consumidor(): seed() global count while (True): if count>0: buffer.pop(0) count = count -1 sleep(random()) #Tempo para simular o processo consumindo a informação else: while(count<=0): pass Operating System Concepts – 8th Edition 6.5 Silberschatz, Galvin and Gagne ©2009 Condição de disputa O incremento do contador pode ser implementado da seguinte forma: register1 = counter register1 = register1 + 1 counter = register1 O decremento do contador, por sua vez, seria: register2 = counter register2 = register2 - 1 count = register2 Suponha que, no momento, count=5 e que o produtor e o consumidor estão sendo executados: t0: produtor: register1 = counter {register1 = 5} t1: produtor: register1 = register1 + 1 {register1 = 6} t2: consumidor: register2 = counter {register2 = 5} t3: consumidor: register2 = register2 - 1 {register2 = 4} t4: produtor: counter = register1 {count = 6 } t5: consumidor: counter = register2 {count = 4} Operating System Concepts – 8th Edition 6.6 Silberschatz, Galvin and Gagne ©2009 Condição de disputa Definição Situação em que várias tarefas acessam dados concorrentemente e o resultado da execução depende da ordem específica em que o acesso ocorre Solução para a condição de disputa: Sincronização de processos Proteção das seções críticas Uma seção crítica (ou região crítica) é um trecho de código no qual a tarefa está alterando uma estrutura compartilhada (variáveis compartilhadas, tabelas, arquivos, etc.) – Dessa forma, se um processo entra na sua seção crítica, nenhum outro processo que compartilhe alguma estrutura pode ser autorizado a entrar na sua seção crítica Operating System Concepts – 8th Edition 6.7 Silberschatz, Galvin and Gagne ©2009 Problema da seção crítica Problema de como controlar o acesso às seções críticas de processos que compartilham dados Ideia: Criar um protocolo de acesso Processo pede autorização para entrar na seção crítica Quando autorizado, processo executa seção crítica Processo avisa que terminou sua seção crítica Processo continua a sua execução Operating System Concepts – 8th Edition 6.8 Silberschatz, Galvin and Gagne ©2009 Problema da seção crítica A entra na região crítica A deixa a região crítica Processo A B tenta entrar na região crítica Processo B B entra na região crítica B deixa a região crítica B bloqueado T1 Operating System Concepts – 8th Edition T3 T2 6.9 T4 Silberschatz, Galvin and Gagne ©2009 Soluções para o problema da região crítica 1. Exclusão mútua Nunca dois processos podem estar simultaneamente em suas regiões críticas 2. Progresso Nenhum processo fora da sua região crítica pode bloquear outros processos 3. Espera limitada Nenhum processo deve esperar eternamente para entrar em sua região crítica Nada pode ser afirmado sobre a velocidade de processamento ou sobre o número de CPUs disponíveis Operating System Concepts – 8th Edition 6.10 Silberschatz, Galvin and Gagne ©2009 Revisão Problema do produtor-consumidor Código em Python Único buffer com 10 posições P produtores C consumidores Problema? A variável count é modificada por vários threads ao mesmo tempo – Condição de disputa: Vários processos disputando ao mesmo tempo o acesso às mesmas variáveis Inconsistência no valor Alguma diferença entre o uso de processos ou threads? Usando processos fica mais pesado Usando processos tem que usar memória compartilhada – Python já tem certo controle sobre essas variáveis Operating System Concepts – 8th Edition 6.11 Silberschatz, Galvin and Gagne ©2009 Revisão Qual é/são as seções críticas do código? Uso da variável count Uso da variável buffer Consumidor def consumidor(): seed() global count while (True): if count>0: buffer.pop(0) count = count -1 Seção crítica sleep(random()) #Tempo para simular o processo consumindo a informação else: while(count<=0): pass Operating System Concepts – 8th Edition 6.12 Silberschatz, Galvin and Gagne ©2009 Revisão Produtor def produtor(): seed() global count i=0 while (True): sleep(random())#Tempo para simular o processo produzindo a informação if count<BUFFER_SIZE: Seção crítica buffer.append(str(i)) count = count +1 i=i+1 else: while (count>=BUFFER_SIZE): Seção crítica pass Operating System Concepts – 8th Edition 6.13 Silberschatz, Galvin and Gagne ©2009 Revisão Main: if __name__ == '__main__': ###Programa principal print "Esse programa simula o problema do produtor-consumidor, utilizando uma lista compartilhada por n threads: os produtores e os consumidores" print "Está se utilizando o método da espera ocupada." count = 0 prod=20 #Numero de produtores cons= 20 #Numero de consumidores t=[1]*(prod+cons) #Inicio o número de threads for i in range(prod): t[i] = Thread(target=produtor, args=()) t[i].start() for i in range(prod,prod+cons): t[i] = Thread(target=consumidor, args=()) t[i].start() while (True): if (len(buffer) != count): print "ERRO!!!!, pois tamanho do buffer = %d e count = %d" % (len(buffer),count) Seção sleep (0.1) crítica if (len(buffer)>10): print "ERRO!!!!, pois o buffer estourou!!!" Operating System Concepts – 8th Edition 6.14 Seção crítica Silberschatz, Galvin and Gagne ©2009 Revisão Como proteger a seção crítica? Enquanto um produtor está na seção crítica, nenhum outro produtor ou consumidor pode entrar na seção crítica Seção crítica = seção aonde se atualiza ou se compara as variáveis compartilhadas Enquanto um consumidor está na seção crítica, nenhum outro consumidor ou produtor pode entrar na seção crítica Operating System Concepts – 8th Edition 6.15 Silberschatz, Galvin and Gagne ©2009 Soluções para o problema da seção crítica Soluções Solução de Peterson – Software Test and set – Solução por hardware Semáforos e mutexes – Auxílio de hardware Operating System Concepts – 8th Edition 6.16 Silberschatz, Galvin and Gagne ©2009 Solução de Peterson Solução por software para controle de 2 processos Ideia Se eu quero entrar na região crítica, devo dizer isso ao outro programa Eu só posso entrar na seção crítica quando for a minha vez – Se o outro processo não quer entrar na seção crítica, então passa a ser a minha vez automaticamente Eu começo dando a vez para o outro programa, mesmo que eu queira usar – Se dois querem, quem der a vez primeiro para o outro vai passar na frente P2 ganhou vez!!! P1a ganhou a vez!!! Operating System Concepts – 8th Edition P1 P2 Se P1 começa na frente: Se P2 começa na frente: VARIÁVEL VEZ 1 2 6.17 Silberschatz, Galvin and Gagne ©2009 Solução de Peterson Código para Processo 0 Se quero entrar, sinalizo isso Dou a vez para o outro para ver se eu ganho a vez...:) Enquanto(1){ eu_quero_entrar[0]=1 vez = 1 enquanto (eu_quero_entrar[1]==1 e vez==1){ Não faço nada Quando terminar a seção crítica, aviso que não quero mais entrar } Executo seção crítica eu_quero_entrar[0]=0 Quando eu não quiser mais entrar, eu paro o enquanto do outro processo e deixo ele entrar na seção crítica } Operating System Concepts – 8th Edition Se eu quero entrar e a vez é minha, eu pulo o enquanto e vou para seção crítica 6.18 Silberschatz, Galvin and Gagne ©2009 Solução de Peterson Código para Processo 0 Se quero entrar, sinalizo isso Dou a vez para o outro para ver se eu ganho a vez...:) Enquanto(1){ eu_quero_entrar[0]=1 vez = 1 enquanto (eu_quero_entrar[1]==1 e vez==1){ Não faço nada Quando o outro programa terminar a seção crítica, ele não vai mais querer entrar e eu saio do enquanto. } Executo seção crítica eu_quero_entrar[0]=0 } Se eu quero entrar e a vez não é minha, eu espero até a vez passar a ser minha Depois que eu executar minha seção crítica, eu digo que não quero mais entrar. A variável vez só serve para distinguir quem vai entrar se tiver disputa, ou seja, mais de um processo querendo entrar na seção crítica Operating System Concepts – 8th Edition 6.19 Silberschatz, Galvin and Gagne ©2009 Solução de hardware Como implementar o lock? Como permitir que os programas entrem na sua seção crítica segundo uma fila? do { acquire lock Fácil de usar.... critical section Mas como funciona??? release lock remainder section test and set ou swap!!! } while (TRUE); - Exemplo: ex_threadv2 e ex-thread-lock.py Operating System Concepts – 8th Edition 6.20 Instruções atômicas (vem daquela ideia antiga de que o átomo era indivisível) (do grego: a=não, tomo=divisão) Silberschatz, Galvin and Gagne ©2009 Solução de hardware Sem me preocupar com a ordem dos processos, usando test and set lock (TSL) TSL(x): Se x==0, então x=1 e retorna 0 Se x==1, então x=1 e retorna 1 do { acquire lock critical section release lock while(TestAndSet(&lock)) ; remainder section } while (TRUE); lock=0 A TSL é uma instrução atômica. Então, apenas 1 processo pode executá-la, mesmo que existam vários processadores. Assim, apenas 1 processo pode aproveitar o lock=0. Então, se lock =0, pode usar. Se lock =1, não pode usar, pois fica preso no while Operating System Concepts – 8th Edition 6.21 Silberschatz, Galvin and Gagne ©2009 Solução de hardware Sem me preocupar com a ordem dos processos, usando swap swap(x,y): x recebe o valor de y e y recebe o valor de x. É literalmente uma troca. do { acquire lock critical section key=true; while(key==true) swap(lock,key); release lock remainder section } while (TRUE); lock=0 Então, se lock =0, key vira false, lock vira 1 e o processo sai do while. Se lock =1, não pode usar, pois key=lock=1 e o processo fica preso no while Operating System Concepts – 8th Edition 6.22 A swap também é uma instrução atômica. Então, apenas 1 processo pode executá-la, mesmo que existam vários processadores. Assim, apenas 1 processo pode aproveitar o lock=0. Silberschatz, Galvin and Gagne ©2009 Solução de hardware Me preocupando com a ordem dos processos, usando test and set do { acquire lock critical section release lock Em todos os exemplos anteriores, não é possível garantir que algum processo não vai ficar esperando eternamente. O mais rápido pega o lock, e não o processo que está esperando a mais tempo. remainder section } while (TRUE); Operating System Concepts – 8th Edition 6.23 Silberschatz, Galvin and Gagne ©2009 Solução de hardware Me preocupando com a ordem dos processos, usando test and set TSL(x): Se x==0, então x=1 e retorna 0 Se x==1, então x=1 e retorna 1 do { acquire lock critical section release lock remainder section } while (TRUE); waiting[i]=true; key=true; while(waiting[i]==true)&(key==true) key = TestAndSet(&lock) Waiting[i]=false; Se lock=0, então lock=1 e key=0. Isso faz o processo sair do while. Uma vez que o processo ganhou o acesso, ele não espera mais para entrar na seção crítica. Se lock=1, então lock=key=1. O programa fica preso no while. Operating System Concepts – 8th Edition 6.24 Silberschatz, Galvin and Gagne ©2009 Solução de hardware Me preocupando com a ordem dos processos, usando test and set TSL(x): Se x==0, então x=1 e retorna 0 Se x==1, então x=1 e retorna 1 do { acquire lock critical section release lock remainder section waiting[i]=true; key=true; while(waiting[i]==true)&(key==true) key = TestAndSet(&lock) Waiting[i]=false; } while (TRUE); j = (i + 1) % n; Escolhe o próximo processo que está while ((j != i) && !waiting[j]) esperando para entrar j = (j + 1) % n; if (j == i) Se ninguém está esperando, libera o lock lock = FALSE; para qualquer um else Do contrário, o próximo processo esperando waiting[j] = FALSE; pode entrar na região crítica Silberschatz, Galvin and Gagne ©2009 Operating System Concepts – 8th Edition 6.25 Semáforos Signal e wait Operações atômicas na modificação do semáforo Modificam o valor do sinal int sinal=1; wait(sinal); Região crítica; wait (S) { while S <= 0 ; // no-op S--; } Se S=1, decrementa S e pode entrar. Caso contrário, fica esperando em espera ocupada signal (sinal); Espera ocupada = espera usando CPU signal (S) { S++; } Operating System Concepts – 8th Edition 6.26 Silberschatz, Galvin and Gagne ©2009 Semáforos Signal e wait Operações atômicas na modificação do semáforo Modificam o valor do sinal int sinal=1; wait (S) { while S <= 0 ; // no-op S--; } wait(sinal); Região crítica; signal (sinal); S pode ser apenas 1 ou 0. S é um MUTEX, pois permite exclusão mútua (apenas 1 pode executar). signal (S) { S++; } Operating System Concepts – 8th Edition 6.27 Silberschatz, Galvin and Gagne ©2009 Semáforos Signal e wait + block e wakeup int sinal=1; wait(sinal); wait(semaphore *S) { S->value--; if (S->value < 0) { add this process to S->list; block(); } } Região crítica; S pode variar entre –infinito e 1. Se S=1, o processo acessa a região crítica. signal (sinal); Permite fazer a fila de processos esperando. Operating System Concepts – 8th Edition signal(semaphore *S) { S->value++; if (S->value <= 0) { remove a process P from S->list; wakeup(P); } } 6.28 Silberschatz, Galvin and Gagne ©2009 Semáforos Signal e wait + block e wakeup int sinal=1; wait(semaphore *S) { S->value--; if (S->value < 0) { add this process to S->list; block(); } } wait(sinal); Não tem espera ocupada! Região crítica; signal (sinal); Operating System Concepts – 8th Edition signal(semaphore *S) { S->value++; if (S->value <= 0) { remove a process P from S->list; wakeup(P); } } 6.29 Silberschatz, Galvin and Gagne ©2009 Exemplos Semáforos em python Thread-lock-semaphore.py Operating System Concepts – 8th Edition 6.30 Silberschatz, Galvin and Gagne ©2009 Exercícios Quando ocorre condição de disputa? Qual a relação entre seção crítica e condição de disputa? Uma solução para o problema da seção crítica feita por software para quando existem no máximo 2 processos disputando o acesso a seção crítica. Como funcionam as soluções de sincronismo baseadas em hardware? A seção crítica é a parte do código na qual ocorrem os acessos às variáveis compartilhadas, onde, eventualmente, podem ocorrer condições de disputa. O que é a solução de Peterson? Quando dois ou mais processos tentam utilizar a mesma variável compartilhada ao mesmo tempo, de tal forma que o valor final da variável dependa da ordem específica com a qual os acessos ocorrem. Através do uso de instruções atômicas que auxiliam a exclusão mútua, tais como a test & set e a swap. Qual a principal diferença funcional entre locks e semáforos? Os locks permitem criar apenas a exclusão mútua, enquanto que os semáforos podem ser usados para fazer a exclusão mútua ou para selecionar um número de eventos que podem ocorrer em paralelo. Operating System Concepts – 8th Edition 6.31 Silberschatz, Galvin and Gagne ©2009 Deadlock e Inanição Deadlock – Acontece quando dois ou mais processos estão esperando por um evento que só poderia ser executado por um dos processos em espera Exemplo Imagine que S e Q são dois semáforos inicializados em 1 P0 P1 wait (S); wait (Q); wait (Q); wait (S); . . . . . . signal (S); signal (Q); Operating System Concepts – 8th Edition signal (Q); signal (S); 6.32 Silberschatz, Galvin and Gagne ©2009 Exercício Crie um programa que crie 2 processos cuja a interação leve a um deadlock. Operating System Concepts – 8th Edition 6.33 Silberschatz, Galvin and Gagne ©2009 Deadlock e Inanição Inanição – Bloqueio indefinido Um processo nunca é retirado da fila de espera por um semáforo Exemplo: Política de escolha de quem recebe o semáforo do tipo LIFO (Last In, First Out) Inversão de prioridades – Problema de escalonamento que ocorre quando um processo de baixa prioridade tem posse de um semáforo compartilhado com processos de alta prioridade Solução: Protocolo de herança de prioridade Todos os processos que compartilham dados com um processo de alta prioridade recebem a mesma prioridade que esse processo Operating System Concepts – 8th Edition 6.34 Silberschatz, Galvin and Gagne ©2009 Problemas clássicos de sincronização Problemas clássicos usados para testar novos esquemas de sincronização propostos Problema do buffer limitado Problema dos leitores-escritores Problema do jantar dos filósofos Operating System Concepts – 8th Edition 6.35 Silberschatz, Galvin and Gagne ©2009 Problema do buffer limitado N buffers, cada um podendo armazenar um item Semáforo mutex inicializado no valor 1 Semáforo full inicializado no valor 0 Semáforo empty inicializado no valor N Operating System Concepts – 8th Edition 6.36 Controla o acesso ao buffer Controla o número de espaços ocupados no buffer Controla o número de espaços vazios no buffer Silberschatz, Galvin and Gagne ©2009 Problema do buffer limitado Estrutura do processo produtor do { // produce an item in nextp Espera ter um vazio para usar e usa Pede acesso ao buffer wait (empty); wait (mutex); // add the item to the buffer Libera o acesso ao buffer signal (mutex); Aumenta o número de espaços ocupados signal (full); } while (TRUE); Operating System Concepts – 8th Edition Não pode trocar a ordem! 6.37 Silberschatz, Galvin and Gagne ©2009 Problema do buffer limitado Espera ter um cheio para usar e usa Estrutura do processo consumidor Pede acesso ao buffer do { wait (full); wait (mutex); // remove an item from buffer to nextc Libera o acesso ao buffer signal (mutex); Aumenta o número de espaços vazios signal (empty); // consume the item in nextc } while (TRUE); Operating System Concepts – 8th Edition 6.38 Silberschatz, Galvin and Gagne ©2009 Exercícios Com base nos códigos do produtor e do consumidor,qual o estado das variáveis compartilhadas ao longo do tempo, se cada processo executa uma linha de código e perde a vez? Dê sua resposta no formato da tabela abaixo. Preencha pelo menos 16 linhas e assuma que o processo produtor foi iniciado antes do processo consumidor. Além disso, suponha que o buffer possui apenas 3 posições. Linha de código Mutex Operating System Concepts – 8th Edition Full Empty 6.39 Buffer[0] Buffer[1] Buffer[2] Silberschatz, Galvin and Gagne ©2009 Exercício Repita o exercício anterior, supondo que o consumidor foi o primeiro processo a ser executado. Repita o exercício anterior, supondo que a operação de produção de item seja 16x mais rápida que a operação de consumo de item. Suponha que o buffer comece com 2 elementos e execute até que o produtor seja bloqueado pela primeira vez. Suponha que as linhas 2 e 3 (wait empty e wait mutex) do código do produtor fossem trocadas e que o buffer seja iniciado cheio. Isso causará algum problema? Suponha que o buffer está cheio. Operating System Concepts – 8th Edition 6.40 Silberschatz, Galvin and Gagne ©2009 Problema dos leitores-escritores Um conjunto de dados é compartilhado entre um número de processos concorrentes Leitores – apenas leem o conjunto de dados; não realizam atualizações Escritores – podem ler e escrever Problema Permitir que múltiplos leitores leiam ao mesmo tempo Apenas um escritor pode acessar os dados compartilhados ao mesmo tempo Existem diversas variações sobre como tratar os leitores e os escritores Todas envolvem prioridades Operating System Concepts – 8th Edition •Solução proposta: nenhum leitor deve esperar que outros leitores terminem só porque existe um escritor esperando prioridade para leitores 6.41 Silberschatz, Galvin and Gagne ©2009 Problema dos leitores-escritores Dados compartilhados Trata do acesso ao readcount Conjunto de dados Semáforo mutex inicializado em 1 Semáforo wrt inicializado em 1 Inteiro readcount inicializado em 0 Exclusão mútua para escritores Número de leitores Operating System Concepts – 8th Edition 6.42 Silberschatz, Galvin and Gagne ©2009 Problema dos leitores-escritores Estrutura do processo escritor Estrutura do processo leitor do { do { wait (wrt) ; // Seção crítica Seção crítica writing is performed wait (mutex) ; readcount ++ ; if (readcount == 1) wait (wrt) ; signal (mutex) signal (wrt) ; // reading is performed } while (TRUE); Seção crítica Operating System Concepts – 8th Edition 6.43 wait (mutex) ; readcount - - ; if (readcount == 0) signal (wrt) ; signal (mutex) ; } while (TRUE); Silberschatz, Galvin and Gagne ©2009 Problema dos leitores-escritores Estrutura do processo escritor Estrutura do processo leitor do { do { wait (wrt) ; // Espera ter acesso ao buffer sozinho writing is performed signal (wrt) ; } while (TRUE); Libera para leitores e escritores wait (mutex) ; readcount ++ ; if (readcount == 1) wait (wrt) ; signal (mutex) // reading is performed wait (mutex) ; readcount - - ; if (readcount == 0) signal (wrt) ; signal (mutex) ; } while (TRUE); Operating System Concepts – 8th Edition 6.44 Silberschatz, Galvin and Gagne ©2009 Problema dos leitores-escritores Estrutura do processo escritor Estrutura do processo leitor do { do { wait (wrt) ; Se ninguém está usando o readcount, posso usar // writing is performed Depois de atualizar, libero o readcount signal (wrt) ; // reading is performed } while (TRUE); Começando pelo mutex readcount Operating System Concepts – 8th Edition wait (mutex) ; readcount ++ ; if (readcount == 1) wait (wrt) ; signal (mutex) Acabei de ler. Se ninguém estiver usando, atualizo o readcount wait (mutex) ; readcount - - ; if (readcount == 0) signal (wrt) ; Libero o readcount signal (mutex) ; } while (TRUE); 6.45 Silberschatz, Galvin and Gagne ©2009 Problema dos leitores-escritores Estrutura do processo escritor Estrutura do processo leitor do { do { Se já não tem alguém wait (wrt) ; lendo, pode ter alguém escrevendo // writing is performed wait (mutex) ; readcount ++ ; if (readcount == 1) wait (wrt) ; signal (mutex) signal (wrt) ; // reading is performed } while (TRUE); Agora o mutex wrt Operating System Concepts – 8th Edition wait (mutex) ; readcount - - ; Se não tem mais if (readcount == 0) leitores, então libera o signal (wrt) ; acesso para escritores signal (mutex) ; e leitores } while (TRUE); 6.46 Silberschatz, Galvin and Gagne ©2009 Variações do problema dos leitoresescritores Primeira variação – nenhum leitor é deixado esperando a menos que o escritor tenha permissão para usar o objeto compartilhado. Segunda variação – uma vez que o escritor está pronto, ele escreve o mais rápido possível Ambos podem levar à inanição, criando ainda mais variações Problema é solucionado em alguns sistemas pelo Kernel, que provê locks para leitorescritor. Operating System Concepts – 8th Edition 6.47 Silberschatz, Galvin and Gagne ©2009 Problema do jantar dos filósofos Filósofos passam suas vidas pensando e comendo Eles não interagem com seus vizinhos Ocasionalmente, eles tentam pegar dois palitos, um de cada vez, para comer da vasilha central. Eles precisam dos dois palitos para comer, mas liberam os dois quando terminam No caso de 5 filósofos Dados compartilhados Vasilha de arroz (conjunto de dados) Semáforos chopstick [5] inicializado em 1 Operating System Concepts – 8th Edition 6.48 Silberschatz, Galvin and Gagne ©2009 Algoritmo do problema do jantar dos filósofos Estrutura do i-ésimo filósofo: do { wait ( chopstick[i] ); wait ( chopStick[ (i + 1) % 5] ); // eat signal ( chopstick[i] ); signal (chopstick[ (i + 1) % 5] ); Possibilidade de deadlock e de inanição // think } while (TRUE); Qual o problema com esse algoritmo? Operating System Concepts – 8th Edition 6.49 Silberschatz, Galvin and Gagne ©2009 Problemas com semáforos Uso incorreto de operações de semáforos: signal (mutex) …. wait (mutex) wait (mutex) … wait (mutex) Omissão do wait (mutex) ou do signal (mutex) (ou de ambos) Deadlock e inanição Operating System Concepts – 8th Edition 6.50 Silberschatz, Galvin and Gagne ©2009 Monitores Uma abstração de alto nível que provê um mecanismo conveniente e eficiente para sincronizar processos Apenas 1 processo pode estar ativo no monitor por vez Contudo, não é poderoso o suficiente para modelar alguns esquemas de sincronização monitor monitor-name { // shared variable declarations procedure P1 (…) { …. } procedure Pn (…) {……} Initialization code (…) { … } } } Operating System Concepts – 8th Edition 6.51 Silberschatz, Galvin and Gagne ©2009 Variáveis condicionais condição x, y; Duas operações em uma variável condicional: x.wait () – o processo que invoca a operação é suspenso até que x.signal () seja chamado x.signal () – resume um dos processos, se houver algum, que invocou o x.wait () Se não existe nenhum x.wait () na variável, então isso não tem nenhum efeito na variável Operating System Concepts – 8th Edition 6.52 Silberschatz, Galvin and Gagne ©2009 Escolha de variáveis condicionais Se o processo P chama x.signal (), com o processo Q no estado x.wait (), o que deve acontecer em seguida? Se Q é resumido, então P deve esperar Opções incluem: Signal and wait – P espera até que Q deixe o monitor ou espere por alguma outra condição Signal and continue – Q espera até que P deixe o monitor ou espera por alguma outra condição Ambos tem prós e contras – Escolha depende da implementação Monitores implementados em Pascal concorrente P executando o signal deixa o monitor imediatamente e Q é resumido Implementado em outras linguagens, incluindo Mesa, C#, Java Operating System Concepts – Implementado no Python com a classe Condition 8th Edition 6.53 Silberschatz, Galvin and Gagne ©2009 Solução para o jantar dos filósofos monitor DiningPhilosophers { enum { THINKING; HUNGRY, EATING) state [5] ; condition self [5]; void test (int i) { if ( (state[(i + 4) % 5] != EATING) && (state[i] == HUNGRY) && (state[(i + 1) % 5] != EATING) ) { state[i] = EATING ; self[i].signal () ; } } void pickup (int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) self [i].wait; } void putdown (int i) { state[i] = THINKING; // test left and right neighbors test((i + 4) % 5); test((i + 1) % 5); } Operating System Concepts – 8th Edition 6.54 initialization_code() { for (int i = 0; i < 5; i++) state[i] = THINKING; } } Silberschatz, Galvin and Gagne ©2009 Solução para o jantar dos filósofos Cada filósofo I chama as operações pickup() e putdown() na seguinte sequência: DiningPhilosophers.pickup (i); EAT DiningPhilosophers.putdown (i); Não tem deadlock, mas a inanição é possível Operating System Concepts – 8th Edition 6.55 Silberschatz, Galvin and Gagne ©2009 End of Chapter 6 Operating System Concepts – 8th Edition Silberschatz, Galvin and Gagne ©2009