Вы находитесь на странице: 1из 14

Sistemas Operacionais

1 - Threads

 Threads são unidades básicas de utilização de CPU e consistem de Program


Counter (PC), Register Set (conjunto de registros) e pilha → lightweight
process (processos leves);
Obs. São considerados processos leves, ou seja, theads são funções;

 Threads de uma mesma tarefa (ou processo) compartilham dados, como


variáveis globais e recursos do sistema operacional, como arquivos abertos e
sinais;

 Todas as theads de um processo compartilham o mesmo espaço de


endereçamento; (memória do processo disponível)

Perda de performace ganho de performace


Heavy weiaht light weiaht
Problema: conflito em acesso
as variáveis;
Theads são mais rápidos de processo;

 Troca de contexto entre threads é muito mais rápida que entre processos;

 Comunicação entre threads é mais rápida e mais fácil de implementar que


entre processos;

1
Sistemas Operacionais

 Threads fornecem mecanismos que permitem a processos seqüenciais


alcançarem paralelismo mesmo fazendo chamadas de sistema bloqueantes;

2
Sistemas Operacionais

 Sistemas operacionais multi-thread são aqueles que permitem mais de uma


thread por processo (ou tarefa);

 Multi-thread a nível de kernel (núcleo):


 SO implementa multi-thread em seu núcleo;
 a troca de contexto entre threads é mais lenta pois deve ser feita como
uma interrupção do kernel;
 a divisão do tempo é feita entre threads, e não entre processos →
distribuição igual do tempo entre todas as threads.

 Multi-thread a nível de usuário (user level):


 SO não enxerga um processo como multi-thread → maior velocidade
de troca de contexto entre threads no nível do usuário;
 uma chamada de sistema de uma thread pode colocar todo o processo
em espera;
 a divisão do tempo é feita por processos (ex. se há dois processos, um
com uma thread e outro com 100 threads, o primeiro rodará 100 vezes mais
rápido);
 exemplo: Linux com P-threads;

 Multi-thread híbrido - implementa os dois esquemas de multi-threading (ex.


Windows NT e Solaris);

3
Sistemas Operacionais

2 - IPC (Comunicação Entre Processos)

Mecanismos que permitem que processos possam compartilhar dados e trocar


informações.

2.1 Condições de corrida

Condição de corrida é aquela que pode ocorrer quando dois (ou mais) processos
têm acesso compartilhado a dados e um processo consegue alterar o conteúdo
dos dados sem que o outro processo o possa perceber.
ex: Spool de impressão - Considerações:
1. processos entram com o nome do arquivo a ser impresso num diretório de
impressão com entradas numeradas e possuindo duas variáveis
compartilhadas: in (próxima entrada livre) e out (próximo arquivo a ser
impresso);
2. processo impressor verifica se há arquivos neste diretório. Havendo, imprime
o arquivo e exclui o nome do arquivo;
Cenário:

• Processos A e B decidem escrever no diretório quase simultâneamente;


• Processo A lê in e armazena seu valor (7) - troca de contexto;
• Processo B lê in e armazena seu valor (7) - B coloca arquivo a ser impresso na
entrada 7 e atualiza in para 8 - troca de contexto;
• Processo A coloca arquivo na entrada 7 e atualiza in para 8;
• processo impressor não percebe nenhuma falha, pois in está com 8, mas o

arquivo de B jamais será impresso → condição de corrida.


4
Sistemas Operacionais

2.2 Regiões críticas (RC)

 Para se evitarem condições de corrida → exclusão mútua de execução:


Proibição da utilização de um recurso compartilhado por mais de um processo
ao mesmo tempo.
 Região crítica: parte do código de um processo em que este acessa variáveis
compartilhadas;
 Condições para que dois processos possam cooperar sem ocorrer condições de
corrida:
1. Dois ou mais processos não podem estar simultaneamente dentro de suas
regiões críticas correspondentes;
2. Nenhuma consideração pode ser feita a respeito da velocidade relativa dos
processos, ou a respeito do número de processadores disponíveis no sistema;
3. Nenhum processo que esteja rodando fora de sua região crítica pode bloquear
a execução de outro processo;
4. Nenhum processo pode ser obrigado a esperar indefinidamente para entrar em
sua região crítica.

2.3 Exclusão Mútua com Espera Ocupada

Quando um processo estiver acessando memória compartilhada dentro de uma


região crítica, mais nenhum outro poderá fazê-lo.

2.4 Semáforos

 Propostos por Dijkstra em 1965: surge um novo tipo de variável semáforo,


com o objetivo de implementar a exclusão mútua de uma maneira segura. As
operações sobre semáforos são chamadas pelos usuários mais implementadas
pelo SO no kernel, de uma maneira indivisível. É está característica que
permite que condições de corrida sejam evitadas no acesso de variáveis
compartilhadas.

 Operações:

1. WAIT: utilizado por um processo para entrar na RC.


2. SIGNAL: utilizado por um processo para sair da RC.
5
Sistemas Operacionais

OBS:
 As operações sobre semáforos são atômicas (indivisíveis);
 Se o semáforo for binário, após um SIGNAL ele permanecerá em zero,
porém, haverá menos um processo dormindo naquele semáforo;
 Semáforos devem ser implementados como chamadas do sistema;
 No caso de sistemas com mais de um processador, utilizam-se variáveis de
travamento TSL para garantir que somente um deles escreva num semáforo
em um instante de tempo;
 Quando um processo é bloqueado em um semáforo, ele é adicionado à sua
lista de processos bloqueados.

Podemos visualizar um semáforo como uma estrutura que possui uma variável
inteira que contém um valor inicial e a qual todo acesso é feito por meio das duas
operações atômicas: wait e signal. Outros nomes também são usados :
1. down ou sleep para wait
2. up ou wakeup para signal

2.4.1 – Implementação de Semáforos


typedef struct semáforo
{
int valor; // valor inteiro
tipo_lista lista; // lista de processos bloqueados no semáforo
}

WAIT
1. Quando um processo deseja entrar em uma RC, executa o wait.
2. wait decrementa o valor do semáforo.
3. Se o valor < 0, já existe um processo acessando a RC.
a. O processo que executou o wait vai para a fila de processos
bloqueados no semáforo.
b. Este processo é bloqueado.
4. Se o valor = 0, significa que não havia nenhum processo acessando a RC, e o
processo que executou o wait pode entrar na RC.
a. Como o wait decrementa o valor do semáforo, agora a RC está
bloqueada para novos acessos.
6
Sistemas Operacionais

wait()
{
s.valor--; // decrementa valor do semaforo
if(s.valor < 0) // se valor < 0, sig. que existe processo acessando a RC
{
adicona o processo P a s.lista;
bloqeuia P;
}
else
processo executanto wait entra na RC;
}

SIGNAL

1. Quando um processo deseja sair de uma RC, executa o signal.


2. signal incrementa o valor do semáforo.
3. Se valor <= 0, significa que existe processo(s) bloqueado(s) no semáforo.
a. Remove um processo da lista de processos em espera.
b. Transfere o processo para a lista de processos prontos.
c. Este processo que estava bloqueado aguardando para entrar na RC
pode agora acessar a RC.
4. Se valor > 0, não existe nenhum processo bloqueado no semáforo.
5. O processo que executou o signal sai da sua região crítica.

signal()
{
s.valor++; // incrementa valor do semaforo
if(s.valor <= 0) // se o semaforo for <= 0, sig. que existe processo bloqueado
{
remove um processo P de s.lista;
desbloqueia P;
}
processo executanto signal sai da RC;
}

Obs:
1. Semáforos utilizados para implementar exclusão mútua são inicializados
com valor 1 (um).
2. O módulo do valor negativo do semáforo em um determinado momento
indica o número de processos bloqueados naquele semáforo.

7
Sistemas Operacionais

2.4.2 – Utilização de Semáforos para evitar condições de corrida

Podemos utilizar semáforos para resolver o problema das regiões críticas de


n processos. Um semáforo chamado MUTEX (mutual exclusion), com valor
inicial 1, é compartilhado pelos n processos.
Se o semáforo é uma variável compartilhada por n processos, porque não
ocorre condições de corrida nesta variável?
☛ Porque um semáforo é uma variável especial, onde o SO trata operações
sobre semáforos como operações indivisíveis, ou seja, não podem ser
interrompidas quando estão sendo executadas pela interrupção do escalonador
para a troca de processos.

Exemplo:
while(true)
{
região não-crítica // neste região não se acessa variáveis compartilhadas
...
wait(mutex); // trava acesso a RC por outros processos
região crítica // com a RC bloqueada, este processso pode acessar com
// segurança variáveis compartilhadas
...
signal(mutex); // saindo da RC, libera-se o acesso a RC p/outros proc.
região não-crítica // neste região não se acessa variáveis compartilhadas
...
}

2.4.3 – Utilização de Semáforos para resolver o problema de sincronização


entre processos

Exemplo: Tem-se dois processos, P1 e P2. P2 só deve ser executado após P1


executar. No início do código o semáforo sinc é inicializado com 0.

Solução:
void P1() void P2()
{ {
... wait(sinc);
...
...
... ...
signal(sinc); ...

} } 8
Sistemas Operacionais

2.4.4 - Produtor x Consumidor utilizando semáforos

#include “prototypes.h”
#define N 100 // nro de posições do buffer
#typedef int semaphore; // um semáforo pode ser representado por um int

semaphore mutex = 1; // controla o acesso ‘a região crítica


semaphore empty = N; // conta as posições vazias do buffer
semaphore full = 0; // conta as posições ocupadas do buffer

void producer(void) // processo produtor


{
int item; // o oposto ao processo (p/ 2 processos)
while(TRUE)
{
produce_item(&item); // produz item para ser colocado no buffer
down(&empty); // decrementa contador de posições vazias
down(&mutex); // entra na rc
enter_item(item); // coloca um item no buffer
up(&mutex); // deixa a rc
up(&full); // incrementa contador de posições ocupadas
}
}

void consumer(void) // processo consumidor


{
int item; // o oposto ao processo (p/ 2 processos)
while(TRUE)
{
down(&full); // decrementa contador de posições ocupadas
down(&mutex); // entra na rc
remove_item(item); // retira um item do buffer
up(&mutex); // deixa a rc
up(&empty); // incrementa contador de posições vazias
consume_item(item); // utiliza o item retirado do buffer
}
}

9
Sistemas Operacionais

3 Problemas Clássicos de Comunicação entre Processos

3.1 O Jantar dos Filósofos


Cinco filósofos estão sentados à mesa, e
devem alternar suas atividades entre
pensar e comer:
1. Para comer, cada um deles precisa de
dois garfos;
2. Quando um filósofo sente fome, ele
deve pegar um garfo à esquerda e outro
à direita (em qualquer ordem), comer e,
quando terminar, devolver os garfos;

Modela o acesso exclusivo a um


número limitado de recursos.

#define N 5 / * number of philosophers * /


void philosopher(int i) / * i: philosopher number, from 0 to 4 * /
{
while (TRUE)
{
think(); / * philosopher is thinking * /
take_fork(i); / * take left fork * /
take_fork((i+1) % N); / * take right fork; % is modulo operator * /
eat(); / * yum-yum, spaghetti * /
put_fork(i); / * put left fork back on the table * /
put_fork((i+1) % N); / * put right fork back on the table * /
}
} Uma má solução para o problema do Jantar dos Filósofos
Situações:
a. se todos tentarem comer ao mesmo tempo, nenhum come → deadlock
b. modificação: após pegar o garfo da esquerda, verificar se o da direita está
livre. Se não estiver, devolva o garfo → starvation (se houver sincronismo)
c. se o tempo de espera for aleatório → pode haver condição de falha (mesmo
que pouco provável)
d. criando uma região crítica protegida com um semáforo binário → evita-se
starvation e deadlock, mas obtém-se paralelismo pobre (apenas um filósofo pode
comer de cada vez e não dois)

#define N 5 / * number of philosophers * /


10
Sistemas Operacionais

#define LEFT (i-1)%N / * number of i’s left neighbor * /


#define RIGHT (i+1)%N / * number of i’s right neighbor * /
#define THINKING 0 / * philosopher is thinking * /
#define HUNGRY 1 / * philosopher is trying to get forks * /
#define EATING 2 / * philosopher is eating * /
typedef int semaphore; / * semaphores are a special kind of int * /
int state[N]; / * array to keep track of everyone’s state * /
semaphore mutex = 1; / * mutual exclusion for critical regions * /
semaphore s[N]; / * one semaphore per philosopher * /

void philosopher(int i) / * i: philosopher number, from 0 to N-1 * /


{
while (TRUE) / * repeat forever * /
{
think(); / * philosopher is thinking * /
take_forks(i); / * acquire two forks or block * /
eat(); / * yum-yum, spaghetti * /
put_forks(i); / * put both forks back on table * /
}
}

void take_forks(int i) / * i: philosopher number, from 0 to N-1 * /


{
down(&mutex); / * enter critical region * /
state[i] = HUNGRY; / * record fact that philosopher i is hungry * /
test(i); / * try to acquire 2 forks * /
up(&mutex); / * exit critical region * /
down(&s[i]); / * block if forks were not acquired * /
}

void put_forks(i) / * i: philosopher number, from 0 to N-1 * /


{
down(&mutex); / * enter critical region * /
state[i] = THINKING; / * philosopher has finished eating * /
test(LEFT); / * see if left neighbor can now eat * /
test(RIGHT); / * see if right neighbor can now eat * /
up(&mutex); / * exit critical region * /
}

void test(i) / * i: philosopher number, from 0 to N-1 * /


{
if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING)
{
state[i] = EATING;
up(&s[i]);
}
}
Uma solução para o problema do Jantar dos Filósofos

 máximo paralelismo entre processos;


 matriz state controla estado do filósofo (comendo, pensando ou com fome);

 um filósofo só pode comer se nenhum vizinho estiver comendo;


 arrays de semáforo para cada filósofo → filósofos com fome são bloqueados
se os garfos necessários estiverem sendo utilizados.
11
Sistemas Operacionais

3.2 Problema dos leitores e escritores

Modela o acesso a bases de dados: muitos processos podem ler uma base ao
mesmo tempo, mas se um processo estiver escrevendo, nenhum outro pode ter
acesso a base, nem mesmo para leitura.
typedef int semaphore; / * use your imagination * /
semaphore mutex = 1; / * controls access to ’rc’ * /
semaphore db = 1; / * controls access to the data base * /
int rc = 0; / * # of processes reading or wanting to * /
void reader(void)
{
while (TRUE) / * repeat forever * /
{
down(&mutex); / * get exclusive access to ’rc’ * /
rc = rc + 1; / * one reader more now * /
if (rc == 1) down(&db); / * if this is the first reader ... * /
up(&mutex); / * release exclusive access to ’rc’ * /
read_data_base(); / * access the data * /
down(&mutex); / * get exclusive access to ’rc’ * /
rc = rc - 1; / * one reader fewer now * /
if (rc == 0) up(&db); / * if this is the last reader ... * /
up(&mutex); / * release exclusive access to ’rc’ * /
use_data_read(); / * noncritical region * /
}
}
void writer(void)
{
while (TRUE) / * repeat forever * /
{
think_up_data(); / * noncritical region * /
down(&db); / * get exclusive access * /
write_data_base(); / * update the data * /
up(&db); / * release exclusive access * /
}
}
Uma solução para o problema dos Leitores e Escritores

 primeiro leitor executa um down no semáforo db (bloqueia os escritores);


 último leitor executa um up no semáforo db (desbloqueia os escritores);
 Uma variável rc (reader counter) conta o número de leitores ativos;
 Esta implementação dá prioridade aos leitores.

12
Sistemas Operacionais

3.3 O Barbeiro Dorminhoco


1. Os clientes sentam nas cadeiras
disponíveis e esperam sua vez;
2. Se não houver cadeiras, o cliente
vai embora;
3. O barbeiro atende os clientes por
ordem de chegada e, não havendo
clientes, ele dorme;

#define CHAIRS 5 / * # chairs for waiting customers * /


typedef int semaphore; / * use your imagination * /
semaphore customers = 0; / * # of customers waiting for service * /
semaphore barbers = 0; / * # of barbers waiting for customers * /
semaphore mutex = 1; / * for mutual exclusion * /
int waiting = 0; / * customers are waiting (not being cut) * /
void barber(void)
{
while (TRUE)
{
down(customers); / * go to sleep if # of customers is 0 * /
down(mutex); / * acquire access to ’waiting’ * /
waiting = waiting - 1; / * decrement count of waiting customers * /
up(barbers); / * one barber is now ready to cut hair * /
up(mutex); / * release ’waiting’ * /
cut_hair(); / * cut hair (outside critical region) * /
}
}

void customer(void)
{
down(mutex); / * enter critical region * /
if (waiting < CHAIRS) / * if there are no free chairs, leave * /
{
waiting = waiting + 1; / * increment count of waiting customers * /
up(customers); / * wake up barber if necessary * /
up(mutex); / * release access to ’waiting’ * /
down(barbers); / * go to sleep if # of free barbers is 0 * /
get_haircut(); / * be seated and be serviced * /
}
else
{
up(mutex); / * shop is full; do not wait * /
}
}

13
Sistemas Operacionais

Uma solução para o problema do Barbeiro Dorminhoco

14

Вам также может понравиться