0% acharam este documento útil (0 voto)
64 visualizações67 páginas

Chap06 Sincronismo

Este documento discute soluções para o problema da seção crítica em sistemas operacionais, que ocorre quando processos concorrentes precisam acessar uma região de código de forma exclusiva para evitar inconsistências nos dados. Apresenta algoritmos para exclusão mútua entre dois processos e a solução de Peterson, que utiliza variáveis compartilhadas de turno e flag para garantir que apenas um processo execute a seção crítica de cada vez.

Enviado por

Fabio Fernandes
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
64 visualizações67 páginas

Chap06 Sincronismo

Este documento discute soluções para o problema da seção crítica em sistemas operacionais, que ocorre quando processos concorrentes precisam acessar uma região de código de forma exclusiva para evitar inconsistências nos dados. Apresenta algoritmos para exclusão mútua entre dois processos e a solução de Peterson, que utiliza variáveis compartilhadas de turno e flag para garantir que apenas um processo execute a seção crítica de cada vez.

Enviado por

Fabio Fernandes
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
Você está na página 1/ 67

Sistemas Operacionais I

Gerência de Processos: Sincronização

Prof. Alexandre Duarte : http://alexandrend.com


Centro de Informática | Universidade Federal da Paraíba

Estes slides são baseados no material que acompanha o livro Operating


Systems Concepts de Silberschatz, Galvin and Gagne
Objetivos
¨ Introduzir o problema da seção críticas, cujas
soluções podem ser utilizadas para garantir
consistência no acesso a dados compartilhados

¨ Apresentar soluções em software e hardware


para o problema da seção crítica

¨ Introduzir o conceito de transação atômica e


descrever os mecanismos utilizados para garantir
atomicidade
Antecendentes
¨ Acesso concorrente a dados compartilhados
pode resultar em inconsistência nos dados

¨ Para manter a consistência dos dados


precisamos de mecanismos para garantir
ordem na execução de processos
cooperativos
Relações temporais
Visão do usuário de threads paralelas

Instruções executas por uma única thread são totalmente


ordenadas
- A < B < C < ...

Na falta de sincronização
- Instruções executas por threads distintas devem
ser consideradas sem ordem ou simultâneas
- Não X < Y, nem Y < X
Exemplo Example
Example: In the beginning...
main()
Y-axis is “time.”
A
Could be one CPU, could
pthread_create()
be multiple CPUs (cores).
foo()
B
A'

• A < •B <AC<B<C
B' • A' <•B'A’ < B’
C • A < •A' A < A’
• C ==• A'C == A’
• C == B'
• C == B’
Seções críticas / Exclusão mútua
¨ Seção crítica: Sequência de instruções que podem gerar
resultados incorretos se executadas simultaneamente

¨ Exclusão mútua significa “não simultâneo”


¤ A < B ou B < A
¤ Não importa qual

¨ Forçar exclusão mútua entre duas execuções de


uma seção crítica
¤ É suficiente para garantir uma execução correta
¤ Garante ordenamento
Critical sections
Critical sections
Seção crítica
is the “happens-before” relation

T1 T2 T1 T2 T1 T2

Possibly incorrect
Provalvemente Correct
Correto Correct
Correto
incorreto
5
Quando SC acontece
¨ Um padrão comum
¤ ler – atualizar – escrever
¤ uma variável compartilhada
¤ em código que pode ser executado por threads concorrentes

¨ Variável compartilhada
¤ Variáveis globais e alocadas no heap
¤ NÃO acontece em variáveis locais. Porque?
¤ Cada thread tem a sua pilha
Processo Produtor
Processo Consumidor
Revisitando o problema do Produtor/
Consumidor
¨ Suponha que desejamos alterar a solução apresentada
anteriormente para o problema do Produtor/Consumidor
que preencha todas as posições do buffer

¨ Uma ideia seria utilizar uma variável inteira count para


contar o número de posições ocupadas no buffer.

¨ Inicialmente count recebe 0 e é incrementado sempre


que o produtor inserir algo no buffer e decrementado
sempre que o consumidor remover algo do buffer.
Produtor
Consumidor
Condição de corrida
count++ poderia ser implementado assim:

count-- poderia ser implementado assim:


Condição de corrida
Condição de corrida
¨ Um programa tem uma condição de corrida se o
resultado da execução depende do “timing”
¤ Não determinístico

¨ Sintomas típicos
¤ Executo no mesmo conjunto de dados e às vezes imprime 0
e outras imprime 4
¤ Executo no mesmo conjunto de dados e às vezes imprime 4
e às vezes dá erro (crashes)
O problema da seção crítica
¨ Considere um sistema composto por n processos [P0, P1, ... , Pn-1]

¨ Cada um desses processos tem um segmento de código


denominado seção crítica em que o processo pode alterar
variáveis compartilhadas

¨ Para evitar inconsistências nos dados compartilhados é preciso


garantir que dois desses processos jamais executarão ao mesmo
tempo código de suas seções críticas

¨ O problema da seção crítica consiste em elaborar um protocolo


que possibilite a cooperação entre os processos e que garanta a
consistência dos dados compartilhados
Estrutura geral de um processo
Pede permissão
para entrar em
uma seção crítica

Libera seção
crítica para outros
processos
Requisitos de uma solução para o
problema da seção crítica
¨ Exclusão mútua: se o processo Pi está executando código de sua
seção crítica então nenhum outro processo pode estar executando
código de suas seções críticas

¨ Progresso: Se nenhum processo estiver executando sua seção


crítica e alguns processos quiserem entrar em suas seções críticas,
só os processos que não estiverem executando suas seções
remanescentes poderão participar da decisão sobre qual processo
será o próximo a entrar em sua seção crítica E essa seleção não
poderá ser adiada indefinidamente

¨ Espera limitada: Há um limite para o número de vezes que outros


processos podem entrar em suas seções críticas após um
processo ter feito uma solicitação para entrar na sua seção crítica e
ter essa solicitação atendida
¤ Pode-se presumir que cada processo execute em uma velocidade
diferente de zero mas não se pode fazer qualquer suposição sobre as
velocidades relativas dos n processos
Implementando exclusão mútua
¨ Via software: totalmente em código
¤ Algoritmos de espera ocupada

¨ Via hardware: instruções especiais de máquina


¤ Instruções atômicas
¤ Desabilitar interrupções

¨ Via suporte do SO: SO fornece primitivas através de


chamadas de sistemas
¤ Semáforos
¤ Monitores
Solução para dois processos
¨ Algoritmo 1
¤ Os processos compartilham a variável int turn, que indica de quem
é a vez de executar a seção crítica

do { - Garante mutex, contudo


while (turn != i);
seção crítica
- Processos se alternam em suas SCs
turn = j;
- O tempo de execução é ditado pelo
seção remanescente
mais lento
} while (1)
- Espera ocupada
- Se um processo falhar, o outro jamais
entrará em sua SC
Solução para dois processos
¨ Algoritmo 2
¤ Os processos compartilham a variável boolean flag[2] = false

¤ Se flag[i] == true, então o processo Pi está pronto para entrar em


sua seção crítica
- Processos não se alternam em suas
do { SCs
while (flag[j]); - Se processo falha fora da SC, não
flag[i] = true; bloqueia outro processo, contudo
seção crítica
flag[i] = false ;
- Não garante mutex
seção remanescente
} while (1) - i e j podem verificar os flags dos
outros, então ambos mudam para true
e entram na SC
Solução para dois processos
¨ Algoritmo 3
¤ Os processos compartilham a variável boolean flag[2] = false

¤ Se flag[i] == true, então o processo Pi está pronto para entrar em


sua seção crítica
- Garante mutex, contudo
do {
flag[i] = true;
while (flag[j]); - Deadlock: ambos setam para true e
seção crítica ficam em loop no while
flag[i] = false ;
seção remanescente
} while (1)
Solução para dois processos
¨ Algoritmo 4
¤ Os processos compartilham a variável boolean flag[2] = false

¤ Se flag[i] == true, então o processo Pi está pronto para entrar em


sua seção crítica
- Deadlock aconteceu porque os dois
do { processos insistem em entrar na SC e
flag[i] = true; ficaram em espera ocupada
while (flag[j]){
flag[i] = false; - Solução: setar flag momentaneamente
(espera aleatória); para permitir o outro prosseguir
- Dar a vez
flag[i] = true;
}
seção crítica - Starvation: Processos executam em
flag[i] = false ; sincronia
seção remanescente
} while (1)
Solução para dois processos
¨ Algoritmo 4
¤ Os processos compartilham a variável boolean flag[2] = false

¤ Se flag[i] == true, então o processo Pi está pronto para entrar em


sua seção crítica

do {
flag[i] = true;
while (flag[j]){
flag[i] = false; Pi seta i para false
(espera aleatória); Pj seta j para false
flag[i] = true; Pi seta i para true
Pj seta j para true
}
seção crítica
flag[i] = false ;
seção remanescente
} while (1)
Solução de Peterson
¨ Utilizada apenas para pares de processos

¨ Os processos compartilham duas variáveis:


¤ int turn;
¤ boolean flag[2]

¨ A variável turn indica de quem é a vez de entrar na seção


crítica.

¨ O array flag é utilizado para indicar se um processo está


pronto ou não para entrar em sua seção crítica
¤ flag[i] = true implica que o processo Pi está pronto!
Algoritmo para o processo Pi

Funciona, mas seria desejável algo mais simples


Hardware de sincronização
¨ Muitos sistemas oferecerem suporte em hardware para
controle de acesso a seções críticas

¨ Processador único: poderia desabilitar interrupções


¤ O código atualmente em execução não seria interrompido
¤ Geralmente essa abordagem é muito ineficiente em sistemas
com múltiplos núcleos/processadores
n Um sistema operacional que funciona dessa forma não é escalável

¨ Máquinas modernas oferecem instruções especiais de


hardware com execução atômica
¤ Atômica = não interrompível
¤ Testar e modificar o conteúdo de uma palavra de memória
¤ Trocar os conteúdos de duas palavras de memória
Solução para o problema da seção crítica
utilizando locks Locks: Example
Locks: Example execution

lock()
lock() Two choices:
• Spin
• Block
unlock() • (Spin-then-block)

unlock()
Solução para o problema da seção crítica
utilizando locks

lock: Variável global


inicializada com FALSE
A instrução TestAndSet
Solução utilizando TestAndSet
Instrução SWAP
Solução utilizando SWAP
Não satisfazem o requisito de espera
limitada
Espera limitada: Há um limite para o número de vezes que outros
processos podem entrar em suas seções críticas após um processo
ter feito uma solicitação para entrar na sua seção crítica e ter essa
solicitação atendida
¤ Pode-se presumir que cada processo execute em uma velocidade
diferente de zero mas não se pode fazer qualquer suposição sobre as
velocidades relativas dos n processos

¤ Se um processo executa muito mais rápido que o outro, tem grandes


chances de sempre obter o lock
Exclusão mútua com espera limitada utilizando a
operação TestAndSet()

Variáveis compartilhadas:
• boolean waiting[n]
• boolean lock
Exclusão mútua com espera limitada utilizando a
operação TestAndSet()

O primeiro a executar
Variáveis compartilhadas: obtém o lock
• boolean waiting[n]
• boolean lock

Varre de forma cíclica

Procura o primeiro
que esteja esperando

Ninguém está esperando


para entrar

Dá chance para quem


está esperando
Exclusão mútua com espera limitada utilizando a
operação TestAndSet()

Exclusão mútua: só entra


Variáveis compartilhadas: se waiting[i] ou key são
• boolean waiting[n] falsos
• boolean lock

Espera limitada: Varre


de forma cíclica. No
máximo vai esperar N-1
unidades de tempo

Progresso: Quando saí da


SC, libera lock ou
waiting[i]
Semáforos
¨ Solução proposta por Dijkstra em 1965

¨ Princípio básico:
¤ Um processo é suspenso enquanto não obtém permissão
para executar sua RC e é automaticamente “acordado”
através de um sinal

¨ Para liberar um semáforo “s” o processo deve executar


uma primitiva signal(s) e para obter acesso à sua RC
via o semáforo “s” o processo deve executar a primitiva
wait(s)
Semáforos
¨ Solução proposta por Dijkstra em 1965

¨ Princípio básico:
¤ Um processo é suspenso enquanto não obtém permissão
para executar sua RC e é automaticamente “acordado”
através de um sinal

¨ Ferramenta de sincronização baseada em duas operações


simples

¨ Para liberar um semáforo “s” o processo deve executar


uma primitiva signal(s) e para obter acesso à sua RC
via o semáforo “s” o processo deve executar a primitiva
wait(s)
Semáforos

¨ Semáforo S – variável inteira


¨ Duas operações para modificar o semáforo S: wait() e signal()
¨ Originalmente P() e V()

¨ Só podem ser acessados através de duas operações


indivisíveis (atômicas)
Semáforos como uma ferramenta geral de
sincronização

¨ Semáforo de contagem: um valor inteiro que pode variar em um domínio irrestrito


¨ Semáforo binário: valor inteiro que só pode variar entre 0 e 1
¤ Igual ao lock
¤ Também conhecido como bloqueio mutex
¤ Garante exclusão mútua
Implementação de Semáforos
¨ Enquanto um processo está na sua seção crítica, os
outros podem estar em uma espera ocupada

¨ Note que algumas aplicações podem gastar bastante


tempo em suas seções críticas portanto esta não é
uma boa solução
Implementação de Semáforos sem espera
ocupada

¨ Associar uma fila de espera a cada semáforo


¨ Cada entrada na fila tem dois itens:
¤ valor (inteiro)
¤ ponteiro para o próximo registro na fila
¨ Duas operações:
¤ block: coloca o processo invocando a operação na
fila de espera apropriada.
¤ wakeup: remove um dos processos da fila de espera
e o coloca na fila de prontos

typedef struct{
int value;
struct process *L;
} semaforo
Implementação de Semáforos sem espera
ocupada
Problemas de semáforos com fila de espera

¨ Deadlock: dois ou mais processos esperam indefinidamente


por um evento que só pode ser causado por um dos
processos em espera
¤ Sejam S e Q dois semáforos inicializados com 1

¨ Starvation: bloqueio indefinido – um processo pode nunca


ser removido da fila de espera de um semáforo na qual ele
está bloqueado. Ex: FILO
¨ Inversão de prioridade: Problema de escalonamento que
ocorre quando um processo de baixa prioridade bloqueia um
processo de prioridade mais alta
Problemas clássicos de sincronização

¨ Problema do Buffer Limitado


¨ Problema dos Leitores e Escritores
¨ Problema do Jantar dos Filósofos
Problema do buffer limitado
¨ N buffers, cada um pode armazenar um item
¨ Semáforo mutex inicializado com 1 (exclusão
mútua para acesso a cadeia de buffers)
¨ Semáforo full inicializado com 0 (nº de buffers
cheios)
¨ Semáforo empty inicializado com N (nº de
buffers vazios)
Problema do buffer limitado:
Produtor
Problema do buffer limitado:
Consumidor
Problema dos leitores e escritores
¨ Um conjunto de dados é compartilhado entre um número de
processos concorrentes
¤ Leitores – apenas leem os dados, não fazem qualquer atualização
¤ Escritores – podem tanto ler quanto atualizar os dados
¨ Problema
¨ Permitir que múltiplos leitores possam acessar os dados ao mesmo
tempo.
¨ Permitir que apenas um escritor tenha acesso aos dados
compartilhados em um determinado momento
¨ Dados compartilhados
¤ Conjunto de dados
¤ Semáforo mutex inicializado com 1
¤ Semáforo wrt inicializado com 1
¤ Contador inteiro readcount inicializado com 0
Problema dos leitores e escritores:
Escritor
Problema dos leitores e escritores:
Leitor
O problema do jantar dos filósofos
O problema do jantar dos filósofos
Problemas com semáforos
¨ Uso correto das operações com semáforos

¤ signal (mutex) …. wait (mutex)

¤ wait (mutex) … wait (mutex)

¤ Esquecer um wait (mutex) ou um signal (mutex)


(ou ambos)
Monitores
¨ Uma abstração de alto nível que fornece um mecanismo
mais conveniente para sincronização de processos
¤ Monitor é o responsável por obter e liberar locks
¤ Código de sincronização é adicionado pelo compilador

¨ Região crítica é implementada dentro do monitor como


procedimentos
¤ Apenas um processo pode estar ativo em um monitor a cada
momento
¤ Um processo pode suspender a si mesmo dentro do monitor
¤ Outro processo pode entrar no monitor
¤ Processos podem ser notificados a continuar
Monitores
Monitores

Dados
compartilhados

...

Fila de threads tentando procedimentos


entrar no monitor
Variáveis condicionais
¨ Exclusão mútua automática
¤ Apenas uma thread pode executar dentro do
monitor por vez

¤ Se uma segunda thread tenta executar no


monitor, ela bloqueia até a primeira sair
n Mais restrita que semáforos
n Mais fácil de utilizar

¨ Porém há um problema...
Monitores

Produz()

Consome()
T1

Buffer vazio
E agora?
Monitores

Produz()
T1
Consome()

Buffer cheio
E agora?
Solução: Variáveis condicionais
¨ condition x, y;
¨ Duas operações em uma variável condicional
¤ x.wait ()
n um processo que invoca a operação é suspenso
n espera por outro para sinalizar
n possuem filas de espera associadas

¤ x.signal ()
n reinicia um processo que tenha invocado um x.wait ()
(ou não faz nada)
Monitores

Filas associadas às x
condições x e y
y

Dados
compartilhados

...

Fila de threads tentando procedimentos


entrar no monitor
Monitores

Produz()

Consome() T1
Fila de threadas
esperando por condição
“not full” ser sinalizada
Buffer cheio
E agora?
Solução para o problema do jantar dos filósofos
Solução para o problema do jantar dos
filósofos

¨ Cada filósofo invoca as operações pickup()


e putdown() na seguinte ordem:

Você também pode gostar

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy