Escolar Documentos
Profissional Documentos
Cultura Documentos
Desabilitando interrupções
Por outro lado, frequentemente convém ao próprio núcleo desabilitar interrupções, para
algumas poucas instruções, enquanto estiver alterando variáveis ou listas. Se ocorrer uma
interrupção, por exemplo, enquanto a lista de processos prontos estiver em um estado
inconsistente poderá haver condições de corrida. A conclusão é: desabilitar interrupções
muitas vezes é uma técnica bastante útil dentro do próprio sistema operacional, mas inade-
quada como um mecanismo geral de exclusão mútua para
A possibilidade de realizar exclusão mútua desabilitando interrupções-mesmo dentro do
núcleo - está se tornando menor a cada dia em virtude do número crescente de chips
multinúcleo até em PCs populares. Dois núcleos já são comuns, quatro estão presentes em
máquinas sofisticadas e oito ou 16 virão em breve. Em um sistema multicore
(multiprocessador), desabilitar as interrupções de uma CPU não impede que outras CPUs
interfiram nas operações que a primeira CPU está executando. Consequentemente,
esquemas mais sofisticados são necessários.
Como uma segunda tentativa, busquemos uma solução de software. Considere que haja
uma única variável compartilhada (trava), Inicialmente contendo o valor 0. Para entrar em
sua região crítica, um processo testa antes se há trava, verificando o valor da variável trava.
Se trava for 0, 0 processo altera essa variável para 1 e entra na região crítica. Se trava já
estiver com o valor 1, o processo simplesmente aguardará até que ela se torne 0. Assim,
um 0 significa que nenhum processo está em sua região crítica e um 1 indica que algum
processo está em sua região crítica.
Infelizmente, essa ideia apresenta exatamente a mesma falha que vimos no diretório de
spool. Suponha que um processo leia a variável trava e veja que ela é 0. Antes que possa
alterar a variável trava para 1, outro processo é escalonado, executa e altera a variável
trava para 1: Ao executar novamente, o primeiro processo também colocará 1 na variável
trava e, assim, os dois processos estarão em suas regiões críticas ao mesmo tempo.
Agora você pode pensar que seria possível contornar esse problema lendo primeiro a
variável trava e, então, verificá-la novamente, um pouco antes de armazená-la. Mas de que
isso adiantaria? A disputa então ocorreria se o segundo processo modifica a variável trava
um pouco depois de o primeiro processo ter terminado sua segunda verificação.
Chaveamento obrigatório
Um terceiro modo de lidar com o problema da exclusão mútua é ilustrado na Figura 2.18.
Esse fragmento de programa, como quase todos os outros deste livro - foi escrito em C. C
foi escolhida aqui porque os sistemas operacionais reais são quase todos escritos em C
(ou, ocasionalmente, em C++), mas muito dificilmente em linguagens como Java, Modula 3
ou Pascal. C é uma linguagem poderosa, eficiente e previsível, características fundamentais
para escrever sistemas operacionais. Java, por exemplo,não é previsível porque a memória
pode se esgotar em um momento crítico, sendo preciso invocar o coletor de lixo (garbage
collector) em uma hora inoportuna. Isso não aconteceria com C, em que não há coleta de
lixo. Uma comparação quantitativa entre C, C++, Java e outras quatro linguagens pode ser
verificada em Prechelt (2000).
Na Figura 2.18, a variável inteira turn, inicialmente 0, serve para controlar a vez de quem
entra na região crítica e verifica ou atualiza a memória compartilhada. Inicialmente, o
processo 0 inspeciona a variável turn, encontra lá o valor 0 e entra em sua região crítica. O
processo 1 também encontra lá o valor 0 e, então, fica em um laço fechado testando
continuamente para ver quando a variável turn se torna 1. Testar continuamente uma
variável até que algum valor apareça é chamado de espera ociosa (busy waiting). A espera
ociosa deveria em geral ser evitada, já que gasta tempo de CPU, Somente quando há uma
expectativa razoável de que a espera seja breve é que ela é usada. Uma variável de trava
que usa à espera ociosa é chamada de trava giratória (spin lock).
Quando deixa a região crítica, o processo 0 põe a variável turn em 1, para permitir que o
processo 1 entre em sua região crítica. Suponha que o processo 1 tenha terminado
rapidamente sua região crítica e, assim, ambos os processos não estejam em suas regiões
críticas, com a variável turn em 0. Agora, o processo 0 executa todo o seu laço
rapidamente, saindo de sua região crítica e colocando a variável turn em 1. Nesse ponto, a
variável turn é 1 e os dois processos estão executando em suas regiões não-críticas.
De repente, o processo 0 termina sua região não crítica e volta ao início de seu laço.
Infelizmente, a ele não será permitido entrar em sua região crítica agora, pois a variável turn
está em 1 e o processo 1 está ocupado com sua região não crítica. O processo 0 fica
suspenso em seu laço while até que o processo 1 coloque a variável turn em 0. Em outras
palavras, chavear a vez não é uma boa ideia quando um dos processos for muito mais lento
que o outro.
Solução de Peterson
resumo:
a desativação de contínuos em sistemas uniprocessadores como uma solução
simples para garantir a exclusão mútua. Quando um processo entra em sua região
crítica, ele desativa todas as contínuas e as reativa quando sai dessa região. Isso
impede que o clock prolongadom enquanto os contínuos estão desativadas,
garantindo que a CPU não mude para outro processo. Dessa forma, um processo
pode acessar uma memória compartilhada sem interferência de outros processos.
Por outro lado, o próprio kernel (núcleo) do sistema operacional geralmente desativa
as contínuas em algumas instruções ao alterar variáveis ou listas. No entanto, se
ocorrer uma interrupção durante esse período, podem ocorrer disputas. Portanto,
desabilitar o controle é uma técnica útil dentro do sistema operacional, mas não é
adequado como um mecanismo geral de exclusão mútua.
No entanto, essa abordagem apresenta uma falha. Suponha que um processo leia a
variável trava e veja que ela está com o valor 0. Antes que possa alterar a trava para 1,
outro processo é escalado, executa e altera a trava para 1. Em seguida, o primeiro processo
também altera a trava para 1, originado em ambos os processos, estando em suas regiões
críticas simultaneamente.
Uma solução possível seria verificar o trabalho novamente antes de armazenar o valor. No
entanto, isso não resolve o problema, pois ainda haveria uma disputa se o segundo
processo modificasse a travar pouco depois de o primeiro processo ter terminado sua
segunda verificação.
Em resumo, essa abordagem com variáveis de trabalho não garante a exclusão mútua de
forma confiável, pois pode levar a situações em que processos múltiplos estão
simultaneamente em suas regiões críticas.
chaveamento obrigatório
O chaveamento obrigatório é uma abordagem que garante que cada processo receba uma
quantidade justa e igual de tempo de CPU. Isso é conseguido dividindo o tempo de CPU em
fatias iguais de chamadas de quantum e alternando a execução entre os processos.
Embora não garanta a exclusão mútua, o chaveamento obrigatório é usado para
compartilhar o tempo de CPU de forma justa entre os processos.
solução de peterson