PROCESSOS

Um processo é basicamente um programa em execução. Assoviado a cada processo esta seu espaço de endereçamento( uma lista de locais da memória a partir de um mínimo, normalmente zero, até um maximo que o processo pode ler e gravar).

Também associado a cada processo esta um conjunto de registradores, incluindo o contador de programa, o ponteiro da pilha e outros registradores de "hardware" e todas as demais informações nescessárias para executar o programa.

Uma boa maneira de obter um noção de processo é pensar nos sistemas de tempo compartilhado. Periodicamente o S.O decide parar de executar um processo e começa a executar outro. Quando o processo é suspenso temporariamente, mais tarde deve ser reiniciado exatamente no mesmo estado em que estava quando foi suspenso. Todas as informações sobre o processo devem ser explicitamente salvas em algum lugar durante a suspensão.

EX:

- o processo pode ter varios arquivos abertos para leitura,

- associado a cada arquivo esta um ponteiro que informa a posição atual (o numero de bytes ou o registro a ser lido em seguida),

- quando o processo é suspenso temporariamente todos esses ponteiros devem ser salvos para que uma chamada READ executada depois que o processo é reiniciado leia os dados adequados

Um processo suspenso consiste em seu espaço de endereço, chamado "imagem de núcleo" (em homenagem as memórias de núcleo magnético utilizadas antigamente) e sua entrada na tabela de processo que contém seus registradores, entre outras coisas.

As chamadas chaves, do sistema de gerenciamento de processos são as que lidam com a criação e o encerramento dos processos.

Ex.

- Um programa chamado SHELL (interpretador de comandos) lê comandos de um terminal

- o usuário digitou um comando que requisita a compilação de um programa

- o SHELL deve criar um novo processo que executará o compilador

- quando o processo termina a compilação ele executa uma "System Call" ( Chamada de sistema ) para encerrar-se

Se um processo pode criar um ou mais processos (referidos como processos – filhos), os quais podem criar processos – filhos, rapidamente chegamos a estrutura denominada árvore de processos.

 

FIG:

 

 

 

 

Processos relacionados que estão cooperando para executar uma tarefa freqüentemente precisam comunicar-se entre si e sincronizar suas atividades. Essa comunicação é chamada "COMUNICAÇÃO INTERPROCESSOS" .

Outras "System Calls" para processos estão disponíveis para requisitar mais memória (ou liberar memória não utilizada), esperar um processo filho terminar e substituir seu programa por um diferente.

Ocasionalmente há necessidade de transmitir as informações para um processo em execução que não esta parado aguardando-as.

Ex.

- um processo que esta se comunicando com outro em um computador diferente faz isso enviando mensagens por uma rede.

 

 

 

- para evitar a possibilidade de que uma mensagem ou sua resposta se perca, o remetente pode requisitar que o próprio S.O notifique-o depois de um número especificado de segundos, de modo que ele possa retransmitir a mensagem se nenhuma confirmação foi recebida ainda.

- depois de configurar esse temporizador, o programa pode continuar fazendo outro trabalho.

- passado o número especificado de segundos, o S.O envia um sinal para o processo. O sinal faz com que o processo suspenda temporariamente o que estava fazendo, salve seus registradores na pilha e comece a executar um procedimento especial de tratamento de sinal (ex: para retransmitir uma mensagem que presumivelmente esta perdida)

- quando a manipulação do sinal estiver concluída, o processo em execução é reiniciado no estado em que estava antes do sinal ( os sinais são o análogo de software das interrupções de hardware e podem ser gerados por uma variedade de causas além da expiração de temporizadores). Muitas exceções detectadas por "hardware" , como executar uma instrução legal ou usar um endereço inválido, também são convertidas em sinais para o processo causador.

A cada pessoa autorizada a utilizar o S.O é designado com uma UNID (de USER IDENTIFICATION – identificação de usuário) pelo administrador do sistema. Cada processo – filho tem a mesma UNID do seu pai.

Uma UNID denominada superusuário tem poder especial e pode transgredir muitas das regras de proteção.

Em instalações grandes, só o administrador de sistema sabe a senha necessártia para torna-se superusuário, mas muitos dos usuários comuns (especialmente estudantes ) despendem esforço considerável tentando localizar defeitos no sistema que os permitam tornar-se superusuários sem a senha.

 

 

Para se livrar dos efeitos das interrupções, o S.O criaram um modelo conceitual que consiste em processos seqüenciais rodando em paralelo. Cada processo tem seu proprio estado, e pode ser tratado como se rodasse em seu proprio processador virtual.

Algumas vezes os processos precisam interagir:

Ex: "compartilhamento de uma memória comum"

Tal interação pode levar a condição de corrida (situações onde a ordem de execução nos processos no tempo determina o resultado. Estas levam a um comportamento que não pode ser reproduzido, chamado de comportamento não deterministico ou não funcional)

 

FIG 2.6)

Dois processos acessam a memória compartilhada ao mesmo tempo

 

 

 

As variáveis "in" e "out" devem ser mantidas em duas palavras de memória acessíveis a todos os processos.

PROC A lê in 7 4(proxima entrada livre)

PROC B lê in 7; 7

PROC A lê o valor da proxima entrada livre e encontra o 7 colocado por B lá

O PROC A escreve o nome de seu arquivo de impressão na entrada 7 apagando o nome do arquivo escrito por B.

 

Para evitar as condições de corrida, introduzimos o conceito de região crítica ou seção critica (um trecho de seu código onde o processo está trabalhando em algo compartilhado, devendo-se desta forma que outro processo manipule a mesma entidade antes que o processo termine seu trabalho sobre ela). As regiões criticas correspondentes presentes nos diversos processos devem ser executadas mutuamente no tempo.

Os processos podem precisar comunicar-se com outros processos, usando as primitivas para a comunicação entre processos. Tais primitivas são usadas para garantir que dois processos não estejam nunca em regiões criticas correspondentes ao mesmo tempo, garantindo assim, a exclusão mutua. Um processo pode estar rodando pronto ou bloqueado podendo ter seu estado modificado no decorrer do seu processamento, quando um outro processo executar uma primitiva de comunicação.

Foram propostas diversas primitivas de comunicação entre processos, denominadas semáforos, contadores de evento, monitores e troca de mensagem. Teoricamente, elas são equivalentes, pois qualquer uma pode ser usada para implementar todas as outras. Nos sistemas reais, os semáforos e a troca de mensagem são os mais usados.

 

 

 

EXEMPLOS

Ex1) Problema do produtor – consumidor (buffer limitado – bounded buffer)

Neste dois processos compartilham um buffer de tamanho fixo. Em um dos processos o produtor coloca informações no buffer de tamanho fixo e no outro o consumidor retira as informações do buffer.

O problema acontece quando o processo (produtor / consumidor) tenta (colocar / retirar) informações no buffer e este esta (cheio / vazio).

Solução:

Para controlar o numero de itens no buffer devemos usar uma variável denominada COUNT. Se o numero máximo de itens for N, o código do processo produtor deve verificar se COUNT = N . Se for o produtor deve ser colocado para dormir, e se não for deve permanecer acordado.

A condição de corrida pode acontecer no caso do acesso COUNT ser irrestrito.

Se acontecer do buffer estar vazio:

- o processo consumidor lê COUNT e verifica que COUNT = 0

- ocorre uma interrupção

- o processo produtor começa a rodar e coloca 1 item no buffer e COUNT = 1

- OBS: o processo produtor infere que o processo consumidor esta dormindo porque no inicio de sua execução COUNT=0

- OBS: Lamentavelmente:

® o processo consumidor não esta dormindo, mas temporariamente interrompido.

® o processo consumidor esta na fila de prontos e não na de bloqueados.

- sendo assim o sinal de "WAKEUP" irá se perder

- quando o processo consumidor voltar a rodar ele verá COUNT=0 e irá dormir.

- quando o processo produtor voltar a funcionar vai encher o buffer e em seguida irá dormir.

- E assim, ambos dormirão para todo o sempre...

 

Essência do problema:

O sinal "WAKEUP" enviado para acordar um processo que não esta dormindo é perdido

Solução:

Fazer uma modificação no esquema SLEEP / WAKEUP, acrescentando a definição das primitivas de um bit ajustado sempre que um sinal é enviado para acordar um processo que já esta acordado. Quando o processo mais tarde tentar dormir ele não conseguirá. A primitiva SLEEP irá apagar este bit permitindo que o processo durma.

 

 

 

Ex 2) Problema "Produtor – Consumidor" usando SEMÁFOROS

Nesta situação usamos 3 semáforos:

- FULL

- EMPTY

- MUTEX

FULL: conta o numero de posições PREENCHIDAS no buffer

EMPTY: conta o numero de posições VAZIAS no buffer

MUTEX: assegura que mais de um processo acesse o buffer simultaneamente

- no início FULL=0, EMPTY=numero de posições disponíveis no buffer, MUTES=1

- os SEMÁFOROS inicializados em 1 e usados por dois ou mais processos para garantir que somente um deles possa entrar em sua região critica são chamados de SEMÁFOROS BINÁRIOS . Se cada processo executar um DOWN sobre um dos SEMÁFOROS BINÁRIOS imediatamente antes de entrar em sua região critica, e um UP imediatamente antes de deixa-la, a exclusão mutua de execução destas regiões críticas estará garantida.

OBS: é conhecido como ESCLUSÃO MÚTUA.

Ex 3) Problema "Produtor – Consumidor" usando contadores de eventos

Sobre um contador de evento é possível apenas três operações:

- READ (Ed): Retorna o valor corrente de E

- ADVANCE (E): Incrementa E de 1 unidade atomicamente

- AWAIT (E,v): Espera até que E tenha um valor maior ou igual a v

 

Nesta solução dois contadores de eventos são usados:

- IN: conta cumulativamente o numero de itens que o PRODUTOR colocou no buffer desde o início da operação do programa

- OUT: conta cumulativamente o numero de itens que o CONSUMIDOR removeu do buffer desde o início da execução do programa

- IN ³ OUT: e nunca IN ³ TAMANHO DO BUFFER

- quando o PRODUTOR computar novo item, ele deve verificar para ver se há lugar no buffer usando a chamada de sistema AWAIT

- a principio OUT=0

- se o PRODUTOR gerar N+1 itens antes do consumidor retirar alguma, a execução do AWAIT o fará esperar até que OUT=1

- Sob a lógica do consumidor:

antes de tentar remover o K-ésimo item ele simplesmente espera até que IN=K ou seja que o PRODUTOR tenha colocado Kitens no buffer


1