Você está na página 1de 6

Aluno: Thiago Waslawick Batista

Exercícios Cap 2

1) As duas transições que faltam no diagrama de estados são “bloqueado para em


execução” e de “pronto para bloqueado”. A primeira opção pode ocorrer quando um
processo que está bloqueado receba uma possível entrada ou o sistema operacional
libere CPU’s suficientes para executar o processo. Já a segunda opção não é possível
de acontecer, já que somente os processos em execução podem ser bloqueados.
2) .
3) Porque ações como salvar os registradores e configurar o ponteiro da pilha não
podem ser expressas em linguagens de alto nível.
4) Porque precisa liberar espaço para outro processo, pois se utilizar a área da pilha do
processo interrompido esse outro processo se interrompe também, por causa disso
é usada essa área do núcleo da pilha separada da pilha do processo interrompido.
5) A fração de tempo desperdiçada da CPU seria de 0,5^5 = 0,03125 ou 1/32
correspondente à chance de todos os cinco processos estarem ociosos.
6) Com a capacidade total da RAM de 4096 MB e um espaço ocupado de 512 MB do SO,
o espaço disponível para os processos será de 3584 MB (4096 -512). Logo, esse
computador suporta executar 14 processos. Para encontrar a espera máxima de E/S
tolerada utiliza-se a seguinte fórmula: uso da CPU = 1 -, onde “p” é à fração do tempo
de espera do processo e “n” a 𝑝𝑛 quantidade de processos simultâneos na memória.
Isolando a variável “p” e substituindo os valores obtemos o seguinte resultado:
p = (1 − 0. 99)1/14
p = 0,720
Então, o valor da espera máxima de E/S que pode ser tolerada é 0,720 ou 72% .
7) Como o tempo de espera de E/S é de 50% e o tempo para finalizar as tarefas são de 20
minutos, em uma execução sequencial as tarefas seriam completadas em 60 minutos,
pois seriam 10 minutos de espera para cada execução de tarefa (20 minutos de
espera) e 20 minutos para conclusão de cada tarefa, resultando em: 10 minutos x 2
tarefas = 20 minutos + 40 minutos = 60 minutos. Já em um execução em paralelo, o
tempo gasto seria de 30 minutos, já que duas tarefas são executadas
simultaneamente 30 minutos, onde 20 minutos correspondem ao tempo da CPU e 10
minutos correspondem ao tempo de espera de cada tarefa.
8) A utilização da CPU é calculada através da seguinte fórmula: 1 - , onde “p” 𝑝𝑛 é a
fração do tempo de espera do processo e “n” a quantidade de processos simultâneos
na memória. Logo, a chance de que todos os processos estejam à espera de entrada
e saída de um dispositivo é = 1-0,004096 = 1 − (0, 4)60,995904, ou seja,
aproximadamente 99,6% de utilização da CPU.
9) Os threads poderiam ser utilizados para gerenciar os downloads de arquivos,
fazendo com que esses arquivos fossem divididos em partes, onde cada thread
ficaria responsável por um determinado pedaço do arquivo, assim o download seria
feito de forma conjunta sem perda de velocidade.
10) Seria difícil manter o sistema de arquivos consistente. Suponha que um processo do
cliente envie uma solicitação ao servidor 1 para atualizar um arquivo. Esse processo
atualiza a entrada de cache em sua memória. Pouco tempo depois, outro processo
do cliente envia uma solicitação ao servidor 2 para ler esse arquivo. Infelizmente, se
o
arquivo também estiver armazenado em cache, o servidor 2, em sua inocência,
retornará dados obsoletos. Se o primeiro processo gravar o arquivo no disco após
armazená-lo em cache, e o servidor 2 verificar o disco em todas as leituras para ver se
sua cópia em cache está atualizada, o sistema pode funcionar, mas é precisamente
tudo. Esses discos acessam que o sistema de armazenamento em cache está
tentando evitar.
11) Não. Se um processo de thread único estiver bloqueado no teclado, ele não
poderá bifurcar-se.
12) Um segmento de trabalho será bloqueado quando for necessário ler uma página
da Web do disco. Se estiverem sendo usados encadeamentos no nível do usuário,
essa ação bloqueará todo o processo, destruindo o valor do multithreading.
Portanto, é essencial que os threads do núcleo sejam usados para permitir que
alguns threads sejam bloqueados sem afetar os outros.
13) Sim. Se o servidor estiver totalmente vinculado à CPU, não haverá necessidade de
vários threads. Isso apenas adiciona complexidade desnecessária. Como exemplo,
considere um número de assistência da lista telefônica (como 555-1212) para uma
área com 1 milhão de pessoas. Se cada registro (nome, número de telefone) tiver,
digamos, 64 caracteres, o banco de dados inteiro ocupará 64 megabytes e poderá
ser facilmente mantido na memória do servidor para fornecer uma pesquisa rápida.
14) Quando um encadeamento é parado, ele possui valores nos registradores. Eles
devem ser salvos, exatamente como quando o processo é interrompido, os registros
devem ser salvos. A multiprogramação de threads não é diferente dos processos de
multiprogramação, portanto, cada thread precisa de sua própria área de salvamento
de registro.
15) Uma chamada dessas é importante porque não há uma interrupção de relógio para
realmente forçar a multiprogramação como há com os processos. Desse modo, é
importante que os threads sejam educados e voluntariamente entreguem a CPU
de tempos em tempos para dar aos outros threads uma chance de serem
executados.
16) Sim. Se um thread começa a ser executado, nenhum outro naquele processo será
executado a não ser que o primeiro thread voluntariamente abra mão da CPU, ou
seja, caso as threads não abram mão da CPU por um tempo fica impossibilitado
escalonar processos pelo esquema de escalonamento circular (dando a vez ao outro)
e as threads seriam executadas de forma sequencial e não “paralela”. Uma solução
para essa situação é obrigar o sistema de tempo de execução a solicitar um sinal de
relógio (interrupção) para permitir o escalonamento circular entre o conjunto de
threads.
17) Em um servidor de thread único o resultado seria igual a:
⅓ * 90 * ⅔ * 15 = 40 ms
1000 ms (1s) / 40 = 25 requisições/segundo
Em um servidor multithread o resultado seria igual a:
tempo médio = 15ms1000 ms
(1s) / 15 = 66 requisições/segundo.
18) A maior vantagem de implementar threads no espaço do usuário é que elas são
escalonadas pelo programador, permitindo que cada processo tenha seu próprio
algoritmo de escalonamento customizado. Neste modo o programador é responsável
por criar, executar, escalonar e destruir a thread. A maior desvantagem é que se um
thread começa a ser executado, nenhum outro naquele processo será executado a
não ser que o primeiro thread voluntariamente abra mão da CPU. Dentro de um único
processo, não há interrupções de relógio, impossibilitando escalonar processos pelo
esquema de escalonamento circular (dando a vez ao outro). A menos que um
thread entre voluntariamente no sistema de tempo de execução, o escalonador
jamais terá uma chance.
19) Sim, pois é possível executar threads de forma sequencial chamando o comando
pthread join. Este comando faz com que um thread aguarde pelo término de
outro thread para ser executado. Dessa forma, é possível criar threads e forçar
uma determinada ordem de execução.
20) A rotina "create global" aloca memória para um ponteiro em uma área de
armazenamento especial reservada para o thread que emitiu a chamada. Ou seja,
apenas o thread que emitiu a chamada tem acesso à variável global. Se outro thread
criar uma variável global com o mesmo nome, ele obterá uma porção de memória
que não entrará em conflito com a existente. Logo, o uso dessa rotina facilita o
gerenciamento de variáveis globais dos threads e evita conflitos e a sobrescrita de
variáveis importantes dos threads durante o escalonamento circular, por exemplo.
21) Pode acontecer que o sistema de tempo de execução esteja exatamente no ponto de
bloquear ou desbloquear um thread e esteja ocupado manipulando as filas de
agendamento. Este seria um momento muito inoportuno para o manipulador de
interrupção do relógio começar a inspecionar essas filas para ver se era hora de fazer
a troca de thread, já que elas podem estar em um estado inconsistente. Uma solução
é definir um sinalizador quando o sistema de tempo de execução for inserido. O
manipulador do relógio veria isso, definiria seu próprio sinalizador e, em seguida,
retornaria. Quando o sistema de tempo de execução terminasse, ele verificaria o
sinalizador do relógio, veria se ocorreu uma interrupção do relógio e agora executaria
o manipulador do relógio.
22) A chamada de sistema select serve para identificar se uma chamada será bloqueada
futuramente. Isso é fundamental para os threads implementados no espaço de
usuários, pois permite que as chamadas de sistemas bloqueantes dos threads de
usuário sejam inspecionadas antes mesmo do thread ser bloqueado. Se a chamada
for bloqueada, a chamada não é feita. Em vez disso, outro thread é executado. Sem a
chamada select é possível pensar em uma solução usando relógios de alarmes para
interromper os threads bloqueados, mas para isso seria necessário que o sistema de
execução requisitasse um sinal de relógio (interrupção) a cada segundo para ter
controle sobre o gerenciamento dos threads. Entretanto, interrupções periódicas de
relógio com frequências altas nem sempre são possíveis, e mesmo que fossem, a
sobrecarga total de processamento seria substancial e afetaria o desempenho do
sistema. Sendo assim, é praticamente impossível implementar threads no espaço do
usuário dadas as condições expostas na questão.
23) Sim, a solução de espera ocupada usando a variável turn funcionará em um ambiente
multiprocessador com memória compartilhada. No exemplo dado, a variável 'turn' é
usada para controlar o acesso a uma região crítica por dois processos. Para evitar uma
situação de vários processos em seção crítica, o segmento de código mostrado na
figura 2.23 é utilizado como solução. O Fragmento de código realiza verificações
constantes na região crítica e gerencia a troca interativa entre dois processos,
evitando que ambos estejam na região crítica. Em um multiprocessador, a diferença é
que os dois processos estão sendo executados em CPUs diferentes. A única condição é
que o próprio 'turno' seja uma variável no compartilhamento da memória.
24) Sim, já que o processo 1 é executado primeiro. Porém não funcionaria com
não preemptivo, já que o valor inicial do turn é 0.
25) Não, pois as threads de usuário são criadas em espaço de usuário somente,o núcleo
não tem conhecimento de sua existência, por isso as threads de usuários não
sofrem com o problema de inversão de prioridade.
26) Não, pois no modelo circular é necessário alocar um tempo de execução para cada
processo, evitando a competição de tempo de uso na CPU. Diferentemente do
escalonamento de prioridade, cujo uso da CPU é dado pela prioridade do
processo, podendo haver dependência de dados entre processos de prioridade
alta e baixa.
27) Cada thread chama procedimentos por conta própria, portanto, deve ter sua
própria pilha para as variáveis locais, endereços de retorno e assim por diante. Isso
é igualmente verdadeiro para threads de nível de usuário e para threads de nível de
kernel.
28) Sim. O computador simulado pode ser multiprogramado. Por exemplo, enquanto o
processo A está em execução, ele lê alguma variável compartilhada. Então, um
tique de relógio simulado acontece e o processo B é executado. Ele também lê a
mesma variável. Em seguida, adiciona 1 à variável. Quando o processo A é
executado, se também adicionar 1 à variável, temos uma condição de corrida.
29) Sim, funcionará como está. Em um dado instante, apenas um
produtor(consumidor) pode adicionar (remover) um item ao (do) buffer.
30) A solução satisfaz a exclusão mútua, pois não é possível que ambos os processos
estejam em sua seção crítica. Ou seja, quando o turn é 0, P0 pode executar sua
seção crítica, mas não P1. Da mesma forma, quando o turn é 1.No entanto, isso
pressupõe que o P0 deve ser executado primeiro. Se P1 produz algo e o coloca em
um buffer, enquanto P0 pode entrar em sua seção crítica, ele encontrará o buffer
vazio e bloqueado. Além disso, essa solução exige uma alternância rigorosa dos dois
processos, o que é indesejável.
31) Para fazer uma operação de semáforo, o sistema operacional primeiro desativa as
interrupções. Em seguida, ele lê o valor do semáforo. Se estiver fazendo um down e o
semáforo for igual a zero, colocará o processo de chamada em uma lista de processos
bloqueados associados ao semáforo. Se estiver fazendo um up, deve verificar se
algum processo está bloqueado no semáforo. Se um ou mais processos estiverem
bloqueados, um deles será removido da lista de processos bloqueados retornando
executável. Quando todas essas operações forem concluídas, as interrupções poderão
ser ativadas novamente.
32) Associados a cada semáforo de contagem estão dois semáforos binários, M, usado
para exclusão mútua, e B, usado para bloqueio. Também associado a cada semáforo
de contagem está um contador que mantém o número de altas menos o número de
baixas e uma lista de processos bloqueados neste semáforo. Para implementar para
baixo, um processo obtém primeiro acesso exclusivo aos semáforos, contador e lista
fazendo um down em M. Depois, diminui o contador. Se for zero ou mais, apenas
aumenta e sai. Se M for negativo, o processo é colocado na lista de processos
bloqueados. Em seguida, é feito um up em M e um down em B para bloquear o
processo. Para implementar, o primeiro M é derrubado para obter exclusão mútua
e, em seguida, o contador é incrementado. Se for maior que zero, ninguém será
bloqueado; portanto, tudo o que precisa ser feito é aumentar. Se, no entanto, o
contador agora for negativo ou zero, é necessário remover algum processo da lista.
Finalmente, um up é feito em B e M nessa ordem.
33) Sim, faz sentido usar uma barreira para sincronizá-los. Algumas aplicações são
divididas em fases, onde um processo não deve seguir para a fase seguinte até que
os
outros processos atinjam o estado de prontidão. A barreira é usada para bloquear os
processos prontos, esperando que os outros processos também atinjam essa
barreira. Quando atingida por todos os processos é que ocorre a sincronização entre
eles.
34) Com o processador é definido em dois modos (modo núcleo e modo usuário), no
modo núcleo não existem restrições, enquanto no modo usuário algumas instruções
não podem ser executadas. O processo sincronizado usando um semáforo de núcleo,
com os threads implementados pelo núcleo conseguiriam controlar essa
sincronização. No caso dos threads implementados em modo usuário, poderia
esbarrar com instruções privilegiadas e assim não conseguiria se sincronizar
corretamente.
35) Esse esquema seria de implementação custosa, uma vez que, cada vez que há uma
mudança em alguma variável que aparece em um predicado no qual algum processo
está esperando, a rotina de sistema reavalia o predicado para ver se o processo pode
ser desbloqueado. Sendo assim, a execução da primitiva "wait until" faria com o que
se perdesse eficiência na sincronização.
36) A atendente se comunica através dos pedidos, os cozinheiros através da comida, os
especialistas em empacotamento através do empacotamento da comida e os caixas
através da entrega da sacola aos clientes e do recebimento do dinheiro. Se
comparado aos processos do UNIX, os quatro processos são conectados por pipes.
37) Essa “ação atômica” de tentar modificar/resgatar e se não for possível tentar
novamente sem apresentar nenhum tipo de interrupção é essencial para evitar
condições de corridas. Logo, essa atomicidade do sistema de transmissão de
mensagens não leva a condições de corrida. O sistema de transmissão pode ficar
em loop para sempre tentando recuperar uma mensagem, mas não é uma condição
de corrida.
38) Iria precisar de n*T segundos.
39) São criados quatro processos, pois existe uma bifurcação no primeiro fork(),entre
uma cópia do processo pai e a criação de um processo filho, e logo depois cada um
deles se bifurca novamente no fork() seguinte, onde mais dois processos são criados.
40) Se um processo ocorre múltiplas vezes na lista, significa múltiplos quantum por ciclo
para este processo. Isto pode ser usado para dar um maior compartilhamento da
CPU para processos de maior importância (uma espécie de prioridade). Entretanto,
caso o processo seja bloqueado, é preciso remover todas suas entradas na lista de
processos prontos.
41) Sim, é possível. Em programas que leem todos os arquivos de entrada nos buffers
no início do código fonte, provavelmente não terá limitação pela E/S, porém se é
um programa mais complexo que lê e grava inúmeros arquivos em seus buffers
durante sua execução de forma incremental, condicional ou em laços de repetição
por exemplo, é provável que haverá limitação pela E/S, dado que a condição de
parada nem sempre pode ser determinada. Caso o sistema operacional tenha um
comando que forneça a quantidade de tempo de CPU usada por um programa,
poderia comparar isso com o tempo total para concluir a execução do programa.
42) Há uma relação inversa entre o quantum de tempo e a mudança de contexto em um
escalonamento circular, já que o quantum de tempo muito alto pode levar outros
processos a um tempo maior de espera. Se o quantum do tempo for pequeno,
haverá mais troca de contexto e mais processos terão tempo para serem executados
por um período menor.
43) .
44) .
45) Para rodízio, durante os primeiros 10 minutos, cada trabalho recebe 1/5 da CPU. Ao
fim de 10 minutos, o vídeo termina. Durante os próximos 8 minutos, cada trabalho
recebe 1/4 da CPU, após o que o término termina. Então, cada um dos três trabalhos
restantes obtém 1/3 da CPU por 6 minutos, até que B termine, e assim por diante. Os
tempos de finalização dos cinco trabalhos são 10, 18, 24, 28 e 30, por uma média de
22 minutos. Para o agendamento prioritário, B é executado primeiro. Após 6 minutos,
está terminado. Os outros trabalhos terminam aos 14, 24, 26 e 30, por uma média de
18,8 minutos. Se os trabalhos são executados na ordem de A a E, terminam em 10,
16, 18, 22 e 30, por uma média de 19,2 minutos. Por fim, o primeiro trabalho mais
curto produz tempos de acabamento de 2, 6, 12, 20 e 30, por uma média de 14
minutos.
46) Na primeira vez, obtém 1 quantum. Em execuções sucessivas, ele obtém 2,4, 8 e
15, portanto, deve ser trocado 5 vezes.
47) .
48) .
49) .
50) .
51) Supondo que todos os filósofos comecem o protocolo simultaneamente, teremos que
no máximo dois filósofos dentre os cinco existentes conseguiriam pegar os garfos
direito e esquerdo para comer, não havendo a possibilidade deem algum momento da
execução do protocolo nenhum filósofo ficar sem comer. Portanto, o protocolo da
questão garante uma operação sem impasse, ou seja, sem travamento.
52) .
53) .
54) A rotina “take_forks” serve para indicar se o filósofo vai conseguir ou não pegar os
dois garfos para comer. Na figura 2.47 é utilizado um array denominado “state” para
controlar o estado de cada filósofo. O valor “HUNGRY” é um estado do algoritmo para
indicar que um filósofo está tentando pegar garfos. Portanto, a rotina “take_forks”
adiciona o estado “HUNGRY” ao filósofo “i” (por meio de “state[i] = HUNGRY”) para
indicar que este filósofo está precisando comer, ou seja, indica que o filósofo “i” deve
tentar pegar dois garfos para comer.
55) A rotina “put_forks” serve para devolver os dois garfos de um filósofo à mesa. Dentro
da rotina é possível observar que o estado do filósofo “i” é definido como“THINKING”
para determinar que ele acabou de comer, e as duas chamadas subsequentes para
teste servem para verificar se os vizinhos da direita e esquerda de “i” podem comer.
Logo, se configurarmos o estado para “THINKING” após as duas chamadas de testes,
o filósofo “i” nunca devolveria os garfos à mesa e não passaria a vez para que outro
filósofo pudesse comer.

Você também pode gostar