Você está na página 1de 17

30/10/2018 SO - Processos e Threads

Processos & Threads


Sistemas Operacionais
PUCPR - Escola
Politécnica
© Luiz A. P. Lima Jr.

Disciplina Introdução Processos & Threads Gerência de Memória Sistemas de Arquivos Entrada e Saída

Tópicos
1.  Introdução
Introduçã o

Processos: o conceito mais importante em sistemas operacionais. Comunicaçã o entre Processos


(IPC)
Como vá rios "programas" podem rodar simultaneamente em um computador, se ele possui um ú nico Exclusã o mú tua com
espera ocupada
processador (ou menos processadores ou nú cleos – "cores" – do que o nú mero de programas em execuçã o)?
Exclusã o mú tua com
bloqueio e desbloqueio
Multiprogramação (time-sharing): o processador é chaveado entre diversos programas dando a cada um
Escalonamento de Processos
algumas dezenas de milissegundos de processamento produzindo a ilusã o de paralelismo (conhecida també m por
Threads
"pseudo-paralelismo").

Ardua tarefa do sistema operacional: tratamento das mú ltiplas atividades realizadas em "paralelo".

1.1.  O Modelo de Processos


Todos os programas que rodam em um computador (o sistema operacional, inclusive) sã o compostos de um conjunto de processos.

PROCESSO = instância de um programa em execução + seu contexto. Este contexto é formado por:

valores das variá veis;


valores dos registradores do processador (PC = Program Counter e SP = Stack Pointer); e
recursos (lista de arquivos abertos, alarmes pendentes, listas de processos relacionados, etc.)

Um PROGRAMA é um arquivo contendo um conjunto de informações que descrevem como construir um processo. Estas informaçõ es
incluem:

Identi icação do formato binário: o formato do arquivo executá vel que permite ao kernel interpretar as demais informaçõ es contidas no arquivo;
Instruções em linguagem de máquina: representam o algoritmo do programa;
Endereço de entrada do programa: identi ica a localizaçã o da instruçã o na qual a execuçã o do programa deverá começar (i.e., localizaçã o do
programa principal main);
Dados: valores usados para inicializar as variá veis e constantes;
Tabelas de símbolos e de relocação: localizaçã o e nomes das funçõ es e variá veis de um programa;
Informações de bibliotecas compartilhadas e linkagem dinâmica: lista de bibliotecas dinâ micas necessá rias ao programa.
Outras informações que descrevem como o processo deverá ser construı́do.

Já um processo é essencialmente uma "cá psula" contendo informaçõ es necessá rias para executar um programa. Vá rios processos podem rodar o
mesmo programa. Multiprogramação (ou time-sharing) é portanto o rá pido chaveamento do processador entre os diversos processos (Figuras 1 e 2)
causando a ilusã o de paralelismo.

FIGURA 1. Multiprogramação: a CPU é chaveada entre os diversos processos

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 1/17
30/10/2018 SO - Processos e Threads
FIGURA 2. Multiprogramação e a ilusão de paralelismo

Consequência: a velocidade de execução de um processo não é constante, ou seja, di icilmente previsı́vel com precisã o. Portanto, cuidados
especiais precisam ser tomados para processos com caracterı́sticas de processamento em tempo real (i.e., aqueles que tê m exigê ncias com relaçã o a
tempo de execuçã o).

Trocas de Contexto

A mudança do processo sendo executado para um outro processo é chamada de troca de contexto.

FIGURA 3. Trocas de contexto

1.1.1.  Criação de processos
MS-DOS (monoprogramado): suspende a execuçã o do processo pai até que o ilho termine sua execuçã o.

Todos os processos no sistema formam uma árvore de processos, sendo a raiz no Linux um
processo especial chamado systemd instanciado na inicializaçã o do sistema. Quando o processo
pai de um outro processo termina antes do processo ilho, o processo systemd o adota.

Comandos shell:
ps aux: lista todos os processos no sistema;
pstree [-p] : mostra a á rvore de processos do sistema (raiz = systemd)
top ou htop: lista processos de forma interativa
Identi icador de processos: PID (ú nico no sistema). O PID de um processo é sempre um
nú mero inteiro maior que zero. O PID do processo systemd é 1, por de iniçã o. FIGURA 4. Hierarquia de processos e o comando fork()
Identi icador de grupo de processos: pgrp (grupos de processos: interessantes para
sinalizaçã o – visto posteriormente).

A Chamada de Sistema fork

SOs da famı́lia UNIX: operaçã o fork (cria có pia exata do processo pai) (pai e ilho rodando em pseudo-paralelismo). No processo ilho, fork
retorna 0 (zero). No processo pai, fork retorna o identi icador (PID) do processo ilho.

FIGURA 5. A chamada de sistema fork

1.1.2.  Estados de um Processo

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 2/17
30/10/2018 SO - Processos e Threads
Uma vez que há a necessidade de comunicaçã o entre processos, e que esta comunicaçã o se dá por compartilhamento de memó ria, sã o
necessá rios mecanismos de sincronizaçã o destes processos (para que nã o haja con litos no acesso aos dados compartilhados). Assim, bloqueia-se o
processo até que uma entrada esteja disponı́vel. Isto també m evita que um processo que nã o tenha condiçõ es ló gicas de prosseguir (por estar
aguardando por alguma condiçã o) seja escolhido pelo SO para executar. Um processo nestas condiçõ es encontra-se no estado BLOQUEADO.

O escalonador é a parte do SO que entra em açã o durante a troca de contexto para determinar qual será o pró ximo processo a ser escolhido para
rodar. O escalonador é o "coraçã o" do SO e, em muitos casos, é implementado diretamente em linguagem de má quina por questõ es de e iciê ncia.

FIGURA 6. Estados de um processo

1. Escalonador escolhe novo processo para executar ( im do tempo de processador alocado ao processo corrente).
2. Escalonador entrega o processador ao novo processo.
3. Processo bloqueado para fazer entrada de dados (por exemplo, espera dados do teclado – scanf/cin) ou outra situaçã o na qual não tem
condições lógicas de rodar.
4. Operaçã o de entrada e saı́da (por exemplo, entrada de dados) concluı́da ou processo volta a ter condiçõ es de rodar novamente.

Detalhes para ativar/bloquear processos estã o dentro do escalonador.

FIGURA 7. O escalonador de processos

1.2.  Implementação do Modelo de Processos


Para implementar o modelo o SO utiliza uma tabela de processos com uma entrada por processo.

Cada entrada (UNIX) possui entre outras coisas:

estado do processo;
memó ria alocada;
valores do contador de programa + ponteiro de pilha;
estado dos seus arquivos abertos;
prioridade;
tudo o que for necessá rio para que o processo possa retomar a sua execuçã o mais tarde do ponto onde parou na troca de contexto.

2.  Comunicação entre processos (IPC) [topo]

IPC = Inter-Process Communication

Meios de implementar comunicaçã o entre processos: pipes, arquivos, copiar/colar, mensagens, memória compartilhada, sockets (em rede).

2.1.  Condições de Corrida

Exemplo: Fila de impressã o.

Qualquer processo que queira imprimir precisa colocar o seu documento na ila de impressã o (compartilhada). O processo de impressã o
retira os documentos na ordem em que chegaram na ila enviando-os à impressora (veja igura).

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 3/17
30/10/2018 SO - Processos e Threads

FIGURA 8. Cenário para exemplo de condição de corrida

Se a ila é compartilhada, isto signi ica que seus dados, assim como os indicadores de frente e fim da ila també m o sã o.

Um processo que queira imprimir um documento precisa executar a operaçã o de inserçã o


na ila que envolve basicamente os seguintes comandos:

1. fim++ (incrementa o indicador do im da ila)


2. insere documento na posiçã o fim da ila

Se dois processos resolvem simultaneamente imprimir um documento e se, por causa do


compartilhamento do tempo do processador, o primeiro processo for interrompido (por ter
acabado o seu tempo) entre os comandos 1 e 2, entã o o segundo processo nã o poderá imprimir
seu arquivo, e a ila icará em um estado invá lido. Situaçõ es semelhantes a esta chamam-se
"condições de corrida". FIGURA 9. Situação em que ocorre condição de corrida

Há uma condiçã o de corrida quando dois ou mais processos estã o acessando dados compartilhados e o resultado depende de qual processo roda
quando. O problema no exemplo de ila de impressã o foi que o segundo processo tentou inserir seu documento na ila antes que o primeiro processo
tivesse terminado de fazê -lo.

Para evitar as condiçõ es de corrida, precisamos implementar algoritmos de exclusão mútua de execução e, para tanto, precisamos identi icar as
regiões críticas do programa.

2.2.  Regiões Críticas
Objetivo: impedir que mais de um processo acesse o dado
compartilhado ao mesmo tempo (i.e., exclusã o mú tua). A
implementaçã o de exclusã o mú tua é essencial para sistemas
operacionais multiprogramados.

Divisã o do programa do processo em:

processamento local;
manipulaçã o de dados compartilhados.

A parte do programa cujo processamento, por manipular dados


compartilhados, pode levar à ocorrê ncia de condiçõ es de corrida é
chamada REGIÃO CRÍTICA.
FIGURA 10. Região crítica de um programa
Objetivo: nunca permitir que dois processos entrem
simultaneamente em suas regiõ es crı́ticas correspondentes (i.e., referentes à mesma variá vel compartilhada). Isto é feito por meio da implementaçã o
de um algoritmo de exclusão mútua de execução.

2.2.1.  Condições para uma boa solução ao problema da condição de corrida

1. Dois ou mais processos nã o podem estar simultaneamente dentro de suas regiõ es crı́ticas correspondentes.
2. Nenhum processo rodando fora de sua regiã o crı́tica pode bloquear a execuçã o de outro processo.
3. Nenhum processo pode ser obrigado a esperar inde inidamente para entrar em sua regiã o crı́tica.
4. Nenhuma consideraçã o pode ser feita a respeito da velocidade relativa dos processos, ou a respeito do nú mero de processadores do sistema.

Há duas classes de algoritmos de exclusã o mú tua:

exclusã o mú tua com espera ocupada; e


exclusã o mú tua com bloqueio e desbloqueio

2.3.  Exclusão Mútua com Espera Ocupada

2.3.1.  Inibição de Interrupções
http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 4/17
30/10/2018 SO - Processos e Threads
O processo inibe todas as interrupçõ es logo apó s entrar em uma regiã o crı́tica, e as habilita ao sair. Assim, ele nã o poderá ser interrompido
mesmo apó s acabar a sua fatia de tempo de execuçã o.

FIGURA 11. Situação com potencial condição de corrida FIGURA 12. Inibição de interrupções

Problemas:

Nã o é boa prá tica dar ao usuá rio a capacidade de inibir interrupçõ es (pode ser mal usada transformando o SO em um sistema
monoprogramado, com apenas aquele processo rodando).
Pode haver vá rios processadores (e a inibiçã o só pode ocorrer naquele que estiver executando o processo no momento).

2.3.2.  Variáveis de Travamento

Variá vel booleana compartilhada (ocupada):

false: nenhum processo está na regiã o crı́tica;


true: existe algum processo na regiã o crı́tica.

Antes de entrar na regiã o crı́tica, o processo veri ica a variá vel para ver se já existe outro processo manipulando a memó ria compartilhada.

FIGURA 13. Variável de travamento usada pelos processos A e B para acesso à região crítica

Processo (A) Processo (B)

while(true) { while (true) {


regiao_nao_critica(); regiao_nao_critica();
while(ocupada) { /* espera */ } while (ocupada) { /* espera */ }
ocupada = true; ocupada = true;
regiao_critica(); regiao_critica();
ocupada = false; ocupada = false;
} }

Notas a respeito de C/C++


O tipo bool com valores true ou false é nativo em C++. Exemplo: bool teste = false; (declara variá vel teste inicializando-a com o valor
false).
A especi icaçã o ISO C11 incorporou o tipo bool à linguagem C (para usá -lo, basta incluir em seu programa: #include<stdbool.h>).
Em C, valores inteiros diferentes de zero = true e zero = false. Assim, while(true) é equivalente a while(1) e corresponde a um laço in inito (uma
vez que a condiçã o do while nunca deixará de ser verdadeira).

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 5/17
30/10/2018 SO - Processos e Threads
Problemas:

espera ocupada (consumindo 100% de CPU);


se dois ou mais processos quiserem entrar na regiã o crı́tica e izerem o teste ao mesmo tempo, eles atribuirã o todos o valor 1 à variá vel
ocupada e entrarã o todos na regiã o crı́tica!

2.3.3.  Estrita alternância

"vez": variá vel compartilhada. Se vez == 'A', entã o é a vez do processo A entrar na sua regiã o crı́tica. Se vez == 'B', é a vez do processo B (e
assim por diante, se houve mais processos). Inicialmente vez == 'A'.

FIGURA 14. Variável compartilhada "vez" usada pelos processos A e B para acesso à região crítica

Procura-se com isto evitar que 2 ou mais processos achem ao mesmo tempo que podem entrar nas suas respectivas regiõ es crı́ticas, como
ocorreu com variá veis de travamento.

Processo (A) Processo (B)

while(true) { while (true) {


regiao_nao_critica(); regiao_nao_critica();
while(vez != 'A') { /* espera */ } while (vez != 'B') { /* espera */ }
regiao_critica(); regiao_critica();
vez = 'B'; vez = 'A';
} }

Problemas:

Espera ocupada (teste contı́nuo da variá vel compartilhada "vez"): consumo desnecessá rio do tempo do processador.
Um processo só terá vez novamente se o outro entrar e sair da sua regiã o crı́tica. Isto representa uma violaçã o da condiçã o 2 para uma boa
soluçã o ao problema da condiçã o de corrida.

Este esquema para exclusã o mú tua é inadequado quando um dos processos é mais lento que o outro.

2.3.4.  Algoritmo de Peterson

O Algoritmo de Peterson evita o bloqueio se o processo nã o estiver interessado em entrar em sua regiã o crı́tica. Antes de entrar em uma regiã o
crı́tica o processo deve executar a funçã o entra_rc(id) (id é o identi icador do processo). Ao sair, deve executar sai_rc(id). Supondo que há
apenas dois processos com identi icadores 0 e 1, e que sã o compartilhadas as variá veis:

int vez;
bool interessado[2];

o có digo das funçõ es será :

void entra_rc(int id)


{
char outro = 1 - id; /* id do outro processo */
interessado[id] = true;
vez = id;
while (vez == id && interessado[outro]) { /* espera ocupada */ }
}

void sai_rc(int id)


{
interessado[id] = false;
}

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 6/17
30/10/2018 SO - Processos e Threads

Leia o material relativo a este algoritmo no livro-texto da disciplina.

2.4.  Exclusão Mútua com Bloqueio e Desbloqueio


O problema com a espera ocupada é que ela gasta desnecessariamente tempo do processador. A soluçã o seria bloquear a execuçã o do processo
quando a sua entrada na regiã o crı́tica nã o for permitida. Para isso, existem duas "primitivas" ou "comandos bá sicos":

sleep(): bloqueia o processo (para de do estado "rodando" para "bloqueado") que a chamou até que outro processo o "acorde".
wakeup(pid): acorda o processo cujo identi icador é pid.

2.4.1.  O Problem do Produtor/Consumidor com Fila (bu er) de tamanho limitado


O produtor insere itens na ila (buffer) e o consumidor os retira da ila.

FIGURA 15. O problema do produtor/consumidor.

Problemas:

Para o produtor: deseja colocar itens na ila, mas ela está cheia.
Para o consumidor: deseja remover itens da ila, mas ela está vazia.

Solução: bloquear o produtor (sleep) quando a ila estiver cheia (nã o tem condiçõ es ló gicas de executar); bloquear o consumidor se a ila
estiver vazia.

Sendo cont uma variá vel compartilhada para controlar o nú mero de elementos na ila e sendo MAXFILA o nú mero má ximo de elementos na ila:

#define MAXFILA 8
int cont = 0; /* variável compartilhada */
Fila fila; /* variável compartilhada */

void produtor() void consumidor()


{ {
int item; int item;
while(true) { while(true) {
item = produz_item(); if(cont == 0) /*vazia*/
if (cont == MAXFILA) /*cheia*/ sleep();
sleep(); item = remove(fila);
insere(fila,item); cont--;
cont++; if (cont == MAXFILA-1)
if (cont == 1) /* 1o elemento */ wakeup(produtor);
wakeup(consumidor); consome_item(item);
} }
} }

Ainda assim pode haver condição de corrida:

Fila vazia (cont == 0);


Consumidor testou se cont == 0, mas ainda nã o dormiu;
Troca de contexto;
Produtor coloca item na ila e tenta acordar o consumidor, sem que este esteja dormindo (perde-se a noti icaçã o de wakeup).
Consumidor volta a executar e dorme;
Produtor enche a ila e dorme.
Quando todos os processos icam bloqueados: DEADLOCK ou IMPASSE

Problema: perda do wakeup.

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 7/17
30/10/2018 SO - Processos e Threads
Solução: quando chegar um wakeup e nã o estiver dormindo, incrementa um contador e, da pró xima vez que quiser dormir, veri ica antes se o
contador é maior que zero. Se for o caso, decrementa o contador sem dormir.

2.4.2.  Semáforos

Um semáforo é um contador de "wakeups".

Operaçõ es elementares para semá foros (operaçõ es atô micas = "indivisı́veis"):

down (sem) Generalizaçã o do sleep.


if (sem == 0) sleep();
sem--;

up (sem) Generalizaçã o do wakeup.


sem++;
if (sem == 1)
wakeup(processo_dormindo_em_sem);

2.4.3.  Semáforos e o Problema do Produtor/Consumidor

Trê s semá foros (trê s situaçõ es para bloquear processo):

cheio: conta nú mero de posiçõ es da ila (buffer) já preenchidos;


vazio: conta nú mero de posiçõ es da ila ainda vazias;
mutex: para assegurar que mais de um processo acesse o buffer ao mesmo tempo.

Os dois primeiros semá foros sã o necessá rios para a sincronizaçã o entre processos, e o terceiro para implementar exclusã o mú tua de execuçã o.

#include "Fila.h" /* definição do tipo/classe Fila, seu tamanho máximo MAXFILA e sua operações */

Semaforo mutex = 1;
Semaforo vazio = MAXFILA;
Semaforo cheio = 0;

Fila f; /* Fila compartilhada (inicialmente vazia) */

void Produtor() void Consumidor()


{ {
int item; int item;
while (true) { while (true) {
item = produz_item(); down(cheio);
down(vazio); down(mutex);
down(mutex); item = f.Remove();
f.Insere(item); up(mutex);
up(mutex); up(vazio);
up(cheio); consome_item(item);
} }
} }

Assim, para proteger uma regiã o crı́tica de forma que apenas um processo por vez a execute:

1. crie um semá foro chamado "mutex" e atribua a ele o valor inicial 1;


2. antes de entrar na regiã o crı́tica: down(mutex);
3. ao sair da regiã o crı́tica: up(mutex).

2.4.4.  O Problema Jantar dos Filósofos

Este é um problema clá ssico de sincronizaçã o entre processos. N iló sofos estã o
sentados ao redor de uma mesa redonda tendo um prato de macarrã o diante de si. Entre
cada dois pratos existe um garfo e a vida de um iló sofo é pensar, comer, pensar, comer e
assim inde inidamente. No entanto, para comer, o iló sofo precisa necessariamente de dois
garfos (um que está à sua esquerda e o outro à sua direita). Quando terminou de pensar
(por ter fome), tenta pegar os garfos vizinhos para poder comer (garfos nã o podem ser
emprestados do outro lado da mesa). Ao terminar de comer, devolve os garfos à mesa.

Primeira Solução

A operaçã o pega_garfo coloca o processo para dormir, se o garfo nã o estiver


disponı́vel.

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 8/17
30/10/2018 SO - Processos e Threads
O nú mero do garfo à direita do iló sofo i é i. O nú mero do garfo à esquerda do FIGURA 16. Jantar dos Filósofos
iló sofo i é (i+1)%N.

#define N 5

void filosofo(int i)
{
while (true) {
pensa();
pega_garfo(i); /* espera que garfo da DIREITA esteja livre */
pega_garfo((i+1)%N); /* espera que garfo da ESQUERDA esteja livre */
come();
devolve_garfo(i);
devolve_garfo((i+1)%N);
}
}

Problema: se todos terminarem de pensar ao mesmo tempo e tentarem pegar o garfo da direita. Ao tentar pegar o garfo da esquerda, nã o
conseguirã o e irã o todos dormir (estado de "bloqueado"). A situaçã o na qual todos os processos de um sistema estã o bloqueados é chamada
"deadlock" (ou "travamento").

Pseudo-solução: Apó s pegar o garfo da direita, veri ica se o da esquerda está disponı́vel. Se nã o estiver, devolve o garfo da direita e tenta
novamente pegar os dois garfos mais tarde.

Problema: Há a possibilidade dos processos icarem pegando e devolvendo garfos inde inidamente. A situaçã o na qual os processos estã o
rodando (estados "rodando" e "pronto") somente tentando pegar os garfos sem jamais comer é chamada "livelock" ou "starvation".

Melhora da pseudo-solução: fazer com que o iló sofo, quando nã o conseguir obter o segundo garfo, devolva o primeiro, espere um tempo
aleató rio e tente novamente. Neste caso, a probabilidade de starvation é pequena, mas é existente e há contextos nos quais nã o pode haver falhas. Por
exemplo, software de controle de segurança em usina nuclear...

Segunda Solução

A regiã o crı́tica da primeira soluçã o corresponde à regiã o onde dados compartilhados sã o manipulados, envolvendo as cinco linhas abaixo da
operaçã o pensa(). Poderı́amos assim acrescentar um semá foro biná rio mutex para evitar que mais de um iló sofo entre na sua regiã o crı́tica
simultaneamente.

#define N 5

Semaforo mutex = 1;

void filosofo(int i)
{
while (true) {
pensa();
down(mutex);
pega_garfo(i);
pega_garfo((i+1)%N);
come();
devolve_garfo(i);
devolve_garfo((i+1)%N);
up(mutex);
}
}

Problema: Um só iló sofo comerá em um dado momento!

Terceira Solução (máximo de paralelismo para um número arbitrário de ilósofos)

vetor estado para controlar se um iló sofo está COMENDO, PENSANDO ou FAMINTO em um dado instante. Inicialmente, todos os iló sofos
estã o PENSANDO. O vetor estado deve ser uma memó ria compartilhada pois cada iló sofo precisa conhecer o estado de seus vizinhos (se um
deles estiver COMENDO, o iló sofo nã o poderá passar para o estado de COMENDO).
se i é o nú mero de um iló sofo, (i+1)%N é o nú mero do seu vizinho esquerdo e (i-1+N)%N é o nú mero do seu vizinho direito.
um semá foro para cada iló sofo (vetor de semá foros s, inicialmente valendo 0 cada um) para que possa ser bloqueado caso nã o haja garfos
disponı́veis.
mutex: semá foro biná rio para controlar acesso à memó ria compartilhada (estado).

#define N 5 /* número de filósofos */


#define PENSANDO 0 /* filósofo pensando */
#define FAMINTO 1 /* filósofo tentando pegar garfos */
#define COMENDO 2 /* filósofo comendo */

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 9/17
30/10/2018 SO - Processos e Threads

#define ESQ (i+1)%N


#define DIR (i-1+N)%N

int estado[N]; /* vetor (compartilhado) p/ controlar estados */


Semaforo mutex = 1; /* exclusão mútua para região crítica */
Semaforo s[N]; /* 1 semáforo por filósofo. Inicialmente, todos == 0 */

/* Protótipos */

void filosofo(int i);


void pega_garfos(int i);
void devolve_garfos(int i);
void teste(int i);
void pensa();
void come();

/* Funções */

void filosofo(int i)
{
while (true) {
pensa();
pega_garfos(i);
come();
devolve_garfos(i);
}
}

void pega_garfos(int i)
{
down(mutex); /* entra na região crítica */
estado[i] = FAMINTO; /* filósofo i está com fome */
teste(i); /* tenta mudar de estado para comendo */
up(mutex); /* sai da região crítica */
down(s[i]); /* fica bloqueado se não conseguiu mudar de estado */
}

void devolve_garfos(int i)
{
down(mutex); /* entra na região crítica */
estado[i] = PENSANDO; /* filósofo i terminou de comer */
teste(DIR); /* verifica se vizinho da direita pode comer */
teste(ESQ); /* verifica se vizinho da esquerda pode comer */
up(mutex); /* sai da região crítica */
}

void teste(int i)
{
if (estado[i] == FAMINTO && estado[ESQ] != COMENDO && estado[DIR] != COMENDO) {
estado[i] = COMENDO;
up(s[i]);
}
}

2.5.  Outros problemas de sincronização de processos

2.5.1.  Grafos de Precedência

Usando semá foros é possı́vel estabelecer uma precedência de execução de processos (ou procedimentos em diferentes processos). Esta
precedê ncia é especi icada por um grafo como no da igura abaixo:

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 10/17
30/10/2018 SO - Processos e Threads

FIGURA 17. Grafo de Precedência

O vı́deo abaixo demonstra a precedê ncia de contagem seguindo o grafo da igura acima.

0:00 / 0:27

2.5.2.  Barreiras

Outro uso comum de semá foros é na implementaçã o de barreiras como na abaixo:

FIGURA 18. Barreira

Assista o vı́deo abaixo que mostra 2 processos compartilhando uma barreira. O primeiro que chega aguarda pelo outro para que ambos passem
juntos pela barreira.

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 11/17
30/10/2018 SO - Processos e Threads

0:00 / 0:33

Pense em como semá foros poderiam ser usados para implementar tanto a precedência estabelecida pelo grafo da Fig. 17 como uma barreira
entre N processos (ao invé s de apenas 2).

Há muitos outros problemas de sincronizaçã o de processos. No livro "The Little Book of Semaphores" você poderá encontrar muitos deles com
suas soluçõ es usando semá foros.

3.  Escalonamento de Processos [topo]

Quase todos os processos alternam computaçã o de dados (usando a CPU) com requisiçõ es de E/S. Se temos vá rios processos concorrendo para o
uso da CPU, nã o seria o caso de deixar uns processos utilizarem a CPU enquanto outros esperam que suas operaçõ es de E/S (lentas) terminem?

Quando dois ou mais processos tê m condiçõ es de rodar, é o escalonador que decide qual será o pró ximo a receber tempo de CPU. Esta decisã o é
baseada em um algoritmo de escalonamento.

Reló gio ("clock"): fornece interrupçõ es perió dicas (~60 Hz). As decisõ es de escalonamento podem ocorrer a cada k-é sima interrupçã o de reló gio
(k>=1):

Escalonamento não-preemptivo: o processo que obtiver direito de rodar, rodará até que seja bloqueado para E/S ou para esperar por outro
processo (semé foro, por exemplo), ou até terminar.
Escalonamento preemptivo: há uma interrupçã o e suspensã o temporá ria da execuçã o de processos nã o bloqueados apó s um tempo má ximo
ixado.

Há trê s categorias de algoritmos de escalonamento:

Em lote: sistemas nã o-interativos. Preemptivo ou nã o preemptivo com longos intervalos de tempo para cada processo.
Interativo: preempçã o é essencial.
Em tempo-real: executam somente processos que visam o progresso da aplicaçã o (e nã o, genericamente, qualquer processo, como no caso
interativo).

3.1.  Escalonamento em Lotes

3.1.1.  First-come, rst-served (FCFS)

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 12/17
30/10/2018 SO - Processos e Threads

FIGURA 19. FCFS: Processos executam até serem bloqueados ou até terminarem

Nã o-preemptivo.
A CPU é atribuı́da aos processos na ordem em eles a requisitam.
Há uma ú nica ila de processos prontos.
Novos processos sã o colocados no inal da ila.
Processos sã o executados até que terminem ou sejam bloqueados.
Processos bloqueados que icam prontos sã o colocados no inal da ila.
Vantagens:
facilidade de implementaçã o;
justo.
Desvantagens:
processos orientados a E/S se tornam muito lentos.

3.1.2.  Job mais curto primeiro (SJF)

Pressuposiçã o: os tempos de execuçã o dos processos sã o conhecidos previamente (razoá vel em determinados sistemas, por exemplo:
processamento de transaçõ es bancá rias ao inal do dia).
O escalonador escolherá o job mais curto primeiro.

processos A, B, C e D (tempos: 8, 4, 4 e 4, respectivamente)


na ordem: A = 8; B = 12; C = 16; D = 20 (mé dia = 14);
tomando o mais curto primeiro: B = 4; C = 8; D = 12; A = 20 (mé dia = 11).
Para 4 processos com tempos a, b, c e d: mé dia (4a + 3b + 2c + d)/4, portanto a contribui mais para a mé dia;

Restriçã o: adequado se todos os jobs estã o disponíveis simultaneamente.

3.2.  Escalonamento em Sistemas Interativos

3.2.1.  Round-robin

Simples, justo.

Quantum: intervalo de tempo durante o qual o processo poderá usar o processador. Durante a troca de contexto é necessá rio, entre outras
coisas:

1. salvar informaçõ es do processo atual na tabela de processos de forma a que possa voltar a executar posteriormente a partir do ponto onde
parou;
2. escolher o pró ximo processo a ser executado (seguindo um algoritmo de escalonamento);
3. recuperar o estado do pró ximo processo.

Todo este procedimento nã o é instantâ neo, levando um tempo S.

Escalonador: precisa manter uma lista de processos prontos para rodar.

FIGURA 20. Escalonamento Round-Robin

Como determinar o tamanho do quantum? Suponhamos que a troca de contexto entre processos leve 1 ms (isto é , S = 1ms).

(a) Quantum pequeno (por exemplo, 4 ms)

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 13/17
30/10/2018 SO - Processos e Threads
Para cada 5 ms de processamento ú til, há 1 ms adicional para administraçã o do sistema (troca de contexto). Isto signi ica que
20% do tempo do processador é gasto com a sobrecarga de administraçã o do sistema. Isto representa um uso ine iciente do
processador.

(b) Quantum grande (por exemplo, 100 ms)

Menos de 1% do tempo do processador é gasto com administraçã o do sistema. No entanto, suponhamos que haja 10 processos
rodando. Se usuá rios interativos teclam ENTER ao mesmo tempo, pode ser que o ú ltimo processo tenha que esperar 1 segundo para
receber tempo de CPU e executar o que for necessá rio. Desta forma, o tempo de resposta para um quantum grande pode nã o ser
aceitá vel.

A soluçã o é um compromisso: em geral, para uma troca de contexto de 1ms, um quantum de 20 a 50ms é razoá vel.

Considerando o quantum Q=2 unidades de tempo, e os seguintes processos com respectivos inı́cios e duraçõ es, o escalonamento é illustrado
na igura abaixo.

Processo Início Duração


A 0 6
B 1 4
C 3 3

FIGURA 21. Escalonamento Round-Robin

3.2.2.  Escalonamento com Prioridades

No escalonamento por Round-Robin, todos os processos sã o igualmente importantes. No entanto, há situaçõ es onde deseja-se que certos
processos tenham prioridades sobre outros (por executar funçõ es vitais ao sistema, vı́deo em tempo real) ou tenham menor prioridade (simulaçõ es ou
e-mail, por exemplo).

A idé ia, entã o, é associar prioridades aos processos: processos com maior prioridade tê m mais tempo de CPU.

Mas como evitar que processos com prioridade maior monopolizem o processador? O escalonador decrementa a prioridade do processo que está
rodando a cada interrupçã o de tempo.

Em UNIX/Linux existe o comando nice para reduzir a prioridade de um processo.

Exercício: utilizaçã o do comando nice em Linux.

Prioridades e Round-Robin

FIGURA 22. Escalonamento com prioridades

Algoritmo: enquanto houver processos com prioridade 4: round-robin na classe (processos em outras classes nã o sã o escolhidos). Se prioridade
4 vazia, round-robin na prioridade 3 e assim por diante. Cada vez que o processo recebe o tempo para executar, a sua prioridade é decrementada.
Quando todos os processos se encontrarem no mesmo nı́vel, as prioridades iniciais sã o restauradas.

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 14/17
30/10/2018 SO - Processos e Threads

3.2.3.  Shortest Process Next (SPN)

Adaptaçã o do SJF (de lotes) para processos interativos.


Di iculdades de estimar o tempo de um processo interativo (que geralmente segue o padrã o: espera por comando, executa comando, espera por
outro comando, executa outro comando, e assim por diante) para determinar qual é o mais curto.

Estimativa a partir de seu comportamento passado:

T0: estimativa para determinado processo;


T1: tempo medido de sua pró xima execuçã o;

Atualizaçã o da estimativa: aT0 + (1-a)T1. A escolha de a determina se o processo de estimativa se esquecerá ou nã o rapidamente das
execuçõ es anteriores.

a=1/2:

T0, T0/2+T1/2, T0/4+T1/4+T2/2, T0/8+T1/8+T2/4+T3/2

Em 3 execuçõ es, o peso de T0 na nova estimativa caiu para 1/8. Esta té cnica de fazer uma estimativa tomando a mé dia ponderada do valor sendo
medido e a estimativa anterior é conhecida como "envelhecimento" ou "aging" => fá cil de se implementar quando a=1/2 (deslocamento de bits à
direita).

3.2.4.  Escalonamento por Loteria

Princípio básico: distribuir bilhetes de loteria aos processos. Prê mio = tempo de CPU (també m aplicá vel a qualquer outro recurso). Ao se fazer
escalonamento, um bilhete é sorteado e o processo que possui o bilhete receberá o recurso (a CPU, no caso).

Processos prioritá rios recebem mais bilhetes.


Facilidade de estabelecer parâ metros de prioridade: um processo que possui uma fraçã o f dos bilhetes obterá em torno de uma fraçã o f da CPU.
Novos processos tê m probabilidade de vencer já o pró ximo sorteio – alta responsividade do sistema.
Processos podem trocar bilhetes entre si (por exemplo, processos servidores só precisam de bilhetes caso existam clientes para os seus
serviços).

3.2.5.  Escalonamento por Fração Justa

Preocupaçã o com a distribuiçã o do tempo de CPU relativa aos donos do processos, e nã o apenas aos processos em si.

usuá rio 1: 9 processos;


usuá rio 2: 1 processo;
http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 15/17
30/10/2018 SO - Processos e Threads
Usando round-robin, o usuá rio 1 obterá 90% do tempo da CPU, enquando o usuá rio 2 apenas 10%.

Soluçã o: considerar o proprietário de cada processo antes de escaloná -lo:

A cada usuá rio é alocada uma fraçã o da CPU e o escalonador escolhe os processos de forma a garantir esta fraçã o.

2 usuá rios: 50% da CPU para cada.


Usuá rio 1: 4 processos A, B. C e D.
Usuá rio 2: 1 processo E.
Escalonamento: A E B E C E D E A E B E C E D E...
Se usuá rio 1 tiver 2 vezes mais tempo de CPU do que o usuá rio 2:
Escalonamento: A B E C D E A B E C D E...
+ vá rias outras possibilidades...

3.3.  Escalonamento em Sistemas de Tempo-Real


Sistemas de tempo real: o tempo é uma funçã o essencial.Tipicamente: dispositivos externos geram estı́mulos => computador deve reagir a eles
dentro de um intervalo de tempo estabelecido.

Sistemas de tempo real críticos: prazos absolutos que devem ser cumpridos;
Sistemas de tempo real não críticos: o descumprimento ocasional de um prazo é indesejá vel, poré m tolerá vel.

Quando um evento (perió dico ou aperió dico) ocorre, o escalonador deve escalonar os processos que respondem à quele evento de tal maneira
que os prazos sejam cumpridos.

é necessá rio que se conheça previamente o comportamento dos processos de tempo real. Dependendo do tempo de execuçã o de cada processo
para tratar de cada evento e da periodicidade dos eventos, o sistema pode ser escalonável ou nã o.

4.  Threads ou processos leves [topo]

fork() : uma có pia idê ntica do processo pai é criada → có pia do có digo, das variá veis, recursos do SO (arquivos abertos, sinais, etc.) →
dispendioso!

Threads (ou "processos leves"): luxos de execuçã o dentro do mesmo processo compartilhando
dados e recursos do processo. No UNIX/Linux, um thread:

Existe dentro de um processo e usa os recursos do processo;


Tem o seu pró prio luxo de controle independente enquanto existir o processo pai e o SO dá suporte
a ele;
Pode compartilhar os recursos do processo com outros threads igualmente independentes;
Morre se o seu processo que os conté m morre

Para um programador, o conceito de um "procedimento" ou "funçã o" que roda independentemente


da funçã o que a chamou pode ser a melhor descriçã o de um thread.

FIGURA 23. Threads internamente a um processo

FIGURA 24. Threads como chamadas a funções

Um processo é criado pelo sistema operacional e conté m informaçõ es sobre os recursos de um programa e seu estado de execuçã o, incluindo:

id do processo, do grupo de processos, do usuá rio e do grupo do usuá rio;


ambiente;
diretó rio de trabalho;
instruçõ es do programa;
registradores;
pilha do programa;
heap (memó ria alocada dinamicamente);

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 16/17
30/10/2018 SO - Processos e Threads
descritores de arquivos;
açõ es para sinais recebidos;
bibliotecas compartilhadas;
ferramentas para comunicaçã o entre processos (mensagens, pipes, memó ria compartilhada).

FIGURA 25. Processo com um único thread

Threads usam e existem dentro destes recursos, mas sã o capazes de ser escalonados pelo sistema operacional e de rodar como entidades
independentes dentro de um processo. Um thread pode ter um luxo independente de controle e ser escalonado porque manté m:

o seu pró prio PC (contador de programa);


o seu pró prio ponteiro de pilha (stack pointer);
os seus pró prios registradores;
as suas pró prias propriedades de escalonamento (e.g. prioridades, etc.);
o seu pró prio conjunto de sinais pendentes e bloqueados;
e seus pró prios dados que lhe sã o especı́ icos.

FIGURA 26. Processo com 2 threads

Um processo pode conter muitos threads, e todos eles compartilham os recursos dentro do processo e sã o executados dentro do mesmo espaço
de endereçamento. Dentro de um programa com mú ltiplos threads há simultaneamente mú ltiplos pontos de execuçã o.

Devido ao fato de threads dentro do mesmo processo compartilharem recursos:

mudanças feitas por um thread a um recurso compartilhado do sistema (como alterar variá vel global ou fechar um arquivo) serã o vistas por
todos os outros threads;
dois ponteiros que tê m o mesmo valor, apontam para o mesmo dado;
leitura e escrita das mesmas posiçõ es de memó ria sã o possı́veis, e, portanto, há a necessidade de sincronizaçã o explı́cita pelo programador
(isto é , usar semáforos, por exemplo, para evitar condiçõ es de corrida).

Veja exercı́cios propostos no livro texto.


Este trabalho está licenciado com uma Licença
Creative Commons - Atribuição-NãoComercial 4.0 Internacional.

http://www.ppgia.pucpr.br/~laplima/ensino/so/materia/02_processos.html 17/17