Disputa entre Processos e Região Crítica - Comunicação entre Processos (IPC - Interprocess Communication)

Neste tutorial sobre Sistemas Operacionais, iremos entrar em detalhes sobre como os vários processos que ocorrem em um sistema operacional trabalham entre si.

Entenderemos como ocorre uma disputa entre dois ou mais processos para acessar uma região crítica, e veremos na prática os problemas que isso pode gerar, antes de estudarmos as soluções.








Disputa entre Processos

Agora que já entendemos como os processos são executados em uma máquina de única CPU (sempre um por vez, através do chaveamento de processos), vamos ver que muitos problemas podem surgir por conta desta maneira dos processos se portarem.

Uma das dificuldades que ocorrem em sistemas com vários processos a serem executados é a competição, ou disputa entre processos, onde um faz uma determinada coisa, então é pausado, ocorre o chaveamento, outro processo vem e acaba bagunçando o trabalho que o anterior fez.

Vamos entender melhor como ocorre um desses problemas, chamados de condições de corrida (race conditions).


Condições de Corrida (Race Conditions)

Vamos imaginar um banco de dados de um banco, especificamente em uma conta de determinado cliente. Como tal cliente é programador, vive caindo dinheiro em sua conta, com bastante frequência.
Porém, o mesmo é super fã de Heavy Metal, e vive comprando CD's pela Internet.

Existem processos no sistema bancário que recebem a informação que o cliente recebeu um valor, e vão na conta dele e aumentam seu saldo.
Porém, outros processos recebem a informação que ele fez uma compra, e então vão em sua conta retirar tal valor.

Tudo simples, não é? Nem tanto, coisas bizarras costumam acontecer em sistemas operacionais com pseudo paralelismo, que ficam chaveando os processos.

Vamos imaginar que o programador tem mil reais em sua conta, e vai receber mais mil.
O processo recebe a informação desse depósito, checa o valor da conta do programador, faz a soma.
O próximo passo é esse processo colocar na conta o valor final, que é de 2 mil reais.

Nessa hora, a CPU alerta "ei processo, seu tempo aqui já acabou, agora é a vez de outro processo executar". Ou seja, antes de atualizar o valor na conta, houve um chaveamento, saiu um processo e vai entrar outro.

Nesse momento, o banco recebe a informação que o programador gastou cem reais em CD's do Iron Maiden. O processo responsável por isso vai na conta do programador e vê que ele tem mil reais na conta (lembre-se: não deu tempo do processo anterior atualizar o valor pra 2 mil).
Então retira R$ 100,00 da conta dele, e atualiza informando que agora ele tem R$ 900,00 na conta.

Ok, esse processo terminou, então o sistema operacional vai chamar aquele processo anterior pra ser concluído. Lembra onde ele parou? Sim, ele ia atualizar o valor pra R$ 2.000,00
E é isso que ele faz, é seu último passo, simplesmente colocar dois mil como saldo final na conta do programador.

No fim das contas, o programador tinha mil, recebeu mais mil, gastou cem e ficou com dois mil na conta. Perceberam? Simplesmente chavear, parar um processo e iniciar outro não funciona, acessar um mesmo dado (no caso, uma variável que armazena o saldo do cliente do banco) exige mais cuidado, pois quando dois ou mais processos atuam juntos em um mesmo local de memória, coisas bizarras podem acontecer por conta do chaveamento de processos.

Vamos estudar ideias e algoritmos, para aprender como evitar que tal tipo de coisa ocorra.
Obviamente isso tudo ocorreu porque o programador estudou Programação de Sistemas Operacionais e suspeitou que o sistema do banco fosse falho, então deixou pra receber o depósito na mesma hora que fez a compra, tudo proposital ;)











Região Crítica

Agora que temos uma ideia dos possíveis problemas, vamos mostrar soluções para evitar tais problemas, e o primeiro conceito que devemos ter em mente é o de região crítica.

Os problemas ocasionados pelas disputas entre dois processos ocorrem quando estes pretendem acessar recursos compartilhados, como memória, variáveis, arquivos etc.

A ideia base é: não permitir que dois ou mais processos acessem esses recursos compartilhados, essa região damos o nome de região crítica, por motivos óbvios.

Uma solução aparentemente simples seria fazer com que um processo acessando a região crítica não fosse interrompido. Porém, isso não depende do código de programação, é uma característica de hardware (tanto em questão de velocidade, chaveamento, capacidade de processamento etc).

Além do mais, pode ser que esse processo demore a ser executado. Ou seja, é essencial que os outros processos não demorem muito para serem executados, eles devem sempre ser chaveados e alternados, tendo sua vez de acessar a região crítica.

Trabalhando com esses conceitos de vários processos é bem fácil 'ficar louco', pois você pode programar belos, lindos e maravilhosos códigos, sem erros de lógica, compilação nem nada.
Mas na hora de rodar, sua máquina simplesmente trava, coisas loucas começam a acontecer.

Portanto, nossa tarefa, como programadores, é: permitir que todos os processos que desejem acessar a região crítica tenham sua vez, e garantir que um não vá influenciar o outro, tudo isso sem depender das especificações do hardware. Nossa solução tem que valer para todas as CPU's.

Nos artigos seguintes iremos estudar ideias e algoritmos interessantes sobre as mais diversas maneiras de resolver este tipo de problema, como através das ideias de exclusão mútua e "dormir e acordar".

Usaremos pitadas de linguagem C para vermos, na prática, toda a geniosidade e beleza por trás da programação de um sistema operacional.

Nenhum comentário: