Sincronização Lip Sync Sincronização cursor-voz Entre outras mídias.
Sincronização - fenix.tecnico.ulisboa.pt · Não pode haver interblocagem (deadlock). Se alguns...
Transcript of Sincronização - fenix.tecnico.ulisboa.pt · Não pode haver interblocagem (deadlock). Se alguns...
Sincronização
Tipos de Sincronização
Competição de vários processos porum recurso
Exclusão mútua
Cooperação entre processosProdutores-consumidoresLeitores-escritoresJantar de filósofos
Exclusão mútua
Problema da ExclusãoMútuaint levantar_dinheiro (ref *conta, int valor)
{if (conta->saldo >= valor) {
conta->saldo = conta->saldo – valor;} else valor = -1return valor;
}
Problema da ExclusãoMútua (à lupa)
As instruções C correspondem normalmente a váriasinstruções máquina
int levantar_dinheiro (ref *conta, int valor){if (conta->saldo >= valor) {
/* conta->saldo = conta->saldo – valor; */mov R1, conta->saldomov R2, valorsub R1, R2mov conta->saldo, R1
} else valor = -1return valor;
}
Problema da ExclusãoMútua (à lupa)
int levantar_dinheiro (ref *conta, int valor)
{if (conta->saldo >= valor) {mov R1, conta->saldomov R2, valor
sub R1, R2mov conta->saldo, R1
} valor = -1;return valor;
}
int levantar_dinheiro (ref *conta, int valor)
{if (conta->saldo >= valor) {mov R1, conta->saldomov R2, valorsub R1, R2mov conta->saldo, R1
} else valor = -1;return valor;
}
P1 P2
Interrupção provocacomutação de processos
Saldo inicial = 100Valor = 50
Saldo inicial = 1002 levantamentos de 50Saldo final = 50!!
SO volta a escalonar P1
Secção crítica(intuitivamente)
int levantar_dinheiro (ref *conta, int valor)
{if (conta->saldo >= valor) {
/* conta->saldo = conta->saldo – valor; */mov R1, conta->saldomov R2, valor
sub R1, R2mov conta->saldo, R3
} else valor = -1return valor;
}
Secção Crítica:não pode serinterrompida
Secção Crítica
Propriedade de exclusão mútua: as instruções de uma região críticade dois ou mais processos nãopodem ser intercaladas emnenhuma execução.
Formato dos programas
loop secção não críticafechar() // pré-protocolosecção críticaabrir() // pós-protocolo
end
PropriedadesUm processo pode bloquear-se na secção não crítica, sem interferir com os outros processos. Não pode haver interblocagem (deadlock). Se alguns processos estão a tentar entrar na região crítica, então pelo menos um deles irá consegui-lo alguma vez no futuro. Não pode haver míngua (starvation). Se um processo inicia o pré-protocolo, então alguma vez no futuro iráconseguir o acesso à região crítica. Na ausência de contenção, se um único processo quiser entrar na região crítica, consegui-lo-á e de uma forma eficiente.
Deadlock, livelock, starvation
Deadlock (interblocagem): Para qualquerexecução, nenhum processo conseguefazer progresso. Todos os processosficam bloqueados à espera uns dos outros.Livelock: Existe pelo menos umaexecução para a qual nenhum processoconsegue fazer progresso.Starvation (míngua): Um dos processosnão consegue fazer progresso, enquantoos outros conseguem.
InterblocagemPropriedade estável: não há forma dos processos saírem da interblocagem. Normalmente é necessário terminar um ou mais processosFácil de detectar (“pára tudo”), impossível de inverterEx: Proc A está à espera de Proc BProc B está à espera de Proc CProc C está à espera de Proc A
A
BC
MínguaPropriedade instável: os processos podem fazer progresso, mas há pelo menos uma combinação de acontecimentos que, se se repetirem consecutivamente, faz com que um deles nunca consiga fazer progressoDifícil de detectar pois qualquer alteração pode fazer com que se “esconda”… mas fica à espera do pior momento para reaparecer (Lei de Murphy)!Ex.: processo nunca é atendido porque outros mais prioritários lhe passam sempre à frente
Soluções para o problema da Secção Crítica
Soluções algorítmicasSoluções com suporte de hardwareSoluções oferecidas pelo Sistema Operativo (com suporte de hardware)
Soluções algorítmicas
O pré-protocolo (fechar()) e o pós-protocolo (abrir()) usam apenasinstruções normais com leitura e escrita na memória
Algoritmo DekkerAlgoritmo PetersenAlgoritmo Lamport (Bakery)
Primeira tentativa
int p1—quer—entrar = FALSE; int p2—quer—entrar = FALSE;
p1_fechar(){
while (p2—quer—entrar);p1—quer—entrar = TRUE;
}
p1_abrir(){
p1—quer—entrar = FALSE; }
p2_fechar(){
while (p1—quer—entrar);p2—quer—entrar = TRUE;
}
p2_abrir(){
p2—quer—entrar = FALSE; }
Não garante exclusão mútua. Porquê ?
Processo 1 Processo 2
Segunda tentativa
int p1—quer—entrar = FALSE; int p2—quer—entrar = FALSE;
p1_fechar(){
p1—quer—entrar = TRUE;while (p2—quer—entrar);
}
p1_abrir(){
p1—quer—entrar = FALSE; }
p2_fechar(){
p2—quer—entrar = TRUE;while (p1—quer—entrar);
}
p2_abrir(){
p2—quer—entrar = FALSE; }
Interblocagem. Porquê ?
Processo 1 Processo 2
Terceira tentativa
int p1—quer—entrar = FALSE; int p2—quer—entrar = FALSE;
p1_fechar(){
p1—quer—entrar = TRUE;while (p2—quer—entrar);
}
p1_abrir(){
p1—quer—entrar = FALSE; }
p2_fechar(){
p2—quer—entrar = TRUE;while (p1—quer—entrar) {
p2-que-entrar = FALSE;while(p1—quer—entrar);p2—quer—entrar = TRUE;
}}
p2_abrir(){
p2—quer—entrar = FALSE; }
Míngua. Porquê ?
Processo 1 Processo 2
Quarta tentativa
int proc—prio = 1;
p1_fechar(){
while (proc-prio == 2); }
p1_abrir(){
proc-prio = 2; }
p2_fechar(){
while (proc-prio == 1); }
p2_abrir(){
proc-prio = 1;}
Não garante nem a 1º nem a 4ª propriedade. Porquê ?
Processo 1 Processo 2
Algoritmo Dekkerint p1—quer—entrar = FALSE; int p2—quer—entrar = FALSE; int proc—prio = 1; p1_fechar(){
p1—quer—entrar = TRUE; a1: while (p2—quer—entrar) {a2: if (proc—prio == 2) {
p1—quer—entrar = FALSE; while (proc—prio == 2) ;
p1—quer—entrar = TRUE; }
}}p1_abrir(){
p1—quer—entrar = FALSE; proc—prio = 2;
}
p2_fechar() {
p2—quer—entrar = TRUE; a1: while (p1—quer—entrar) {a2: if (proc—prio == 1) {
p2—quer—entrar = FALSE; while (proc—prio == 1) ;
p2—quer—entrar = TRUE; }
}}p2_abrir()
{ p2—quer—entrar = FALSE; proc—prio = 1;
}
Algoritmo Dekkerint p1—quer—entrar = FALSE; int p2—quer—entrar = FALSE; int proc—prio = 1; p1_fechar(){
p1—quer—entrar = TRUE; a1: while (p2—quer—entrar) {a2: if (proc—prio == 2) {
p1—quer—entrar = FALSE; while (proc—prio == 2) ;
p1—quer—entrar = TRUE; }
}}p1_abrir(){
p1—quer—entrar = FALSE; proc—prio = 2;
}
p2_fechar() {
p2—quer—entrar = TRUE; a1: while (p1—quer—entrar) {a2: if (proc—prio == 1) {
p2—quer—entrar = FALSE; while (proc—prio == 1) ;
p2—quer—entrar = TRUE; }
}}p2_abrir()
{ p2—quer—entrar = FALSE; proc—prio = 1;
}
•P1 anuncia que quer entrar•Verifica se P2 quer entrar. Se não quiser, P1 entra
•Se P2 quer entrar, mas P1 for o mais prioritário, P2 há-de retirar a intenção de entrar e P1 entra
•Senão, P1 indica que não quer entrar (para deixarP2 entrar) e fica em espera activa qté que P2 saia
•Ao sair, indica que já não está na secção crítica
•O algoritmo é totalmente simétrico, P2 fazexactamente o mesmo que P1 (basta trocar o “1”pelo “2” e vice-versa)
•P1 volta a anunciar que quer entrar e repete o pré-protocolo
Algoritmo de Petersonint p1—quer—entrar = FALSE; int p2—quer—entrar = FALSE; int proc—prio = 1; p1_fechar() {a1: p1—quer—entrar = TRUE; b1: proc—prio = 2; c1: while (p2—quer—entrar&&
proc—prio == 2) ;
}p1_abrir(){
p1—quer—entrar = FALSE; }
p2_fechar() {a2: p2—quer—entrar = TRUE; b2: proc—prio = 1; c2: while (p1—quer—entrar &&
proc—prio == 1) ;
}p2_abrir(){
p2—quer—entrar = FALSE; }
Algoritmo de Petersonint p1—quer—entrar = FALSE; int p2—quer—entrar = FALSE; int proc—prio = 1; p1_fechar() {a1: p1—quer—entrar = TRUE; b1: proc—prio = 2; c1: while (p2—quer—entrar&&
proc—prio == 2) ;
}p1_abrir(){
p1—quer—entrar = FALSE; }
p2_fechar() {a2: p2—quer—entrar = TRUE; b2: proc—prio = 1; c2: while (p1—quer—entrar &&
proc—prio == 1) ;
}p2_abrir(){
p2—quer—entrar = FALSE; }
•P1 anuncia que quer entrar
•P1 estabelece P2 como mais prioritário•Se P2 não quer entrar, P1 entra•Se P2 quer entrar e for o prioritário, P1 fica emespera activa até que P2 saia•Se P1 e P2 quiserem entrar ao mesmo tempo, o último coloca o outro como prioritário
•Ao sair, indica que já não está na secção crítica
Algoritmo de Bakery, N=2int n1 = 0; int n2 = 0; p1_fechar() {
n1 = 1; n1 = n2+1; while (n2 && n1 > n2) ;
}p1_abrir(){
n1 = 0; }
p2_fechar() {
n2 = 1; n2 = n1+1; while (n1 && n2 >= n1) ;
}p2_abrir(){
n2 = 0; }
Mais do que 2 processosAlgoritmo de Bakery (Lamport)
Cada processo que quer entrar num secção crítica escolhe um inteiro, maior do que qualquer outro inteiro escolhido.O processo com o menor número inteiro entra, os outros esperam.Os empates são resolvido pela identificação do processo
Garante exclusão mútuaGarante todas as propriedades enunciadas anteriormenteFavorece ligeiramente os processo com menor identificador.
Algoritmo de Bakeryint choosing[N]; // Inicializado a FALSEint ticket[N]; // Inicializado a 0fechar(int i) {
int j; choosing[i] = TRUE; ticket[i] = 1 + maxn(ticket); choosing[i] = FALSE;
for (j=0; j<N; j++) {if (j==i) continue; while (choosing[j]) ;
while (ticket[j] && (ticket[j] < ticket[i])||(ticket[i] == ticket[j] && j < i))) ;
}}abrir(int i){
ticket[i] = 0; }
•Pi verifica se tem a menor senha de todos os Pj
•Se Pj estiver a escolher uma senha, espera que termine
•Neste ponto, Pj ou já escolheu uma senha, ou ainda não escolheu
•Se escolheu, Pi vai ver se é menor que a sua•Se não escolheu, vai ver a de Pi e escolher uma senha maior
•Pi indica que está a escolher a senha•Escolhe uma senha maior que todas as outras•Anuncia que escolheu já a senha
•Se o ticket de Pi for menor, Pi entra•Se os tickets forem iguais, entra o que tiver o menor identificador
Conclusões sobre as Soluções Algorítmicas
Complicadas de usar directamente porprogramasNão funcionam dentro do sistema operativo (ex: rotinas de interrupção)Só contemplam espera activa, sendo muito ineficientes em esperas prolongadas (ex: esperar que uma tecla seja premida)Solução: Introduzir instruções hardware para facilitar a solução
Soluções com suporte do hardware
O pré-protocolo e pós-protocolousam instruções especiais oferecidaspelos processadores para facilitar a implementação da sincronização
Inibição de interrupçõesExchange (xchg no Intel IA)Test-and-set (cmpxchg no Intel IA)
Exclusão mútua com inibição de interrupções
int mascara;fechar(){
mascara = mascarar_int();}abrir(){
repoe_int(m);}
•As interrupções são mascaradas(inibidas) na entrada da secção crítica
•Durante a secção crítica, como não há forma de interromper o processo, é garantidoque as instruções se executam atomicamente•Este mecanismo só pode ser utilizado dentro do sistema operativo em secçõescríticas de muito curta duração
•Inibição das interrupções impede que se executem serviços de sistema (I/O, etc)•Se o programa se esquecer de chamar abrir(), as interrupções ficam inibidas e o sistema fica parado
•Não funciona em multiprocessadores
•Na saída da secção crítica é reposta a máscara de interrupções que existia antes da secção crítica
O problema da atomicidadecom multiprocessadores
CPU CPU CPU
Cache Cache Cache Cache
Memoria IO
bus
CPU
P2 na secção críticaERRO!!
P1 na secção críticaERRO!!
Instante 3
P2 inibe as suas interrupções e entra na secção crítica
Instante 2
P1 inibe as suas interrupções e entra na secção crítica
Instante 1
P2P1
Exclusão mútua com exchangeint trinco = FALSE;
fechar() {
li R1, trinco; guarda em R1 o endereço do trincoli R2, #1
l1: exch R2, 0(R1); faz o exchangebnez R2, l1 ; se não estava a 0 entra, senão repete
}
abrir(){
trinco = FALSE;
}
Exclusão mútua com exchangeint trinco = FALSE;
fechar() {
li R1, trinco; guarda em R1 o endereço do trincoli R2, #1
l1: exch R2, 0(R1); faz o exchangebnez R2, l1 ; se não estava a 0 entra, senão repete
}
abrir(){
trinco = FALSE;
}
•O exchange corresponde às seguintes operaçõesexecutadas de forma atómica
•lw Rt, 0(R1)•sw 0(R1), R2•mov R2, Rt
•A atomicidade é conseguida mantendo o bus trancado entre o Load e o Store
•R2 contém o valor que estava no trinco•Se era 0, o trinco estava livre
•O processo entra•O exchange deixou o trinco trancado
•Se não era 0, significa que o trinco estavatrancado
•O processo fica em espera activa até queencontre o trinco aberto
Exchange emmultiprocessadores
CPU CPU CPU
Cache Cache Cache Cache
Memoria IO
bus
CPU
P2 verifica que o trinco estátrancado e fica em espera activa
P1 entra secção críticaInstante 3
P2 tenta fazer exchange masbloqueia-se a tentar obter o bus
P1 completa exchange e tranca a secção crítica
Instante 2
P1 inicia exchange e tranca o busInstante 1
P2P1
Exclusão mútua com test-and-setint trinco = FALSE;
fechar() {
li R1, trinco; guarda em R1 o endereço do trincol1: tst R2, 0(R1); faz o test-and-set
bnez R2, l1 ; se não estava set entra, senão repete}abrir(){
trinco = FALSE; }
Exclusão mútua com test-and-setint trinco = FALSE;
fechar() {
li R1, trinco; guarda em R1 o endereço do trincol1: tst R2, 0(R1); faz o test-and-set
bnez R2, l1 ; se não estava set entra, senão repete}abrir(){
trinco = FALSE; }
•O test-and-set corresponde às seguintes operaçõesexecutadas de forma atómica
•lw R2, 0(R1)•sw 0(R1), #1
•A atomicidade é conseguida mantendo o bus trancado entre o Load e o Store
•R2 contém o valor que estava no trinco•Se era 0, o trinco estava livre
•O processo entra•O test-and-set deixou o trinco trancado
•Se não era 0, significa que o trinco estavatrancado
•O processo fica em espera activa até queencontre o trinco aberto
Conclusões sobre as Soluçõescom Suporte do Hardware
Oferecem os mecanismos básicos para a implementação da exclusão mútua, mas...Não podem ser usadas directamente por programas em modo utilizador (ex: inibição de interrupções)Só contemplam espera activa, sendo muito ineficientes em esperas prolongadas (ex: esperar que uma tecla seja premida)
Soluções oferecidas peloSistema Operativo
Analisando as soluções anteriores (algorítmica e com suporte de hardware), conclui-se que…… tem que haver uma solução melhor para osprogramadores!Solução:
O Sistema Operativo oferece objectos de sincronização• Trinco Lógico: secções críticas• Semáforo: sincronização genérica
Programas utilizador sincronizam-se usando apenas Trincos Lógicos e Semáforos
• Interface simples• Semântica clara• Implementação eficiente e segura, assegurada pelo sistema
operativoA implementação dos objectos de sincronização no Sistema Operativo usa um misto das soluções algorítmicas e do suporte de hardware
Trinco LógicoObjecto do Sistema Operativo para a implementação de secções críticas em programas utilizadorOferece duas operações:
fechar (trinco): chamada pelo programa quando quer entrar na secção crítica
• Se a secção crítica estiver aberta, o processo entra na secção crítica e fecha-a
• Se a secção crítica estiver fechada, o processo bloqueia-se atéela ser aberta; nessa altura, entra na secção crítica e fecha-a
abrir (trinco): Chamada pelo programa quando quer sair da secção crítica
• Abre a secção crítica• Se houver processos bloqueados na secção crítica, acorda um
Trinco lógico
O trinco lógico serve exclusivamente para implementar exclusão mútuaUm processo só pode fazer abrir(t) se tiver previamente feitofechar(t)
O processo que faz abrir(t) tem que ser o mesmo que fez fechar(t)
Um trinco é criado sempre no estado ABERTO
No início da secção crítica, osprocessos têm que chamarfechar(t)Se o trinco estiver FECHADO, o processo espera que ele fiqueaberto. Quando ficar ABERTO, passa-o ao estado FECHADO. Estas operações executam-se atomicamente.
No fim da secção crítica, osprocessos têm que chamarabrir(t)Passa o trinco para o estadoABERTO e desbloqueia um processo que esteja à suaespera em fechar()
SecçãoCrítica:
trinco_t t = ABERTO;
int levantar_dinheiro (ref *conta, int valor){
fechar(t);if (conta->saldo >= valor) {conta->saldo = conta->saldo – valor;
} else valor = -1abrir(t);return valor;
}
Diagrama de Estado dos Processos
ExecuçãoEm
Executável Bloqueado
Seleccionado pelo Despacho
Retirado pelo Despacho
Bloqueado num Trinco Lógico
Desbloqueado
Cooperação entre Processos
Cooperação entre Processos
Vários processos executam em conjunto uma ou mais tarefas, nas quais
Competem por recursosIndicam uns aos outros a:• Ausência/existência de recursos• Ocorrência de acontecimentos
Exemplo de Cooperação entreProcessos: produtor - consumidor
/* ProdutorConsumidor com semaforos */ int buf[N]; int prodptr=0, consptr=0; produtor() {
while(TRUE) {int item = produz(); buf[prodptr] = item; prodptr = (prodptr+1) % N;
}}
consumidor() {
while(TRUE) {int item; item = buf[consptr]; consptr = (consptr+1) % N; consome(item);
}}
Produtor
Produtor
Consumidor
Consumidor
prodptrconsptr
Que acontece se não houveritens no buffer ?
Que acontece se o buffer estiver cheio ?
SemáforosObjecto do sistema operativo para sincronizaçãogenéricaUm semáforo é conceptualmente composto por
ContadorFila de processos bloqueados no semáforo
Primitivascriar_semaforo(): cria um semáforo e inicializa o contadoresperar(s): bloqueia o processo chamador se o contador for menor ou igual a zero; senão decrementa o contadorassinalar(s): se houver processos bloqueados, liberta um; senão, incrementa o contador
Todas as primitivas se executam atomicamenteesperar() e assinalar() podem ser chamadas porprocessos diferentes.
Diagrama de Estado dos Processos
ExecuçãoEm
Executável Bloqueado
Seleccionado pelo Despacho
Retirado pelo Despacho
Bloqueado num semáforo
Desbloqueado
Semáforos – Estrutura de Dados
Semáforo s
Tabela deSemáforos do Sistema
Fila de processos bloqueados
Contador
Descritores dos processosbloqueados no semáforo s
SemáforosExistem muitas variantes de semáforo
Genérico: assinalar() liberta um processo qualquerda filaFIFO: assinalar() liberta o processo que se bloqueouhá mais tempoSemáforo com prioridades: o processo especifica emesperar() a prioridade, assinalar() liberta osprocessos por ordem de prioridadesSemáforo com unidades: as primitivas esperar() e assinalar() permitem especificar o número de unidades a esperar ou assinalar
Semáforos: especificação do semáforo genérico
typedef struct {int contador;queue_t fila_procs;
} semaforo_t;
esperar
contador > 0
contador-- bloqueia processo
S N
assinalar
processosbloqueados
desbloqueia processo contador++
S N
criar_semaforo(n)
cria estrutura dados
contador ← n
Exclusão Mútua com Semáforossemaforo_t sem = 1;
int levantar_dinheiro (ref *conta, int valor)
{esperar(sem);if (conta->saldo >= valor) {
conta->saldo = conta->saldo – valor;} else valor = -1
assinalar(sem);return valor;
}
No início da secção crítica, todosos processos têm que chamaresperar()
No fim da secção crítica, todosos processos têm que chamarassinalar()
Um semáforo para exclusãomútua tem que ser inicializadocom 1 unidade
O semáforo é mais genérico que o trinco lógico, por isso pode ser usadopara garantir exclusão mútuaMas…
Mais ineficiente que o trinco lógicoO programador tem que garantir o uso simétrico de esperar() e assinalar()
Produtor-Consumidor com Semáforos
produtor() {
while(TRUE) {int item = produz();esperar(pode_prod);fechar(trinco);buf[prodptr] = item; prodptr = (prodptr+1) % N;abrir(trinco);assinalar(pode_cons);
}}
consumidor() {
while(TRUE) {int item;esperar(pode_cons);fechar(trinco);item = buf[consptr]; consptr = (consptr+1) % N; abrir(trinco);assinalar(pode_prod);consome(item);
}}
Produtor
Produtor
Consumidor
Consumidor
prodptrconsptrint buf[N]; int prodptr=0, consptr=0;trinco_t trinco;semaforo_t pode_prod = criar_semaforo(N),
pode_cons = criar_semaforo(0);
Produtor-Consumidor com Semáforos
produtor() {
while(TRUE) {int item = produz();esperar(pode_prod);fechar(trinco);buf[prodptr] = item; prodptr = (prodptr+1) % N;abrir(trinco);assinalar(pode_cons);
}}
consumidor() {
while(TRUE) {int item;esperar(pode_cons);fechar(trinco);item = buf[consptr]; consptr = (consptr+1) % N; abrir(trinco);assinalar(pode_prod);consome(item);
}}
Produtor
Produtor
Consumidor
Consumidor
prodptrconsptrint buf[N]; int prodptr=0, consptr=0;trinco_t trinco;semaforo_t pode_prod = criar_semaforo(N),
pode_cons = criar_semaforo(0);
Cada semáforo representa um recurso:•pode_produzir: espaços livres, inicialmente N•pode_consumir: itens no buffer, inicialmente 0
Espera que haja 1 item no buffer
Assinala que há 1 espaço livre no buffer
Espera que haja 1 espaço livre no buffer
Assinala que há 1 item no buffer
Sincronização GenéricaInclui
Secções críticasCooperação entre processos
Mecanismos de programaçãoTrincos Lógicos para as secções críticasSemáforos para a cooperação entre processos
Problemas de Sincronização:Produtor-ConsumidorLeitores-EscritoresJantar dos Filósofos
Metodologia de Resolução de Problemas de Sincronização
Cooperação entre processos1. Identificar os recursos (condições,
acontecimentos) partilhados entre processos2. Associar um semáforo a cada recurso
(condição, acontecimento)3. Inicializar o semáforo com o número de
recursos inicialmente existentes4. Chamar esperar(s) quando se tem que
garantir a existência do recurso associado ao semáforo s
5. Chamar assinalar(s) quando se produziu um recurso associado ao semáforo s
Metodologia de Resolução de Problemas de Sincronização
Gestão de recursossemGest = criar_semaforo(N);
void reservar_recurso(){esperar (semGest);algoritmo
}void libertar_recurso(){
algoritmoassinalar (semGest);
}
Metodologia de Resolução de Problemas de Sincronização
Assinalar acontecimentoProcisemEvent = criar_semaforo(0);void esperar_acontecimento(){
esperar (semEvent);}
Procjvoid assinalar_acontecimento(){
assinalar (semEvent);}
Metodologia de Resolução de Problemas de Sincronização
Secções Críticas1. Identificar as secções críticas
1. Uma secção crítica é uma secção de código onde se lê e/ou escreve uma ou mais variáveis partilhadas por vários processos
2. Associar um trinco lógico a cada uma delas1. A mesma variável partilhada tem que estar
protegida sempre pelo mesmo trinco2. Usam-se trincos diferentes para proteger variáveis
completamente independentes, que podem ser acedidas concorrentemente
3. Chamar fechar(t) no início e abrir(t) no fim de cada secção crítica
Metodologia de Resolução de Problemas de SincronizaçãoExclusão mútuatrinco_t trinco;fechar (trinco);
Secção criticaabrir (trinco);
Ou
mutex = criar_semaforo(1);esperar (mutex);
Secção criticaassinalar (mutex);
Aplicação da Metodologia ao Produtor-Consumidor
Cooperação entre processos1. Recursos: posições livres para os produtores produzirem;
itens no buffer para os consumidores consumirem2. Posições livres: semáforo pode_prod; Itens no buffer:
semáforo pode_cons3. Número inicial de posições livres: N; Número inicial de itens
no buffer: 04. Produtores: têm que garantir a existência de uma posição
livre antes de produzirem => têm que chamar esperar no semáforo correspondente (pode_prod); Consumidores: têm que garantir a existência de um item no buffer antes de o usarem => têm que chamar esperar no semáforo correspondente (pode_cons);
5. Produtores: produzem itens quando os colocam no buffer=> chamam assinalar no semáforo correspondente (pode_cons); Consumidores: produzem posições livres no buffer => chamam assinalar no semáforo correspondente (pode_prod)
Aplicação da Metodologia ao Produtor-Consumidor
Secções críticas1. Variáveis partilhadas pelos vários processos: buffer,
prodptr, consptr2. Proteger o acesso a essas variáveis com o trinco trinco3. Chamar fechar(trinco) antes de as ler ou escrever,
chamar abrir(trinco) depois de as ler ou escrever
Produtor-Consumidor com Semáforos
produtor() {
while(TRUE) {int item = produz();esperar(pode_prod);fechar(trinco);buf[prodptr] = item; prodptr = (prodptr+1) % N;abrir(trinco);assinalar(pode_cons);
}}
consumidor() {
while(TRUE) {int item;esperar(pode_cons);fechar(trinco);item = buf[consptr]; consptr = (consptr+1) % N; abrir(trinco);assinalar(pode_prod);consome(item);
}}
Produtor
Produtor
Consumidor
Consumidor
prodptrconsptrint buf[N]; int prodptr=0, consptr=0;trinco_t trinco;semaforo_t pode_prod = criar_semaforo(N),
pode_cons = criar_semaforo(0);
Cada semáforo representa um recurso:•pode_produzir: espaços livres, inicialmente N•pode_consumir: itens no buffer, inicialmente 0
Espera que haja 1 item no buffer
Assinala que há 1 espaço livre no buffer
Espera que haja 1 espaço livre no buffer
Assinala que há 1 item no buffer
Produtor-Consumidor com Semáforos: erro comum #1
produtor() {
while(TRUE) {int item = produz();esperar(pode_prod);fechar(trinco);buf[prodptr] = item; prodptr = (prodptr+1) % N;abrir(trinco);assinalar(pode_cons);
}}
consumidor() {
while(TRUE) {int item;fechar(trinco);esperar(pode_cons);item = buf[consptr]; consptr = (consptr+1) % N; abrir(trinco);assinalar(pode_prod);consome(item);
}}
int buf[N]; int prodptr=0, consptr=0;trinco_t trinco;semaforo_t pode_prod = criar_semaforo(N),
pode_cons = criar_semaforo(0);
Se não houver itens no buffer (situação inicial):• Consumidor fecha o trinco e bloqueia-se no semáforo (com o trinco fechado)• Outros consumidores bloqueiam-se no trinco
• Os produtores vão tentar produzir (e desbloquear os consumidores) mas encontramo trinco fechado e bloqueiam-se• Interblocagem: os consumidores estão àespera dos produtores, e vice-versa
Interblocagem
fechar (trinco)
fechar (trinco)
Processo 1
assinalar (sem)
esperar (sem)
Processo 2
abrir (trinco)
abrir (trinco)
.
.
.
.
.
.
.
.
.
.
Interblocagem
fechar (trinco)
fechar (trinco)
Processo 1
assinalar (sem)
esperar (sem)
Processo 2
abrir (trinco)
abrir (trinco)
Bloqueado
Bloqueado
Solução: Um processo não se pode bloquear num semáforo dentro de uma secção crítica
Chama esperar fora da secção crítica, ouLiberta a secção crítica, faz esperar e volta a readquirir a secção crítica
Interblocagem
esperar (semA)
esperar (semA)
Processo 1
esperar (semB)
esperar (semB)
Processo 2
assinalar (semA)assinalar (semB) assinalar (semA)
assinalar (semB)
.
.
.
.
.
.
.
.
.
Interblocagem
esperar (semA)
esperar (semA)
Processo 1
esperar (semB)
esperar (semB)
Processo 2
assinalar (semA)assinalar (semB) assinalar (semA)
assinalar (semB)
Expira o quantum do processo
Bloqueado
Bloqueado
Solução: quando um processo precisa de adquirir vários semáforos outrincos, eles têm que ser adquiridos sempre pela mesma ordem
Produtor-Consumidor com Semáforos: erro comum #2
produtor() {
while(TRUE) {int item = produz();esperar(pode_prod);fechar(trinco);buf[prodptr] = item; prodptr = (prodptr+1) % N;abrir(trinco);assinalar(pode_cons);
}}
consumidor() {
while(TRUE) {int item;esperar(pode_cons);fechar(trinco);item = buf[consptr]; consptr = (consptr+1) % N; assinalar(pode_prod);abrir(trinco);consome(item);
}}
int buf[N]; int prodptr=0, consptr=0;trinco_t trinco;semaforo_t pode_prod = criar_semaforo(N),
pode_cons = criar_semaforo(0);
O consumidor assinala que há espaço paraproduzir antes de libertar o trinco
• O produtor vai tentar produzir mas encontra o trinco fechado e bloqueia-se• De seguida executa-se o consumidor que liberta o trinco• Agora o produtor já se pode executar• Não há interblocagem mas existem 2 comutações de processosdesnecessárias
Produtor-Consumidor com Semáforos: versão optimizada
produtor() {
while(TRUE) {int item = produz();esperar(pode_prod);fechar(trinco_prods);buf[prodptr] = item; prodptr = (prodptr+1) % N;abrir(trinco_prods);assinalar(pode_cons);
}}
consumidor() {
while(TRUE) {int item;esperar(pode_cons);fechar(trinco_cons);item = buf[consptr]; consptr = (consptr+1) % N; abrir(trinco_cons);assinalar(pode_prod);consome(item);
}}
Produtor
Produtor
Consumidor
Consumidor
prodptrconsptrint buf[N]; int prodptr=0, consptr=0;trinco_t trinco_prods, trinco_cons;semaforo_t pode_prod = criar_semaforo(N),
pode_cons = criar_semaforo(0);
•Consumidores e produtores têm secçõescríticas diferentes pois não partilham variáveis•Permite produzir e consumir ao mesmo tempo em partes diferentes do buffer
Problemas de Sincronização
Produtor-ConsumidorLeitores-EscritoresJantar dos Filósofos
Produtor-Consumidor com Semáforos
produtor() {
while(TRUE) {int item = produz();esperar(pode_prod);fechar(trinco);buf[prodptr] = item; prodptr = (prodptr+1) % N;abrir(trinco);assinalar(pode_cons);
}}
consumidor() {
while(TRUE) {int item;esperar(pode_cons);fechar(trinco);item = buf[consptr]; consptr = (consptr+1) % N; abrir(trinco);assinalar(pode_prod);consome(item);
}}
Produtor
Produtor
Consumidor
Consumidor
prodptrconsptrint buf[N]; int prodptr=0, consptr=0;trinco_t trinco;semaforo_t pode_prod = criar_semaforo(N),
pode_cons = criar_semaforo(0);
Leitores-Escritores com Semáforos
ProblemaPretende-se gerir o acesso a uma estrutura de dados partilhada em que existem duas classes de processos:• Leitores – apenas lêem a estrutura de dados• Escritores – lêem e modificam a estrutura de dados
Condições• Os escritores só podem aceder em exclusão mútua• Os leitores podem aceder simultaneamente com
outro leitores mas em exclusão mútua com os escritores
• Nenhuma das classes de processos deve ficar àmingua
Leitores-Escritores com Semáforos
inicia_leitura(){
fechar(m);if (em_escrita || escritores_espera > 0) {
leitores_espera++;abrir(m);esperar(leitores);fechar(m);leitores_espera--;if (leitores_espera > 0)
assinalar(leitores);}
nleitores++;abrir(m);
}acaba_leitura(){
fechar(m);nleitores--;if (nleitores == 0 && escritores_espera > 0)
assinalar(escritores);abrir(m);
}
int nleitores=0;boolean_t em_escrita=FALSE;int leitores_espera=0, escritores_espera=0;
inicia_escrita(){
fechar(m);if (em_escrita || nleitores > 0) {
escritores_espera++;abrir(m);esperar(escritores);fechar(m);escritores_espera--;
}em_escrita = TRUE;abrir(m);
}acaba_escrita(){
fechar(m);em_escrita = FALSE; if (leitores_espera > 0)
assinalar(leitores);else if (escritores_espera > 0)
assinalar(escritores);abrir(m);
}
semaforo_t leitores=0, escritores=0;trinco_t m;
Jantar dos Filósofos
Cinco Filósofos estão reunidos para filosofar e jantar spaghetti. Para comer precisam de dois garfos, mas a mesa apenas tem um garfo por pessoa.
Condições:• Os filósofos podem estar em um de três estados :
Pensar; Decidir comer ; Comer.• O lugar de cada filósofo é fixo.• Um filósofo apenas pode utilizar os garfos
imediatamente à sua esquerda e direita.
Jantar dos Filósofos
Jantar dos Filósofos com Semáforos, versão #1
INCORRECTA, pode conduzir a interblocagem
semaforo_t garfo[5] = {1, 1, 1, 1, 1};
filosofo(int id){
while (TRUE) {pensar();esperar(garfo[id]);esperar(garfo[(id+1)%5]);comer();assinalar(garfo[id]);assinalar(garfo[(id+1)%5]);
}}
InterblocagemEsta solução pode conduzir a interblocagem
Filósofo 0 adquire garfo 0 e perde o processador (expira o seu quantum ou háum processo mais prioritário para se executar)Filósofo 1 adquire o garfo 1 e perde o processador (idem)Filósofo 2 adquire o garfo 2 e perde o processador (idem)Filósofo 3 adquire o garfo 3 e perde o processador (idem)Filósofo 4 adquire o garfo 4, tenta adquirir o garfo 1 e bloqueia-se (estáadquirido pelo filósofo 1)Filósofos 0, 1, 2 e 3 voltam a executar-se e ficam bloqueados à espera, respectivamente, dos garfos 1, 2, 3 e 4
Interblocagem: os processos estão todos à espera uns dos outros e nunca sairão dessa situação
Existe uma cadeia circular de processos na qual cada processo possui um ou mais recursos que são pretendidos pelo processo seguinte na cadeia
Razão: Os processos necessitam de adquirir vários semáforos, mas eles não foram adquiridos sempre mesma ordem
Filósofo 0: esperar(0), esperar(1)Filósofo 4: esperar(4), esperar(0)
Jantar dos Filósofos com Semáforos, versão #2
Adquirir os semáforos sempre pela mesma ordem (ordemcrescente de número de semáforo)
semaforo_t garfo[5] = {1, 1, 1, 1, 1};semaforo_t sala = 4;
filosofo(int id){
while (TRUE) {pensar();if (i == 4) {
esperar(garfo[(id+1)%5]);esperar(garfo[id]);
} else {esperar(garfo[id]);esperar(garfo[(id+1)%5]);
}comer();assinalar(garfo[id]);assinalar(garfo[(id+1)%5]);
}}
Jantar dos Filósofos com Semáforos, versão #3
Usar um semáforo para representar a condição de bloqueio (e não o estado dos garfos/filósofos), Representar o estado dos garfos/filósofos com variáveis acedidas numa secçãocrítica
#define PENSAR 0
#define FOME 1
#define COMER 2
#define N 5
int estado[N] = {0, 0, 0, 0, 0};
semaforo_t semfilo[N] = {0, 0, 0, 0, 0};
trinco mutex;
Testa(int k){
if (estado[k] == FOME &&
estado[(k+1)%N] != COMER &&
estado[(k-1)%N] != COMER){
estado[k] = COMER;
assinalar(semfilo[K]);
}
}
filosofo(int id)
{while (TRUE) {
pensar();
fechar(mutex);
estado[id] = FOME;
Testa(id);
abrir(mutex);
esperar(semfilo[id]);
comer();
fechar(mutex);
estado[id] = PENSAR;
Testa((id-1+N)%N);
Testa((id+1)%N);
abrir(mutex);
}
}
Jantar dos Filósofos com Semáforos, versão #4
Limitar o acesso à “sala” a N-1 filósofos (fica sempre pelo menos1 garfo livre)
semaforo_t garfo[5] = {1, 1, 1, 1, 1};semaforo_t sala = 4;
filosofo(int id){
while (TRUE) {pensar();esperar(sala);esperar(garfo[id]);esperar(garfo[(id+1)%5]);comer();assinalar(garfo[id]);assinalar(garfo[(id+1)%5]);assinalar(sala);
}}
Implementação do TrincoLógico no SO
Pode ser chamada pelos programasutilizador através das chamadas sistemasfechar() e abrir()Também usada para sincronizaçãointerna do SOTrês técnicas de implementação:
Inibição de interrupções e bloqueio de processosExchange e bloqueio de processosExchange e espera activa
Implementação do trinco lógico no SO
Inibição de interrupções e bloqueio de processos
void fechar (trinco_t t){
int m = mascara_int();if (t == FECHADO)bloqueia processo
t = FECHADO;repoe_int(m);
}
void abrir (trinco_t t){
int m = mascara_int();t = ABERTO;if (processos bloqueados em t) {desbloqueia um processo
}repoe_int(m);
}
trinco_t t = ABERTO;
• Inibição das interrupções garante exclusão mútua• Processo é bloqueado no trinco, passando ao estado “bloqueado”• É muito eficiente em uniprocessadores, mas não funciona em multiprocessadores
Implementação do trinco lógico no SO
Exchange e bloqueio de processos
void fechar (trinco_t t){
int m = FECHADO;while (exchange(t,m) == FECHADO) {bloqueia_processo(t);m = FECHADO;
}}
void abrir (trinco_t t){
t = ABERTO;desbloqueia_processos(t);
}
trinco_t t = ABERTO;
• Instrução exchange(t,m) obtém o estado do trinco e fecha-o atomicamente• Se o trinco estava aberto, fica fechado e rotina termina• Se o trinco estava fechado, o sistema bloqueia o processo
Implementação do trinco lógico no SO (apenas para uso interno do SO)
Exchange e Espera Activa
void so_fechar (trinco_t t){
int m = FECHADO;while (exchange(t,m) == FECHADO);
}
void so_abrir (trinco_t t){
t = ABERTO;}
so_trinco_t t = ABERTO;
•Instrução exchange(t,m) obtém o estadodo trinco e fecha-o atomicamente•Espera activa só é possível se for usadaapenas para sincronização interna do sistemaoperativo•Eficiente em secções críticas pequenas (4-5 instruções C)•Instruções load linked – store conditionalpodem ser usadas em vez do exchange
•Para abrir o trinco, basta posicionar a variável•Os processos bloqueados estão em esperaactiva e vão imediatamente ver que o trincojá está aberto
Implementação do trinco lógico no SO
Optimização: exchange em modoutilizador
void fechar (trinco_t t){
int m = FECHADO;if (exchange(t,m) == ABERTO)return;
_fechar(t);}
void abrir (trinco_t t){
_abrir(t);}
trinco_t t = ABERTO;
• Rotinas fechar() e abrir() são bibliotecas em modoutilizador, _fechar() e _abrir() as chamadas sistemasapresentadas anteriormente• fechar() tenta fechar o trinco em modo utilizador com exchange. Se conseguir porque o trinco estava aberto, émuito eficiente (1 instrução máquina).• Se o trinco estiver fechado, efectua a chamada sistema_fechar()
• Para abrir o trinco, efectua a chamadasistema _abrir() para desbloquear osprocessos que estejam bloqueados
Exclusão mútua emmultiprocessadores modernos:load linked – store conditional (R4600)
int trinco = FALSE; fechar() {
li R1, trincol1: ll R2, 0(R1)
bnez R2, l1 li R2, #1sc R2, 0(R1)beqz R2, l1
}abrir(){
trinco = FALSE;}
As instruções exchange e test-and-set não são adequadas aosmultiprocessadores modernos:
•Deixam o bus trancado durante os dois acessos àmemória (load e store), que significam muitas dezenas de instruções•Não se adequam à estrutura de pipeline dos processadores RISC•Ocupam o bus na espera activa, impedindo que o processador que a obteve execute a secção crítica e a liberte
Solução: load linked, store conditional•Em conjunto, funcionam como um “exchange” com controlo de concorrência optimista•Load linked
•Faz um load•Inicia o snoop ao bus, detectando acessos ao endereço do load
•Store conditional•Se não houve acessos ao endereço do load, faz um store normal•Se houve, falha
•Se o conjunto load linked – store conditional falhar, terá que ser repetido até funcionar
Sincronização em Windows
Secções críticas em WindowsProdutor – consumidor em Windows
Sincronização em WindowsModo utilizador
InterlockedLONG InterlockedExchangeAdd(PLONG plAddend, LONG lIncrement);LONG InterlockedExchange(PLONG plTarget, LONG lValue); PVOID InterlockedExchangePointer(PVOID* ppvTarget, PVOID pvValue);
// Secção crítica com InterlockedExchangeBOOL g_trinco = FALSE;void levantar_dinheiro (ref *conta, int valor){
while (InterlockedExchange (&g_trinco, TRUE) == TRUE)Sleep(0);
if (conta->saldo >= valor) {conta->saldo = conta->saldo – valor;
} else valor = -1
InterlockedExchange(&g_trinco, FALSE);
return valor;}
Sincronização em WindowsModo utilizador – threads do mesmoprocesso
Critical SectionVOID InitializeCriticalSection(PCRITICAL_SECTION trinco); VOID DeleteCriticalSection(PCRITICAL_SECTION trinco); VOID EnterCriticalSection(PCRITICAL_SECTION trinco);VOID LeaveCriticalSection(PCRITICAL_SECTION trinco);BOOL TryEnterCriticalSection(PCRITICAL_SECTION trinco);
// Secção crítica com CRITICAL_SECTIONCRITICAL_SECTION trinco;void levantar_dinheiro (ref *conta, int valor){
EnterCriticalSection(trinco);
if (conta->saldo >= valor) {conta->saldo = conta->saldo – valor;
} else valor = -1
LeaveCriticalSection(trinco);
return valor;}
Sincronização em WindowsModo núcleo
WaitFor objectDWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds); DWORD WaitForMultipleObjects(DWORD dwCount, CONST HANDLE* phObjects,
BOOL fWaitAll, DWORD dwMilliseconds);
MutexHANDLE CreateMutex(PSECURITY_ATTRIBUTES psa, BOOL fInitialOwner, PCTSTR pszName);HANDLE OpenMutex(DWORD fdwAccess, BOOL bInheritHandle, PCTSTR pszName);BOOL ReleaseMutex(HANDLE hMutex);
SemaphoreHANDLE CreateSemaphore(PSECURITY_ATTRIBUTE psa,
LONG lInitialCount, LONG lMaximumCount, PCTSTR pszName);HANDLE OpenSemaphore(DWORD fdwAccess, BOOL bInheritHandle, PCTSTR pszName);BOOL ReleaseSemaphore(HANDLE hsem, LONG lReleaseCount, PLONG plPreviousCount);
DeleteCriticalSection(&cs);CloseHandle(hmtx);
--Pode ser misturada com outrosobjectos núcleo
Outros
LeaveCriticalSection(&cs);ReleaseMutex(hmtx);Notify
TryEnterCriticalSection(&cs);
WaitForSingleObject (hmtx, 0);
Wait condicional
--WaitForSingleObject (hmtx, dwMilliseconds);
Wait com timeout
EnterCriticalSection(&cs);WaitForSingleObject (hmtx, INFINITE);
Wait
InitializeCriticalSection(&cs);
hmtx= CreateMutex (NULL, FALSE, NULL);
Inicialização
CRITICAL_SECTION m;HANDLE m;Declaração
Threads do mesmo processoThreads de qualquer processoÂmbito
Eficiente (modo utilizador)Ineficiente (modo núcleo)Performance
Critical SectionMutex
Sincronização em Linux
Secções críticas em LinuxProdutor – consumidor em Linux
pthreads#include <thread.h>
int thr_create(void * stack_base, size_t stack_size, void *(*start_routine)(void *)void * arg, long flags, thread_t * new_thread);
size_t thr_min_stack(void);int thr_join(thread_t wait_for, thread_t *departed,
void **status);
thread_t thr_self(void);void thr_yield(void);
int thr_suspend(thread_t target_thread);int thr_continue(thread_t target_thread);void thr_exit(void *status);int thr_kill(thread_t target_thread, int sig);
int thr_setconcurrency(int new_level);int thr_getconcurrency(void);int thr_setprio(thread_t target_thread, int pri);int thr_getprio(thread_t target_thread, int *pri);
int thr_keycreate(thread_key_t *keyp, void (*destructor)(void *value));
int thr_setspecific(thread_key_t key, void *value);int thr_getspecific(thread_key_t key, void **valuep);
Mutexes
#include <synch.h>
int mutex_init(mutex_t *mp, int type, void * arg);
int mutex_destroy(mutex_t *mp);int mutex_lock(mutex_t *mp);int mutex_trylock(mutex_t *mp);int mutex_unlock(mutex_t *mp);
Read-Write Locks
int rwlock_init(rwlock_t *rwlp, inttype, void * arg);
int rwlock_destroy(rwlock_t *rwlp);int rw_rdlock(rwlock_t *rwlp);int rw_wrlock(rwlock_t *rwlp);int rw_unlock(rwlock_t *rwlp);int rw_tryrdlock(rwlock_t *rwlp);int rw_trywrlock(rwlock_t *rwlp);
Semaphores
#include <synch.h>
int sema_init(sema_t *sp, unsignedint count, int type, void * arg);
int sema_destroy(sema_t *sp);int sema_wait(sema_t *sp);int sema_trywait(sema_t *sp);int sema_post(sema_t *sp);
Semáforos (Sistema V)
IPC no SV - Semáforosum semáforo consiste num conjunto de contadores que sópodem ter valores positivoscriação de um semáforo:
#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semget (key, nsems, semflg)key_t key;int nsems, semflg;
“nsems” especifica o número de contadores, que são inicializados a zero na criaçãooperações sobre semáforos:
int semop (semid, sops, nsops)int semid;struct sembuf (*sops)[ ];int nsops;
são executadas as nsops operações definidas em sops e devolvido o valor do último contador acedidouma operação é definida, de acordo com a estrutura sembuf, por:
short sem_num; /* numero */short sem_op; /* operação */short sem_flag; /* flags */
segundo o valor de sem_op temos:sem_op > 0 o valor de sem_op é adicionado ao do contadorsem_op < 0 o valor de sem_op é adicionado ao do contador; o processo pode ficar bloqueadosem_op = 0 o processo é suspenso até o valor do contador atingir zero
Semáforos - Controlosintaxe:int semctl (semid,
semnum, cmd, arg)int semid, semnum, cmd;union semun {
int val;struct semid_ds *buf;ushort *array;
} arg;
comandos possíveis:IPC_STAT preenche arg.buf com estado actualIPC_SET inicializa parâmetros a partir de buf.argIPC_RMID elimina o semáforo em causaGETALL copia os valores dos contadores para arg.arraySETALL inicializa os valores a partir de arg.array
comandos possíveis com semnum especificado:GETVAL devolve o valor do contadorSETVAL inicializa o valor do contadorGETPID devolve o pid da última operaçãoGETNCNT devolve o número de processos aguardando um valor não zero do contadorGETZCNT devolve número de processos aguardando um valor zero do contador
#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>
#define SEMMUTEX 5#define SEMALOC 6int mutex, semaloc;
void IniSem(int nmax) { union semun {
int val;struct semid_ds *buf;ushort *array;} init;
if ((mutex = semget (SEMMUTEX, 1,0777|IPC_CREAT)) < 0) erro ("semgetSEMMUTEX");if ((semaloc = semget (SEMALOC, 1, 0777|IPC_CREAT)) < 0) erro("semgetSEMALOC");
init.val = 1;if (semctl (mutex, 0, SETVAL, init) < 0) erro("semctl mutex");
init.val = nmax;if (semctl (semaloc, 0, SETVAL, init) < 0) erro("semctl SemAloc");
}
Distribuidor de Memória
Distribuidor de Memória (cont.)
void Esperar (key_t semid, int semnum, int uni) {struct sembuf s;
s.sem_num = semnum;s.sem_op = -uni;s.sem_flg = 0;if (semop (semid, &s, 1) < 0) erro ("semopEsperar");
}
void Assinalar (key_t semid, int semnum, int uni) {struct sembuf s;
s.sem_num = semnum;s.sem_op = uni;s.sem_flg = 0;if (semop (semid, &s, 1) < 0) erro ("semopAssinalar");
}
void EsperarMutex (key_t semid, int semnum) {struct sembuf s;
s.sem_num = semnum;s.sem_op = -1;s.sem_flg = SEM_UNDO;if (semop (semid, &s, 1) < 0) erro ("semopEsperarMutex");
}
void AssinalarMutex (key_t semid, int semnum) {struct sembuf s;
s.sem_num = semnum;s.sem_op = 1;s.sem_flg = SEM_UNDO;if (semop (semid, &s, 1) < 0) erro ("semopAssinalarMutex");
}
Interblocagem
Soluções preventivas -procuram evitarsituações de interblocagem
Garantir que os recursos são adquiridos todos pela mesma ordemRequisitar todos os recursos que o processo necessita no início da sua execuçãoQuando a aquisição de um recurso não épossível liberta todos os recursos detidos.
Interblocagem
Soluções de Detecção de Interblocagemdetecção de um estado de interblocagem e tentativa de resolução por libertação forçada de recursos
Soluções de Eliminação do Estado de Interblocagem
o algoritmo analisa a evolução do conjunto de recursos que os processos possuem e procura evitar o aparecimento de um estado de interblocagem
Mecanismos Directos de Sincronização
ObjectivoSuspender temporariamente a execução de subprocessos
Limitações:A sincronização directa implica o conhecimento do identificador do processo sobre o qual se pretende actuar. Não se pode dar aos programas dos utilizadores a possibilidade de interferirem com outros utilizadores A restrição habitual é apenas permitir o uso de sincronização directa entre processos do mesmo utilizador
Mecanismos Directos de Sincronização
Funções que actuam directamente sobre o estado dos processos
Suspender (IdProcesso)Acordar (IdProcesso)
A função de suspensão é também frequentemente utilizada para implementar mecanismos de atraso temporizado que funcionam como uma auto suspensão
Adormecer (Período)
Diagrama de Estadodos Processos
Em Execução
Executável Bloqueado Suspenso
SuspenderSeleccionado pelo
Despacho
Preempção
Desbloquear
Acordar
Acordar
Suspender
Suspender
Modelo Computacional (resumo)
Trincoscriar_trinco
abrirfechar
Semáforoscriar_semáforoeliminar_semáforo
esperarassinalar
Sincronização Directasuspenderacordar
adormecer