Transcript Processo A

Sistemas operacionais

Carlos Oberdan Rolim Ciência da Computação Sistemas de Informação

* Baseado no material do Prof. Luis Cláudio Gubert

Processos

Processos

Conceito de Processo Escalonamento de Processos Operações com Processos Processos Cooperativos Comunicação entre Processos

Conceito de processo

Um sistema operacional executa uma variedade de programas: Sistema Batch – jobs Sistema Tempo Compartilhado (Time-shared) – programas do usuário ou tarefas Livros usam os termos job e processo quase que indeterminadamente.

Processo – um programa em execução; execução do processo deve progredir de maneira seqüencial.

Um processo inclui: Contador de programa pilha Seções de dados

Criação de processos

Sistemas operacionais precisam assegurar a criação de todos os processos necessários Há quatro eventos que fazem com que processos sejam criados Inicio do sistema Execução de uma chamada ao sistema de criação de processo por um processo em execução Uma requisição do usuário para criar um novo processo Inicio de um job em lote Inicialização de processos em segundo plano pelo SO Daemons Tecnicamente um processo é criado quando um outro processo faz uma chamada ao sistema de criação de processos Fork  clone identico ao processo que o chamou

Término de processos

Após efetuar sua tarefa processo termina por alguma das seguintes razões: Saída normal (voluntária) Saída por erro (voluntária) Erro fatal (involuntário) Cancelamento por um outro processo (involuntário)  Ex.comando kill

Hierarquia de processos

Relação pai x filho de processos No Unix um processo pai, todos os seus filhos e descendentes formam um grupo de processos Quando um sinal é enviado todos os processos do grupo de processos recebem o sinal e decidem o que fazer Exemplo de inicialização do sistema Processo init que cria os teminais de video, que disparam os interpretadores de comando, que disparam novos processos e assim por diante Init é o processo raiz Windows não apresenta hierarquia de processos Todos são iguais Somente existe uma especie de hierarquia quando um processo cria outro Pai recebe handle dos filhos para os controla los.Porém pode passar esse handle para outros processo invalidando conceito de hierarquia

Estados de processo

Durante a execução de um processo, ele altera seu estado Novo (new): O processo está sendo criado.

Executando (running): instruções estão sendo executadas.

Esperando (waiting): O processo está esperando algum evento acontecer.

Pronto (ready): O processo está esperando ser associado a um procesador.

Terminado (terminated): O processo terminou sua execução.

Diagrama de estados de processos

Process Control Block (PCB)

Informações associadas com cada processo.

Estado do Processo Contador de Programas Registradores da CPU Informações de escalonamento da CPU Informação de Gerenciamento de memória Informação para Contabilidade Informações do status de E/S

Processo Control Block

Troca de CPU entre processos

Filas de escalonamento de processos

Fila de Job – conjunto de todos os processos no sistema.

Fila de Processos prontos (Ready queue) – conjunto de todos os processos residentes na memória principal, prontos e esperando para executar.

Fila de dispositivos – conjunto dos processos esperando por um dispositivo de E/S.

Migração de processos entre as várias filas.

Fila de processos pronto e várias filas de E/S

Representação de Escalonamento de Processos

Escalonadores

Escalonador de Jobs (Long-term scheduler) processos prontos.

– seleciona quais processos devem ser trazidos para a fila de Escalonador da CPU (Short-term scheduler) CPU para ele.

– seleciona qual processo deve ser executados a seguir e aloca

Inclusão de escalonador de nível médio

Escalonadores (cont)

Escalonador da CPU é invocado muito freqüentemente (milisegundos) → (deve ser um fato).

Escalonador de Jobs é invocada muito infreqüentemente (segundos, minutos) → (pode ser lento).

O escalonador de Jobs controla o grau de multiprogramação.

Processos podem ser descritos como: Processos com E/S predominante (I/O-bound process) – gasta mais tempo realizando E/S do que computando, muitos ciclos curtos de CPU.

Processos com uso de CPU predominante (CPU-bound process) CPU.

– gasta mais tempo realizando computações; poucos ciclos longos de

Troca de contexto

Quando CPU alterna para outro processo, o sistema deve salvar o estado do processo deixando o processador e carregar o estado anteriormente salvo do processo novo.

Tempo de troca de contexto é sobrecarga no sistema; o sistema não realiza trabalho útil durante a troca de contexto.

Tempo de Troca de Contexto é dependente de suporte em hardware.

Criação de processos

Processo pai cria processo filho, o qual, por sua vez, pode criar outros processos, formando uma árvore de processos.

Compartilhamento de Recursos Pai e filho compartilham todos os recursos.

Filho compartilha um subconjunto dos recursos do pai.

Pai e filho não compartilham recursos.

Execução Pai e filho executam concorrentemente.

Pai espera até filho terminar.

Criação de processos (cont) Espaço de endereçamento

Filho duplica espaço do pai.

Filho tem um programa carregado no seu espaço.

Exemplos no UNIX

Chamada de sistemas fork

cria um novo processo

Chamada de sistemas execve

é usada após o

fork

para sobrescrever o espaço de memória do processo com um novo programa.

Uma Árvore de Processos em um Sistema UNIX Típico

Terminação de processos

Processo executa última declaração e pede ao sistema operacional para decidir (exit).

Dados de saída passam do filho para o pai (via wait).

Recursos do processo são desalocados pelo sistema operacional.

Pai pode terminar a execução do processo filho (abort).

Filho se excedeu alocando recursos.

Tarefa delegada ao filho não é mais necessária.

Pai está terminando.

Comunicação entre Processos

Processos Independentes.

Não afetam nem são afetados por outros processos.

Processos Cooperados.

Compartilham: Memória; Arquivos; Dispositivos de E/S; Etc.

Vantagens da cooperação entre processos:

Compartilhamento de Informações Aumento na velocidade da computação Modularidade Conveniência

Processos Cooperados Compartilhamento de dados.

Processo “ produtor ” de dados.

Insere informações no buffer Processo “ consumidor ” de dados, Remove informações do buffer

Buffer:

Limitado: Produtor e Consumidor.

Ilimitado: Somente Consumidor.

Implementação.

Via memória compartilhada; Via IPC (Interprocess Comunication - SO)

Buffer Circular

Implementação como um array circular.

próxima entrada 8 7 6 4 5 Introdução de um contador de quantidade.

Produtor e Consumidor manipulam o contador. próxima saída Contador 5

Implementação

i aponta para posição anterior ao primeiro elemento f aponta para último elemento c indica o número máximo de elementos presentes N indica o número máximo de elementos

Buffer vazio

i == f c == 0

Buffer cheio

i == f c ==N

Comportamento básico

int buffer[N]; int c = 0; int i = 0, f = 0;

Produtor Consumidor

while (true) while ( true) f = (f + 1) % N; i = ( i + 1 ) % N; buffer[f] = produz(); consome( buffer[i]); c++; c--;

Problemas

Produtor insere em posição que ainda não foi consumida Consumidor remove de posição já consumida

Condições de Corrida (Race Conditions)

Processos trocam dados, as vezes, através de memória Principal, arquivo...

Região/seção crítica Porção de programa que pode levar a condição de disputa Tipo de memória não altera o problema Alguma forma de garantir que apenas 1 esteja na região crítica

Condições de Corrida Mecanismo de

Sincronização

.

Garante o compartilhamento de recursos e a comunicação entre os processos.

Garante a integridade e a confiabilidade dos dados compartilhados.

Condições de Corrida: Situações onde dois ou mais processos estão acessando dados compartilhados.

O resultado final pode variar de acordo com a ordem de execução.

Condições de Corrida

Exemplo 1: 7 1 Processo A 3 7+1 7 8 5 Resultado Final: Contador = 6 ( ERRO!

) 7 2 6 6 Processo B 7-1 4

Condições de Corrida

Exemplo 2: 10 Y 5 4 Valor armazenado pelo 3 perdido .

6 X 6 Processo A 2 8 suspenso recebe CPU 7 1 próxima entrada 8 9 7 7 4 Processo B 8 5 3 7 recebe CPU suspenso

Condições de Corrida

Região Crítica: Parte do código onde é feito acesso a recursos compartilhados, e que podem levar a condições de corrida.

Ex: Processo A.

Código normal Início da Seção Crítica (Protocolo de Entrada) Seção Crítica Término da Seção Crítica (Protocolo de Saída) Código normal

Concorrência em programas

Enquanto um processo estiver usando um recurso, os outros devem aguardar até que o recurso esteja liberado.

Exclusão Mútua .

Exclusividade no acesso a um determinado recurso.

A exclusão mútua deve afetar os processos concorrentes quando um deles estiver em uma região crítica.

Regiões críticas

Como evitar condições de disputa?

Implementar exclusão mútua Um por vez junto à região Garantir que dois ou mais processos estarão sincronizados, e só um dentro da região crítica Operando sobre dados compartilhados

Regiões críticas

Quatro requisitos básicos para os algoritmos Dois ou mais processos NUNCA podem estar simultaneamente na região crítica Não considerar velocidade relativas dos processos Quem estiver fora da região não pode bloquear quem quer entrar Tem que entrar em algum momento!

Regiões críticas

Algumas alternativas

Espera ocupada Inibição de interrupção, variáveis de bloqueio, estrita alternância, TSL Bloqueio/desbloqueio Sleep/wakeup Semáforos Monitores Troca de mensagens “caixa postal”, rendezvous

Problemas de Sincronização

Velocidades muito diferentes dos processos: Podem causar problema de solução de sincronização entre processos.

Ex: Problema produtor / consumidor .

Starvation (Indefinitely Postponed): Um processo nunca consegue executar sua região crítica e acessar o recurso compartilhado.

Outros processos são sempre prioritários.

Também chamado de problema de inversão de prioridade

Soluções de Hardware Inibição das interrupções:

Solução mais simples para a exclusão mútua; Falha de Proteção: O processo precisa voltar a habilitar as interrupções.

Instrução

Test-and-Set

.

Utilização de uma variável para testar a possibilidade de executar a região crítica.

O processo impedido de executar sua região crítica executa um loop de espera.

Gasto de CPU.

Inibição de interrupção

Processo desativa interrupções quando entra na região crítica Evita a interrupção Ninguém, além dele, estará processando Problemas Usuário é não confiável Sistemas multiprocessados Kernel pode precisar interromper (e.g., atualizar lista de prontos)

Test and Set Locked (TSL)

Precisa de apoio do hardware Operação para mover e ajustar de forma atômica um registrador de flag Só entra se registrador igual a 0 entrar_regiao: TSL register, flag // copiar flag em register e faz flag = 1 CMP JNZ RET register, #0 // register ? 0 : 0, 1 entrar_regiao // volta para novo TSL // retorna ao processo sair_regiao: MOV flag, #0 RET // coloca 0 em flag // retorna ao processo Processo X { código...

entrar_regiao() R.C.

sair_regiao() código...

}

Soluções de Software

Estrita alternância Bloqueio/desbloqueio Sleep/wakeup Semáforos Monitores Troca de mensagens “caixa postal”, rendezvous Em geral, as soluções por software resolvem a exclusão mútua, mas geram a espera ocupada (Busy Wait): Teste contínuo de uma variável até que ocorra uma mudança no seu valor; O processo impedido de executar sua região crítica executa um loop de espera.

Gasto de CPU.

Estrita alternância

Cabana com uma lousa dentro Só um na cabana, só um número na lousa Quem quiser entrar na região, verifica a lousa Entra na cabana. Se a lousa não tiver o seu número, sai e dá uma volta Se estiver, entra na região e, ao sair, escreve o próximo número

Estrita alternância

Outra opção: várias cabanas, com várias lousas Cada um examina todas as lousas mas altera apenas a sua TRUE na lousa  processo na região Quem quiser entrar verifica as outras lousas Se todas falsas, escreve TRUE na sua e entra Escreve FALSE ao sair Vários processos Verificação não é atomica

Sleep/Wakeup

TSL funciona, mas é espera ocupada...

Problema de prioridade invertida L com prioridade baixa na região critica, e H com alta prioridade tentando entrar Produtor/consumidor com buffer limitado Buffer cheio? Produtor dorme

Starvation

Em vez de esperar, um processo comunica diretamente ao outro SLEEP e WAKEUP Buffer vazio? Consumidor dorme...

Sleep/Wakeup

Extern N, count; #define N 100 int count = 0; void producer(void) { } int item; while (TRUE) { } produz(&item); if (count == N) sleep(); armazena(item); count++; void consumer (void) { } int item; while (TRUE) { } if (count == 0) sleep(); remove(item); count--; if (count == N-1) wakeup (producer); consome(item); if (count == 1) wakeup(consumer);

Sleep/Wakeup

Problemas Condição de disputa em “count” Buffer vazio, consumidor sabe que é zero Interrompido Produtor produz e faz count++ e “acorda” consumidor Consumidor volta e vai dormir Produtor enche o buffer e vai dormir Sinal não usado é perdido...

Adicionar um bit; sleep com bit, ignora...

Geralmente não funciona

Semáforos Ferramenta de sincronização criada por Dijkstra (1965); Características:

Variável inteira; Não negativa.

Manipulados por duas operações

atômicas

:

DOWN ou Wait (P - Proberen - Testar) UP ou Signal (V - Verhogen - Incrementar)

Implementados como chamada ao sistema.

Semáforos

Dijskstra (1965) Variável que armazena sinais para uso futuro Novo tipo de variável: semáforo Operações de down e up DOWN(s) { s --; if (s < 0) sleep(); } UP(s) { s ++; if (s <= 0) wakeup_alguém() } Operações atômicas: teste, incremento...

Semáforos (Counting Semaphores); Semáforos Binários (Mutual Exclusion Semaphores);

Semáforos e cervejas...

Hora Você

03:00 Chega em casa 03:05 Olha no freezer, sem cerveja 03:10 Sai para o bar 03:15 03:20 Chega no bar 03:25 Compra uma grade e sai 03:30 03:35 Chega em casa 03:36 Guarda as cervejas 03:40 03:50 03:51 03:51

Colega

Chega em casa Olha no freezer, sem cerveja Sai para o bar Chega no bar Compra uma grade e sai Chega em casa Guarda as cervejas SEM ESPAÇO!!!

Semáforos e cervejas...

Hora Você

03:00 Chega em casa 03:04 DOWN(&birita) 03:05 Olha no freezer, sem cerveja 03:10 Sai para o bar 03:15 03:19 03:20 Chega no bar 03:25 Compra uma grade e sai 03:30 03:35 Chega em casa 03:36 Guarda as cervejas 03:37 UP(&birita) 03:38

Colega

Chega em casa

DOWN(&birita)

Olha no freezer, COM cerveja

Semáforos e cervejas...

Semáforo birita iniciado com 1

Você (A)

P(&birita); Se (sem cerveja) compra cerveja; V(&birita);

Colega (B)

P(&birita); Se (sem cerveja) compra cerveja; V(&birita); Execução Antes birita 1 A: P() 0 B: P() -1 A: V() 0 B: V() 1 fila B Você RC RC fim Colega espera pronto, RC fim

Semáforos – Comportamento básico

down(s) if (s == 0) bloqueia_processo(); else s--; up(s) if (s == 0 && existe processo bloqueado) acorda_processo(); else s++;

Semáforos e E/S

Cada dispositivo com seu semáforo Início de E/S Processo gerente faz um down “Sleep” Fim de E/S Processo faz um up (libera) “Wakeup”

Semáforos

Falsa impressão de simplicidade Inversão da ordem de DOWNs Buffer completo  produtor bloqueado e mutex 0 Consumidor tenta acesso ao buffer  bloqueado Deadlock! (depois...) Facilitar a sincronização, com primitivas de alto nível Hoare (1974) & Hansen (1975) Monitores

Semáforos

Semáforos fazem duas coisas Exclusão mútua Só um dentro Sincronização Um

processo

espera por um “sinal” de outro

processo

Binário ou não

Produtor – Consumidor com Semáforo

semaforo cheio = 0; semaforo vazio = N;

Produtor Consumidor

while (true) while(true) wait(vazio); wait(cheio); f = (f+1)%N; i = (i+1) %N; buffer[f] = produz(); consome(buffer[i]); signal(cheio); signal(vazio);

Semáforos

#define N 100 typedef int semaphore; semaphore mutex = 1; empty = N; full =0; extern mutex, empty, full; void producer(void) { int item; while (TRUE) { produz(&item); down(&empty); down(&mutex); armazena(item); up(&mutex); up(&full); } } void consumer (void) { } int item; while (TRUE) { } down(&full); down(&mutex); remove(&item); up(&mutex); up(&empty) consome(item);

Mutex (Mutual Exclusion)

Estrutura de sincronização bastante simples que pode estar em dois estados: Locked; Unlocked.

Duas operações são definidas sobre um mutex: Lock; Unlock.

lock Processo 1 unlock lock

Mutex

unlock Processo 2

Locks

Convenções Lock::Obtêm() antes de acessar dados Bloqueia se o lock não estiver disponível Lock::Libera() depois de acessar Erro se tentar liberar antes de obter

Processo A

lock *birita; birita->obtêm(); Se (sem cerveja) compra cerveja; birita->libera();

Processo B

extern lock *birita; birita->obtêm(); Se (sem cerveja) compra cerveja; birita->libera();

Variáveis condicionais

São iguais a semáforos??? Não!

Semáforos têm valor, VCs não Um signal (V, Up) em um semáforo sempre incrementa, mesmo que não haja pendências. Depois, em um wait (P, Down), o processo decrementa e segue Nas VCs, um signal sem wait simplesmente é perdido. Depois, se houver um wait, ele vai sempre aguardar um signal, mesmo que já tenha havido 1 bilhão de signals

Variáveis condicionais

Sleep/wakeup são a mesma coisa? Quase Sleep/wakeup se aplicam a processos e threads específicas, e não a variáveis

Monitores Mecanismo de sincronização de alto nível proposto por Hoare (1974) e Brinch Hansen (1975);

Conjunto de procedimentos e funções de alto nível agrupadas em um módulo Similar a uma classe, na OO

Característica mais importante é a implementação automática da exclusão mútua:

Somente um processo pode estar ativo em um monitor em um determinado instante de tempo.

Somente um procedimento sendo executado, até a completude, por vez Programador livre de especificar as restrições

Monitores

Compilador tem que oferecer suporte a monitores Chamadas aos procedimentos do monitor com certas “regalias” Se algum outro processo estiver ativo no monitor, suspende o novo processo Geralmente implementados com o uso de semáforos binários Poucas linguagens implementam (e.g., ADA)

Monitores

Idéia similar a um objeto Alterar propriedades, só através dos métodos Entrar na região crítica, só através do monitor

Monitores e semáforos

Dependem de memória comum Em memória distribuída a coisa não funciona Como “compartilhar” uma variável?

Troca de mensagens

Primitivas de SEND/RECEIVE Enviar para e receber de Recepção bloqueante Novos problemas Perda de mensagens ACK, NACK Recebimento duplicado Controle de seqüência Segurança...

Troca de mensagens

Produtor/consumidor

Consumidor

enviar N msg vazias while (TRUE) { receive (produtor, &m); extrai_item(); send (produtor, &m); consome_item(); }

Produtor

while (TRUE) { produz_item(); receive (consumidor, &m); constroi_msg(); send (consumidor, &m); }

Troca de mensagens

Diretamente com sockets Plataformas de portabilidade PVM, MPI, Linda, P4, Parmacs...

Caixa postal e rendevouz

SEND/RECEIVE assíncrono Só bloqueia com caixa postal cheia Buffer (CP) finito SEND/RECEIVE síncrono Bloqueia até entregar Mais fácil de implementar

Caixa postal e pipes

Pipe são caixas postais Fronteiras de mensagens não existem Emular com leitura por tamanho fixo

Threads

Uma thread (ou um processo leve) é uma unidade básica de utilização da CPU; ela consiste de: Contador de programas Conjunto de registradores Espaço de pilha Uma thread compartilha com seus pares (outras threads vinculadas): Seção de código Seção de dados Recursos do sistema operacional Coletivamente conhecida como uma tarefa (task).

Um processo tradicional ou pesado é igual a uma tarefa com uma thread

Threads (cont)

Em uma tarefa com múltiplas threads, enquanto uma thread servidora está bloqueada e esperando, uma segunda thread na mesma tarefa pode executar.

Cooperação de múltiplas threads no mesmo job traz alta vazão (throughput) e aumento de desempenho.

Aplicações que requerem compartilhamento de um buffer comum (p.ex.: produtor consumidor) se beneficiam da utilização de threads.

Threads provêem um mecanismo que permite a processos seqüenciais realizarem chamadas de sistemas bloqueantes enquanto podem ao mesmo tempo explorar o paralelismo.

Threads suportadas pelo Kernel (Mach e OS/2).

Threads em nível de usuário; suportadas acima do kernel, via um conjunto de chamadas de bibliotecas no nível do usuário (Projeto Andrew do CMU).

Abordagem híbrida implementa tanto threads em nível do usuário quanto suportadas pelo kernel (Solaris 2).

Múltiplas Threads

Suporte a threads no solaris 2

Solaris 2 é uma versão de UNIX com suporte a threads nos níveis do usuário e do kernel, suporte a multiprocessamento simétrico e escalonamento em tempo real.

LWP (processos leves) – nível intermediário entre threads no nível do usuário e do kernel.

Necessidades de recursos para cada tipo de thread: Kernel thread: pequenas estruturas de dados e uma pilha; troca de contexto entre threads não requer alteração em informações acessadas pela memória – relativamente rápidas.

Processos leves (LWP): PCB com dados de registradores, informações de contabilidade e de memória; troca de contexto entre LWP é relativamente lenta.

Thread em nível de usuário: somente necessita de pilha e contador de programa; não havendo envolvimento do kernel tem-se uma troca de contexto rápida. Kernel somente enxerga os LWPs que suportam threads em nível de usuário.

Threads no solaris

Comunicação entre processos (IPC)

Mecanismo para processos se comunicarem e sincronizarem suas ações.

Sistema de mensagens – processos se comunicam uns com os outros sem utilização de variáveis compartilhadas.

Suporte a IPC provê duas operações uma para envio outra para recebimento: send(mensagem) – tamanho da mensagem fixo ou variável receive(mensagem) Se P e Q querem se comunicar, eles necessitam: Estabelecer um link de comunicação entre eles Trocar mensagens via send/receive Implementação de links de comunicação Físico (ex. Memória compartilha, barramento de hardware) Lógico (ex. Propriedades lógicas)

Questões de implementação

Como são estabelecidas as ligações?

Pode um link estar associado com mais de dois processos?

Quantos links podem existir entre cada par de processos comunicantes?

Qual a capacidade de um link?

O tamanho da mensagem utilizado pelo link é fixo ou variável?

O link é unidirecional ou bidirecional?

Comunicação direta

Processos devem nomear o outro explicitamente: send (P, mensagem) – envia uma mensagem ao processo P receive(Q, mensagem) – recebe uma mensagem do processo Q Propriedades dos links de comunicação Links são estabelecidos automaticamente.

Um link é associado com exatamente um par de processos comunicantes.

Entre cada par de processos existe exatamente um link.

O link pode ser unidirecional, mas é usualmente bidirecional.

Comunicação indireta

Mensagens são dirigidas e recebidas de caixas postais – mailboxes (também chamadas de portas).

Cada mailbox possui uma única identificação.

Processos podem se comunicar somente se eles compartilham a mailbox.

Propriedades do link de comunicação: O link é estabelecido somente se os processos compartilham uma mailbox comum Um link pode estar associado com muitos processos.

Cada par de processos pode compartilhar vários links de comunicação.

Link pode ser unidirecional ou bidirecional.

Operações Criar uma nova mailbox Enviar e receber mensagens através da mailbox Destruir uma mailbox

Comunicação indireta (cont) Compartilhamento de Mailbox

P1, P2, e P3 compartilham mailbox A.

P1, envia; P2 e P3 recebem.

Quem recebe a mensagem?

Soluções:

Permitir que um link esteja associado com no máximo dois processos.

Permitir somente a um processo de cada vez executar uma operação de recebimento.

Permitir ao sistema selecionar arbitrariamente por um receptor. Remetente é notificado de quem foi o receptor.

Bufferização

Fila de mensagens associada ao link; implementada em uma dentre três formas.

Capacidade Zero – 0 mensagens Remetente deve esperar pelo receptor (rendezvous).

Capacidade Limitada – tamanho finito de n mensagens Remetente deve aguardar se link está cheio.

Capacidade Ilimitada – tamanho infinito Remetente nunca espera.

Condições de exceção – Recuperação de error

Término de processos Perda de Mensagens Embaralhamento de Mensagens