Baixe o app para aproveitar ainda mais
Prévia do material em texto
Capítulo 1 Introdução Exercícios práticos 1.1 Quais são as três finalidades principais de um sistema operacional? Resposta: Fornecer um ambiente para um usuário de computador executar programas no hardware do computador de uma maneira conveniente e eficiente. Alocar os recursos separados do computador conforme a necessidade para solucionar o problema. O processo de alocação deverá ser tão justo e eficiente quanto possível. Como um programa de controle, ele tem duas funções principais: (1) supervisão da execução dos programas do usuário para impedir erros e uso indevido do computador, e (2) gerenciamento da operação e controle de dispositivos de E/S. 1.2 Quais são as principais diferenças entre sistemas operacionais para computadores de grande porte (mainframes) e computadores pessoais? Resposta: Geralmente, os sistemas operacionais para sistemas em bacth possuem requisitos mais simples do que para os computadores pessoais. Os sistemas em batch não precisam se preocupar com a interação com um usuário tanto quanto um computador pessoal. Como resultado, um sistema operacional para um PC precisa se preocupar com o tempo de resposta para um usuário interativo. Os sistemas em batch não têm esses requisitos. Um sistema em batch puro também pode não ter que lidar com tempo compartilhado, enquanto um sistema operacional precisa alternar rapidamente entre diferentes jobs. 1.3 Liste as quatro etapas que são necessárias para se executar um programa em uma máquina completamente dedicada. Resposta: a. Reservar tempo de máquina. b. Carregar programa manualmente na memória. c. Carregar endereço inicial e iniciar execução. d. Monitorar e controlar a execução do programa a partir do console. 1.4 Enfatizamos a necessidade de um sistema operacional fazer uso eficiente do hardware de computação. Quando é apropriado que o sistema operacional deixe de lado esse princípio e “desperdice” recursos? Por que esse tipo de sistema não é realmente desperdiçador? Resposta: Sistemas monousuário devem maximizar o uso do sistema para o usuário. Uma GUI poderia “desperdiçar” ciclos de CPU, mas ela otimiza a interação do usuário com o sistema. 1.5 Qual é a principal dificuldade que um programador precisa contornar na escrita de um sistema operacional para um ambiente de tempo real? Resposta: A principal dificuldade é manter o sistema operacional dentro das restrições de tempo fixas de um sistema de tempo real. Se o sistema não completar uma tarefa em determinado espaço de tempo, isso pode causar uma interrupção do sistema inteiro em que está rodando. Portanto, ao escrever um sistema operacional para um sistema de tempo real, o escritor precisa estar certo de que seus esquemas de escalonamento não permitam que o tempo de resposta exceda a restrição de tempo. 1.6 Considere as diversas definições de sistema operacional. Considere se o sistema operacional deverá incluir aplicações como navegadores Web e programas de correio. Argumente tanto contra como a favor, e dê suporte à sua resposta. Resposta: Ponto. Aplicações como navegadores Web e ferramentas de correio eletrônico estão tendo um papel cada vez mais importante nos modernos sistemas de computador de desktop. Para atender esse papel, eles devem ser incorporados como parte do sistema operacional. Ao fazer isso, eles podem oferecer melhor desempenho e melhor integração com o restante do sistema. Além disso, essas aplicações importantes podem ter o mesmo estilo do software de sistema operacional. Contraponto. O papel fundamental do sistema operacional é gerenciar recursos do sistema como CPU, memória, dispositivos de E/S etc. Além disso, seu papel é executar aplicações de software como navegadores Web e aplicações de correio eletrônico. Incorporando essas aplicações no sistema operacional, sobrecarregamos o sistema operacional com funcionalidade adicional. Esse peso resulta no sistema operacional realizando uma tarefa insatisfatória no gerenciamento dos recursos do sistema. Além disso, aumentamos o tamanho do sistema operacional, aumentando assim a probabilidade de falhas do sistema e violações de segurança. 1.7 Como a distinção entre o modo kernel e o modo usuário funciona como uma forma rudimentar de sistema de proteção (segurança)? Resposta: A distinção entre o modo kernel e o modo usuário oferece uma forma rudimentar de proteção da seguinte maneira. Certas instruções só poderiam ser executadas quando a CPU estivesse no modo kernel. De modo semelhante, dispositivos de hardware só poderiam ser acessados quando o programa estivesse executando no modo kernel. O controle sobre quando as interrupções poderiam ser ativadas ou desativada também é possível apenas quando a CPU estiver no modo kernel. Conseqüentemente, a CPU possui uma capacidade muito limitada ao executar no modo usuário, forçando assim a proteção de recursos críticos. 1.8 Quais das seguintes instruções deverão ser privilegiadas? a. Definir o valor do timer. b. Ler o clock. c. Limpar a memória. d. Emitir uma instrução de trap. e. Desativar interrupções. f. Modificar entradas na tabela de status de dispositivo. g. Passar do modo usuário para kernel. h. Acessar dispositivo de E/S. Resposta: As operações a seguir precisam ser privilegiadas: definir valor do timer, limpar a memória, desativar interrupções, modificar entradas na tabela de status de dispositivo, acessar dispositivo de E/S. O restante pode ser realizado no modo usuário. 1.9 Alguns computadores antigos protegiam o sistema operacional colocando-o em uma partição da memória que não poderia ser modificada pelo job do usuário ou pelo próprio sistema operacional. Descreva duas dificuldades que você acredita que poderiam surgir com tal esquema. Resposta: Os dados exigidos pelo sistema operacional (senhas, controles de acesso, informações contábeis e assim por diante) teriam que ser armazenadas em ou passadas pela memória desprotegida e, portanto, seriam acessíveis a usuários não autorizados. 1.10 Algumas CPUs oferecem mais de dois modos de operação. Quais são os dois usos possíveis desses modos múltiplos? Resposta: Embora a maioria dos sistemas só faça a distinção entre os modos usuário e kernel, algumas CPUs têm suporte para modos múltiplos. Modos múltiplos poderiam ser usados para fornecer uma política de segurança mais detalhada. Por exemplo, ao invés de distinguir entre apenas modo usuário e kernel, você poderia distinguir entre diferentes tipos de modo usuário. Talvez os usuários pertencentes ao mesmo grupo poderiam executar o código um do outro. A máquina entraria em um modo especificado quando um desses usuários estivesse executando o código. Quando a máquina estivesse nesse modo, um membro do grupo poderia executar o código pertencente a qualquer outro no grupo. Outra possibilidade seria oferecer diferentes distinções dentro do código do kernel. Por exemplo, um modo específico permitiria a execução de drivers de dispositivo USB. Isso significaria que os dispositivos USB poderiam ser atendidos sem ter que passar para o modo kernel, basicamente permitindo que os drivers de dispositivo USB executassem em um modo quase usuário/kernel. 1.11 Os timers poderiam ser usados para calcular a hora atual. Dê uma pequena descrição de como isso poderia ser realizado. Resposta: Um programa poderia usar a técnica a seguir para calcular a hora atual usando interrupções de timer. O programa poderia preparar um timer por algum tempo no futuro e ir dormir. Quando ele fosse acordado pela interrupção, poderia atualizar seu estado local, que está usando para registrar o número de interrupções que recebeu até o momento. Depois, poderia repetir esse processo de definir continuamente interrupções de timer e atualizar seu estado local quando as interrupções forem realmente ativadas. 1.12 A Internet é uma LANou uma WAN? Resposta: A Internet é uma WAN, pois os diversos computadores estão localizados em locais geograficamente diferentes e estão conectados por links de rede de longa distância. Capítulo 2 Estruturas do sistema operacional Exercícios práticos 2.1 Qual é a finalidade das chamadas do sistema? Resposta: Chamadas do sistema permitem que os processos em nível de usuário solicitem serviços do sistema operacional. 2.2 Quais são as cinco principais atividades de um sistema operacional em relação ao gerenciamento de processos? Resposta: a. A criação e exclusão dos processos de usuário e do sistema b. A suspensão e retomada dos processos c. A provisão de mecanismos para sincronismo de processo d. A provisão de mecanismos para a comunicação entre processos e. A provisão de mecanismos para tratamento de deadlock 2.3 Quais são as três atividades principais de um sistema operacional em relação ao gerenciamento de memória? Resposta: a. Registrar quais partes da memória estão atualmente sendo usadas e por quem. b. Decidir quais processos devem ser carregados na memória quando o espaço da memória se tornar disponível. c. Alocar e desalocar espaço de memória conforme a necessidade. 2.4 Quais são as três atividades principais de um sistema operacional em relação ao gerenciamento de armazenamento secundário? Resposta: Gerenciamento de espaço livre. Alocação de armazenamento. Escalonamento de disco. 2.5 Qual é a finalidade do interpretador de comandos? Por que ele normalmente é separado do kernel? Resposta: Ele lê comandos do usuário ou de um arquivo de comandos e os executa, normalmente transformando-os em uma ou mais chamadas do sistema. Ele normalmente não faz parte do kernel, pois o interpretador de comandos está sujeito a mudanças. 2.6 Que chamadas do sistema precisam ser executadas por um interpretador de comandos ou shell a fim de inicial um novo processo? Resposta: Nos sistemas Unix, uma chamada do sistema fork seguida por uma chamada do sistema exec precisam ser realizadas para iniciar um novo processo. A chamada fork clona o processo atualmente em execução, enquanto a chamada exec sobrepõe um novo processo com base em um executável diferente sobre o processo que chama. 2.7 Qual é a finalidade dos programas do sistema? Resposta: Os programas do sistema podem ser imaginados como grupos de chamadas do sistema úteis. Eles oferecem funcionalidade básica aos usuários, de modo que os usuários não precisem escrever seus próprios programas para solucionar problemas comuns. 2.8 Qual é a principal vantagem da técnica em camadas para o projeto do sistema? Quais são as desvantagens do uso da técnica em camadas? Resposta: Em todos os casos de projeto modular, projetar um sistema operacional de uma maneira modular tem diversas vantagens. O sistema é mais fácil de depurar e modificar, pois as mudanças afetam apenas seções limitadas do sistema, ao invés de tocar em todas as seções do sistema operacional. A informação é mantida apenas onde é necessária, e é acessível apenas dentro de uma área definida e restrita, de modo que quaisquer bugs que afetem esses dados devem ser limitados a um módulo ou camada específica. 2.9 Liste cinco serviços fornecidos por um sistema operacional. Explique como cada um oferece conveniência aos usuários. Explique também em quais casos seria impossível para os programas em nível de usuário oferecerem esses serviços. Resposta: a. Execução do programa. O sistema operacional carrega o conteúdo (ou seções) de um arquivo na memória e inicia sua execução. Um programa em nível de usuário não poderia ser confiado para alocar tempo de CPU corretamente. b. Operações de E/S. Discos, fitas, linhas seriais e outros dispositivos precisam ser comunicados com um nível muito baixo. O usuários só precisa especificar o dispositivo e a operação a realizar sobre ele, enquanto o sistema converte essa solicitação em comandos específicos do dispositivo ou do controlador. Os programas em nível de usuário não podem ser confiáveis para acessar somente dispositivos que deveriam ter acesso e acessá-los somente quando não estiverem sendo usados. c. Manipulação do sistema de arquivos. Existem muitos detalhes na criação, exclusão, alocação e nomeação de arquivo, que os usuários não deveriam ter que realizar. Blocos de espaço de disco são usados por arquivos e precisam ser acompanhados. A exclusão de um arquivo exige a remoção da informação de nome do arquivo e a liberação dos blocos alocados. As proteções também precisam ser verificadas para garantir o acesso apropriado ao arquivo. Os programas do usuário não poderiam garantir aderência aos métodos de proteção nem serem confiados para alocar apenas blocos livres e desalocar blocos na exclusão do arquivo. d. Comunicações. A passagem de mensagens entre os sistemas exige que as mensagens sejam transformadas em pacotes de informação, enviadas ao controlador de rede, transmitidas por um meio de comunicação e remontadas pelo sistema de destino. A ordenação de pacotes e a correção de dados precisa ocorrer. Novamente, os programas do usuário poderiam não coordenar o acesso ao dispositivo de rede, ou então poderiam receber pacotes destinados a outros processos. e. Detecção de erro. A detecção de erro ocorre nos níveis de hardware e de software. No nível de hardware, todas as transferências de dados precisam ser inspecionadas para garantir que os dados não foram modificados no caminho. Todos os dados na mídia precisam ser verificados para garantir que não tenham sido alterados desde que foram escritos na mídia. No nível de software, a mídia precisa ser verificada em relação à coerência dos dados; por exemplo, para saber se o número de blocos de armazenamento alocados e desalocados combina com o número total no dispositivo. Lá, os erros constantemente são independentes do processo (por exemplo, a modificação dos dados em um disco), de modo que é preciso haver um programa global (o sistema operacional) que trata de todos os tipos de erros. Além disso, tendo os erros processados pelo sistema operacional, os processos não precisam conter código para apanhar e corrigir todos os erros possíveis em um sistema. 2.10 Qual é a finalidade das chamadas do sistema? Resposta: As chamadas do sistema permitem que os processos em nível de usuário solicitem serviços do sistema operacional. 2.11 Quais são as principais vantagens da técnica de microkernel para o projeto do sistema? Resposta: Os benefícios normalmente incluem o seguinte: (a) a inclusão de um novo serviço não exige a modificação do kernel, (b) isso é mais seguro, pois mais operações são feitas no modo usuário do que no modo kernel, e (c) projeto e funcionalidade de kernel mais simples normalmente resulta em um sistema operacional mais confiável. 2.12 Por que alguns sistemas armazenam o sistema operacional em firmware, e outros em disco? Resposta: Para certos dispositivos, como PDAs de mão e telefones celulares, um disco com um sistema de arquivos pode não estar disponível para o dispositivo. Nessa situação, o sistema operacional precisa ser armazenado no firmware. 2.13 Como um sistema poderia ser projetado para permitir uma escolha de sistemas operacionais para inicializar? O que o programa de bootstrap precisaria fazer? Resposta: Considere um sistema que gostaria de executar tanto Windows XT quanto três distribuições diferentes do Linux (por exemplo, RedHat, Debian e Mandrake). Cada sistema operacional estará armazenado no disco. Durante a inicialização do sistema, um programa especial (que chamaremos de gerenciador de boot) determinará em qual sistema operacional a partida será dada. Isso significa que, no começo da inicialização para um sistema operacional, o gerenciador de boot primeiro será executado durante a partida do sistema. É essegerenciador de boot que é responsável por determinar qual sistema será iniciado. Normalmente, os gerenciadores de boot precisam ser armazenados em certos locais do disco rígido para serem reconhecidos durante a partida do sistema. Os gerenciadores de boot normalmente oferecem ao usuário uma seleção de sistemas; os gerenciadores de boot normalmente também são preparados para inicializar em um sistema operacional padrão, se nenhuma escolha for feita pelo usuário. Capítulo 3 Processos Exercícios práticos 3.1 O Palm OS não fornece meios de processamento simultâneo. Discuta três complicações principais que o processamento simultâneo acrescenta a um sistema operacional. Resposta: a. Um método de compartilhamento de tempo precisa ser implementado para permitir que cada um dos diversos processos tenha acesso ao sistema. Esse método envolve a preempção dos processos que não abrem mão da CPU voluntariamente (usando uma chamada do sistema, por exemplo) e o kernel ser reentrante (de modo que mais de um processo possa estar executando o código do kernel simultaneamente). b. Os processos e os recursos do sistema precisam ter proteções e devem ser protegidos um do outro. Qualquer processo precisa ser limitado na quantidade de memória que ele pode usar e nas operações que ele pode realizar sobre dispositivos como discos. c. Deve-se ter o cuidado no kernel para impedir deadlocks entre os processos, de modo que os processos não estejam esperando pelos recursos alocados um do outro. 3.2 O processador Sun UltraSPARC possui múltiplos conjuntos de registradores. Descreva as ações de uma troca de contexto se o novo contexto já estiver carregado em um dos conjuntos de registradores. O que mais precisa acontecer se o novo contexto estiver na memória, ao invés de um conjunto de registradores e todos os conjuntos de registradores estiverem em uso? Resposta: O ponteiro de conjunto-de-registradores-atual da CPU é alterado para apontar para o conjunto contendo o novo contexto, o que leva muito pouco tempo. Se o contexto estiver na memória, um dos contextos em um conjunto de registradores precisa ser escolhido e movido para a memória, e o novo contexto precisa ser carregado da memória para o conjunto. Esse processo leva um pouco mais de tempo do que em sistemas com um conjunto de registradores, dependendo de como uma vítima de substituição é selecionada. 3.3 Quando um processo cria um novo processo usando a operação fork(), qual dos seguintes estados é compartilhado entre o processo pai e o processo filho? a. Pilha b. Heap c. Segmentos de memória compartilhada Resposta: Somente os segmentos de memória compartilhada são compartilhados entre o processo pai e o processo filho recém bifurcado. As cópias da pilha e da heap são feitas para o processo recém criado. 3.4 Novamente considerando o mecanismo de RPC, considere a semântica “exatamente uma vez”. O algoritmo para implementar essa semântica é executado corretamente, mesmo que a mensagem “ACK” de volta ao cliente seja perdida devido a um problema de rede? Descreva a seqüência de mensagens e se a semântica “exatamente uma vez” ainda é preservada. Resposta: A semântica “exatamente uma vez” garante que um procedimento remoto será executado exatamente uma vez e apenas uma vez. O algoritmo geral para garantir isso combina um esquema de confirmação (ACK) com estampas de tempo (ou algum outro contador incremental que permita que o servidor faça a distinção entre mensagens duplicadas). A estratégia geral é que o cliente envie o RPC ao servidor junto com uma estampa de tempo. O cliente também iniciará um clock de timeout. O cliente, então, esperará uma de duas ocorrências: (1) ele receberá um ACK do servidor, indicando que o procedimento remoto foi realizado, ou (2) ele esgotará um tempo limite. Se o cliente esgotar o tempo limite, ele assumirá que o servidor não conseguiu realizar o procedimento remoto, de modo que o cliente invoca a RPC pela segunda vez, enviando uma estampa de tempo mais adiante. O cliente pode não receber o ACK por um de dois motivos: (1) a RPC original nunca foi recebida pelo servidor, ou (2) a RPC foi recebida – e executada – corretamente pelo servidor, mas o ACK foi perdido. Na situação (1), o uso de ACKs permite que o servidor por fim receba e execute a RPC. Na situação (2), o servidor receberá uma RPC duplicada e usará a estampa de tempo para identificá-la como uma duplicata, de modo que não executará a RPC pela segunda vez. É importante observar que o servidor precisa enviar um segundo ACK de volta ao cliente, para informar-lhe que a RPC foi executada. 3.5 Considere que um sistema distribuído é suscetível a falhas no servidor. Que mecanismos seriam exigidos para garantir a semântica “exatamente uma vez” para a execução de RPCs? Resposta: O servidor deverá registrar no armazenamento estável (como um log de disco) a informação com relação às operações da RPC que foram recebidas, se elas foram executadas com sucesso e os resultados associados às operações. Quando ocorre uma falha do servidor e uma mensagem de RPC é recebida, o servidor pode verificar se a RPC foi previamente executada e, portanto, garantir a semântica “exatamente uma vez” para a execução de RPCs. Capítulo 4 Threads Exercícios práticos 4.1 Forneça dois exemplos de programação em que o multithreading oferece melhor desempenho do que uma solução de único thread. Resposta: (1) Um servidor Web que atende cada solicitação em um thread separado. (2) Uma aplicação paralelizada, como multiplicação de matriz, onde diferentes partes da matriz podem ser trabalhadas em paralelo. (3) Um programa GUI interativo, como um depurador onde um thread é usado para monitorar entrada do usuário, outro thread representa a aplicação em execução, e um terceiro thread monitora o desempenho. 4.2 Quais são duas diferenças entre os threads em nível de usuário e os threads em nível de kernel? Sob quais circunstâncias um tipo é melhor que o outro? Resposta: (1) Threads em nível de usuário são desconhecidos pelo kernel, enquanto o kernel está ciente dos threads de kernel. (2) Em sistemas usando mapeamento M:1 ou M:N, os threads do usuário são escalonados pela biblioteca de threads e o kernel escalona os threads de kernel. (3) Os threads de kernel não precisam estar associados a um processo, enquanto cada thread de usuário pertence a um processo. Os threads de kernel geralmente são mais dispendiosos de se manter do que os threads do usuário, pois precisam ser substituídos por uma estrutura de dados do kernel. 4.3 Descreva as ações tomadas por um kernel para a troca de contexto entre os threads em nível de kernel. Resposta: A troca de contexto entre os threads de kernel normalmente exige salvar o valor dos registradores da CPU a partir do thread sendo trocado para fora, e restaurar os registradores da CPU do novo thread sendo escalonado. 4.4 Que recursos são usados quando um thread é criado? Como eles diferem daqueles usados quando um processo é criado? Resposta: Como um thread é menor do que um processo, a criação de threads normalmente usa menos recursos do que a criação de processo. A criação de um processo requer a alocação de um bloco de controle de processo (PCB), uma estrutura de dados relativamente grande. O PCB inclui um mapa de memória, lista de arquivos abertos e variáveis de ambiente. A alocação e o gerenciamento do mapa de memória normalmente é a atividade mais demorada. A criação de um thread de usuário ou kernel envolve a alocação de uma pequena estrutura de dados para manter um conjunto de registradores, pilha e prioridade. 4.5 Considere que um sistema operacional relaciona threads em nível de usuário ao kernel usando o modelo muitos-para-muitos e o mapeamento é feito por meio de LWPs. Além do mais, o sistema permite que os desenvolvedores criemthreads em tempo real. É necessário vincular um thread em tempo real a um LWP? Explique. Resposta: Sim. A temporização é crucial para as aplicações de tempo real. Se um thread for marcado como tempo real, mas ao estiver ligado a um LWP, o thread pode ter que esperar para ser conectado a um LPW antes de executar. Considere se um thread em tempo real estiver sendo executado (conectado a um LWP) e depois prosseguir para bloquear (ou seja, precisa realizar E/S, ter sido interrompido por um thread de tempo real de prioridade mais alta, estiver esperando por um bloqueio de exclusão mútua etc.) Enquanto o thread de tempo real está bloqueado, o LWP ao qual foi conectado foi atribuído a outro thread. Quando o thread de tempo real tiver sido escalonado para execução novamente, primeiro ele precisa esperar para ser conectado a um LWP. Vinculando um LWP a um thread de tempo real, você está garantindo que o thread será capaz de executar com o mínimo de atraso uma vez escalonado. 4.6 Um programa Pthread que realiza a função de somatório foi apresentado na Seção 4.3.1. Reescreva esse programa em Java. Resposta: Por favor, consulte o Web site de suporte para obter o código fonte da solução. Capítulo 5 Escalonamento de CPU Exercícios práticos 5.1 Um algoritmo de escalonamento de CPU determina uma ordem para a execução de seus processos escalonados. Dados n processos a serem escalonados em um processador, quantos schedules diferentes possíveis existem? Dê uma fórmula em termos de n. Resposta: n! (n fatorial = n x n - 1 x n - 2 x ... x 2 x 1). 5.2 Defina a diferença entre escalonamento preemptivo e não preemptivo. Resposta: O escalonamento preemptivo permite que um processo seja interrompido no meio de sua execução, tirando a CPU e alocando-a a outro processo. O escalonamento não preemptivo garante que um processo abre mão do controle da CPU somente quando terminar com seu burst de CPU atual. 5.3 Suponha que os processos a seguir cheguem para execução nos momentos indicados. Cada processo será executado pela quantidade de tempo listada. Ao responder as perguntas, use o escalonamento não preemptivo e baseie todas as decisões na informação que você tem no momento em que a decisão precisa ser tomada. Processo Tempo de chegada Tempo de burst P1 0,0 8 P2 0,4 4 P3 1,0 1 a. Qual é o tempo de turnaround médio para esses processos com o algoritmo de escalonamento FCFS? b. Qual é o tempo de turnaround médio para esses processos com o algoritmo de escalonamento SJF? c. O algoritmo SJF deveria melhorar o desempenho, mas observe que decidimos executar o processo P1 no tempo 0 porque não sabíamos se dois processos mais curtos chegariam cedo. Calcule o tempo de turnaround médio se a CPU ficar ociosa pela primeira 1 unidade e depois o escalonamento SJF for usado. Lembre-se de que os processos P1 e P2 estão aguardando durante esse tempo ocioso, de modo que seu tempo de espera poderá aumentar. Esse algoritmo poderia ser conhecido como escalonamento com conhecimento futuro. Resposta: a. 10,53 b. 9,53 c. 6,86 Lembre-se de que o tempo de turnaround é o tempo de término menos o tempo de chegada, de modo que você precisa subtrair os tempos de chegada para calcular os tempos de turnaround. O FCPS é 11 se você se esquecer de subtrair o tempo de chegada. 5.4 Que vantagem existe em haver diferentes tamanhos de quantum de tempo em diferentes níveis de um sistema de enfileiramento multinível? Resposta: Os processos que precisam de atendimento mais freqüente, por exemplo, processos interativos como editores, podem estar em uma fila com um pequeno quantum de tempo. Os processos sem necessidade de atendimento freqüente podem estar em uma fila com um quantum maior, exigindo menos trocas de contexto para concluir o processamento, fazendo assim um uso mais eficiente do computador. 5.5 Muitos algoritmos de escalonamento de CPU são parametrizados. Por exemplo, o algoritmo RR requer que um parâmetro indique a fatia de tempo. As filas de feedback multinível exigem parâmetros para definir o número de filas, os algoritmos de escalonamento para cada fila, os critérios usados para mover processos entre as filas, e assim por diante. Esses algoritmos, portanto, na realidade são conjuntos de algoritmos (por exemplo, o conjunto de algoritmos RR para todas as fatias de tempo, e assim por diante). Um conjunto de algoritmos pode incluir outro (por exemplo, o algoritmo FCFS é o algoritmo RR com um quantum de tempo infinito). Que relação (se houver) existe entre os seguintes pares de conjuntos de algoritmos? a. Prioridade e SJF b. Filas de feedback multinível e FCFS c. Prioridade e FCFS d. RR e SJF Resposta: a. A tarefa mais curta tem a prioridade mais alta. b. O nível mais baixo de MLFQ é FCFS. c. FCFS oferece a prioridade mais alta para o job que existiu por mais tempo. d. Nenhuma. 5.6 Suponha que um algoritmo de escalonamento (no nível de escalonamento de CPU a curto prazo) favorece aqueles processos que usaram o menor tempo de processador no passado recente. Por que esse algoritmo favorece os programas voltados para E/S e não deixa os programas voltados para CPU estagnados permanentemente? Resposta: Ele favorecerá os programas voltados para E/S, devido ao burst de CPU relativamente curto que é feito por eles; porém, os programas voltados para CPU não se estagnarão, pois os programas voltados para E/S abrirão mão da CPU com mais freqüência, para realizar sua E/S. 5.7 Faça a distinção entre escalonamento PCS e SCS. Resposta: O escalonamento PCS é feito local ao processo. É assim que a biblioteca de threads escalona os threads nos LWPs disponíveis. O escalonamento SCS é a situação onde o sistema operacional escalona threads de kernel. Em sistemas usando muitos-para-um ou muitos-para- muitos, os dois modelos de escalonamento são fundamentalmente diferentes. Em sistemas usando um-para-um, PCS e SCS são iguais. 5.8 Considere que um sistema operacional relaciona os threads em nível de usuário ao modelo muitos-para-muitos, onde o mapeamento é feito por meio do uso de LWPs. Além do mais, o sistema permite que desenvolvedores de programa criem threads de tempo real. É necessário vincular um thread de tempo real a um LWP? Resposta: Sim, caso contrário um thread de usuário pode ter que competir por um LWP disponível antes de ser realmente escalonado. Vinculando o thread do usuário a um LWP, não existe latência enquanto se espera por um LWP disponível; o usuário de tempo real pode ser escalonado imediatamente. Capítulo 6 Sincronismo de processos Exercícios práticos 6.1 Na Seção 6.4, mencionamos que desativar interrupções com freqüência poderia afetar o clock do sistema. Explique por que isso poderia acontecer e como esses efeitos poderiam ser minimizados. Resposta: O clock do sistema é atualizado a cada interrupção de clock. Se as interrupções fossem desativadas - particularmente por um longo período de tempo -, é possível que o clock do sistema facilmente perdesse a hora correta. O clock do sistema também é usado para fins de escalonamento. Por exemplo, o quantum de tempo para um processo é expresso como o número de batidas de clock. A cada interrupção de clock, o escalonador determina se o quantum de tempo para o processo atualmente em execução expirou. Se as interrupções de clock fossem desativadas, o escalonador não poderia atribuir quantuns de tempo com precisão. Esse efeito pode ser minimizado desativando-se as interrupções de clock somente por períodos muito curtos. 6.2 O problema dos fumantes de cigarro. Considere um sistema com três processos fumante e um processo agente. Cada fumante continuamente prepara um cigarro e depois o fuma. Mas, para preparar e fumar um cigarro, o fumante precisa de três ingredientes: tabaco, papel e fósforos. Um dos processos fumante tem papel,outro tem tabaco, e o terceiro tem fósforos. O agente tem um estoque infinito de todos os três materiais. O agente coloca dois dos ingredientes sobre a mesa. O fumante que tem o ingrediente restante, então, prepara e fuma um cigarro, sinalizando ao agente quando terminar. O agente, então coloca outros dois dos ingredientes, e o ciclo se repete. Escreva um programa para sincronizar o agente e os fumantes usando o sincronismo Java. Resposta: Por favor, consulte o Web site de suporte para obter a solução do código fonte. 6.3 Dê os motivos pelos quais Solaris, Windows XP e Linux implementam múltiplos mecanismos de bloqueio. Descreva as circunstâncias sob as quais eles usam spinlocks, mutexes, semáforos, mutexes adaptativos e variáveis de condição. Em cada caso, explique por que o mecanismo é necessário. Resposta: Esses sistemas operacionais oferecem diferentes mecanismos de bloqueio, dependendo das necessidades dos desenvolvedores de aplicação. Spinlocks são úteis para sistemas multiprocessadores onde um thread pode executar em um loop ocupado (por um pequeno período de tempo) ao invés de incorrer no overhead de ser colocado em uma fila de sleep. Mutexes são úteis para o bloqueio de recursos. O Solaris 2 utiliza mutexes adaptativos, significando que o mutex é implementado com um spin lock em máquinas multiprocessadoras. Semáforos e variáveis de condição são ferramentas mais apropriadas para sincronização quando um recurso tiver que ser mantido por um longo período de tempo, pois o spinning é ineficiente por uma longa duração. 6.4 Explique as diferenças, em termos de custo, entre os três tipos de armazenamento: volátil, não volátil e estável. Resposta: O armazenamento volátil refere-se à memória principal e de cache, e é muito rápido. Porém, o armazenamento volátil não pode sobreviver a falhas do sistema ou falta de energia no sistema. O armazenamento não volátil sobrevive a falhas no sistema e faltas de energia. Discos e fitas são exemplos de armazenamento não volátil. Recentemente, dispositivos USB usando memória EPROM (Erasable Program Read-Only Memory) apareceram oferecendo armazenamento não volátil. Armazenamento estável refere-se ao armazenamento que tecnicamente nunca poderá ser perdido, pois existem cópias de backup redundantes dos dados (normalmente, em disco). 6.5 Explique a finalidade do mecanismo de ponto de verificação. Com que freqüência os pontos de verificação devem ser realizados? Descreva como a freqüência dos pontos de verificação afeta: O desempenho do sistema quando não ocorre falha O tempo gasto para se recuperar de uma falha do sistema O tempo gasto para se recuperar de uma falha do disco Resposta: Um registro de logaritmo de ponto de verificação indica que um registro de logaritmo e seus dados modificados foram gravados em armazenamento estável e que a transação não precisa ser refeita no caso de uma falha do sistema. Obviamente, com quanto mais freqüência os pontos de verificação forem realizados, menor a probabilidade de que atualizações redundantes tenham que ser realizadas durante o processo de recuperação. O desempenho do sistema quando não ocorre falha - Se não houver falhas, o sistema deverá incorrer no custo de realizar pontos de verificação que são basicamente desnecessários. Nessa situação, a execução de pontos de verificação com menos freqüência levará a um melhor desempenho do sistema. O tempo gasto para se recuperar de uma falha do sistema - A existência de um registro de ponto de verificação significa que uma operação não terá que ser refeita durante a recuperação do sistema. Nessa situação, com quanto mais freqüência os pontos de verificação forem executados, mais rápido é o tempo de recuperação de uma falha do sistema. O tempo gasto para se recuperar de uma falha do disco - A existência de um registro de ponto de verificação significa que uma operação não terá que ser refeita durante a recuperação do sistema. Nessa situação, com quanto mais freqüência os pontos de verificação forem executados, mais rápido é o tempo de recuperação de uma falha do disco. 6.6 Explique o conceito da atomicidade da transação. Resposta: Uma transação é uma série de operações de leitura e escrita sobre alguns dados, seguidas por uma operação de commit. Se a série de operações em uma transação não puder ser completada, a transação deverá ser abortada e as operações que ocorreram deverão ser canceladas. É importante que a série de operações em uma transação apareça como uma operação indivisível para garantir a integridade dos dados sendo atualizados. Caso contrário, os dados poderiam ser comprometidos se as operações de duas (ou mais) transações diferentes fossem intermisturadas. 6.7 Mostre que alguns schedules são possíveis sob o protocolo de bloqueio em duas fases, mas não sob o protocolo de estampa de tempo, e vice-versa. Resposta: Um schedule que é permitido no protocolo de bloqueio em duas fases, mas não no protocolo de estampa de tempo, é: etapa T0 T1 Precedência 1 lock-S(A) 2 read(A) 3 lock-X(B) 4 write(B) 5 unlock(B) 6 lock-S(B) 7 read(B) T1 ! T0 8 unlock(A) 9 unlock(B) Esse schedule não é permitido no protocolo de estampa de tempo porque, na etapa 7, a estampa de tempo W de B é 1. Um schedule que é permitido no protocolo de estampa de tempo, mas não no protocolo de bloqueio em duas fases, é: etapa T0 T1 T2 1 write(A) 2 write(A) 3 write(A) 4 write(B) 5 write(B) Esse schedule não pode ter instruções de bloqueio adicionadas para torná- la válida sob o protocolo de bloqueio em duas fases, pois T1 precisa desbloquear (A) entre as etapas 2 e 3, e deve bloquear (B) entre as etapas 4 e 5. 6.8 A instrução [TD]wait()[TN] em todos os exemplos de programa Java fez parte de um loop [TD]while[TN]. Explique por que você sempre deverá ter que usar uma instrução [TD]while[TN] ao usar [TD]wait()[TN] e por que você nunca usaria uma instrução [TD]if[TN]. Resposta: Essa é uma questão importante para se enfatizar! Java só oferece notificação anônima - você não pode notificar um certo thread de que certa condição é verdadeira. Quando um thread é notificado, é sua responsabilidade verificar novamente a condição pela qual ele está esperando. Se um thread não verificar a condição novamente, ele pode ter recebido a notificação sem a condição ter sido atendida. Capítulo 7 Deadlocks Exercícios práticos 7.1 Liste três exemplos de deadlocks que não estão relacionados a um ambiente de sistema de computador. Resposta: Dois carros, vindos de direções opostas, cruzando uma ponte de única pista. Uma pessoa descendo por uma escada enquanto outra pessoa está subindo a mesma escada. Dois trens trafegando um em direção ao outro na mesma linha. Dois carpinteiros que precisam pregar pregos. Há um único martelo e um único balde de pregos. O deadlock ocorre se um carpinteiro tiver o martelo e o outro carpinteiro tiver os pregos. 7.2 Suponha que um sistema esteja em um estado inseguro. Mostre que é possível que os processos terminem sua execução sem entrar em um estado de deadlock. Resposta: Um estado inseguro pode não necessariamente levar a um deadlock, mas significa apenas que não podemos garantir que o deadlock não ocorrerá. Assim, é possível que um sistema em um estado inseguro ainda possa permitir que todos os processos completem sem que ocorra deadlock. Considere a situação onde um sistema tem 12 recursos alocados entre os processos P0, P1 e P2. Os recursos são alocados de acordo com a seguinte política: Máximo Atual Necessário P0 10 5 5 P1 4 2 2 P2 9 3 6 [LIST]for (int i = 0; i < n; i++) { // primeiro acha um thread que possa terminar for (int j = 0; j < n; j++) { if (!finish[j]) { boolean temp = true; for (int k = 0; k < m; k++) { if (need[j][k] > work[k]) temp =false; } if (temp) { // se este thread puder terminar finish[j] = true; for (int x = 0; x < m; x++) work[x] += work[j][x]; } } } } Figura 7.1 Algoritmo de segurança do algoritmo do banqueiro. Atualmente, existem dois recursos disponíveis. Esse sistema está em um estado inseguro, pois o processo P1 poderia completar, liberando assim um total de quatro recursos. Mas não podemos garantir que os processos P0 e P2 podem completar. Porém, é possível que um processo possa liberar recursos antes de solicitar algo mais. Por exemplo, o processo P2 poderia liberar um recurso, aumentando assim o número total de recursos para cinco. Isso permite que o processo P0 complete, o que liberaria um total de nove recursos, permitindo assim que o processo P2 complete também. 7.3 Prove que o algoritmo de segurança apresentado na Seção 7.5.3 requer uma ordem de m x n2 operações. Resposta: A Figura 7.1 oferece um código Java que implementa o algoritmo de segurança do algoritmo do banqueiro (a implementação completa do algoritmo do banqueiro está disponível com o download do código fonte). Como podemos ver, os loops externos aninhados - ambos percorrendo n vezes - oferecem o desempenho n2. Dentro desses loops externos existem dois loops internos seqüenciais, que percorrem m vezes. O grande oh desse algoritmo é, portanto, O(m x n2). 7.4 Considere um sistema de computação que executa 5.000 jobs por mês sem esquema de prevenção ou impedimento de deadlock. Os deadlocks ocorrem por volta de duas vezes por mês, e o operador precisa terminar e reexecutar cerca de 10 jobs por deadlock. Cada job tem valor aproximado de US$ 2 (em tempo de CPU), e os jobs terminados tendem a estar na metade quando são abortados. Um programador de sistemas estimou que um algoritmo de impedimento de impasse (como o algoritmo do banqueiro) poderia ser instalado no sistema com um aumento no tempo médio de execução por tarefa de aproximadamente 10 por cento. Como a máquina atualmente possui um tempo ocioso de 30 por cento, todos os 5.000 jobs por mês ainda poderiam ser executados, embora o tempo de turnaround aumente em cerca de 20 por cento na média. a. Quais são os argumentos a favor da instalação do algoritmo de impedimento de impasse? b. Quais são os argumentos contra a instalação do algoritmo de impedimento de impasse? Resposta: Um argumento para se instalar o impedimento de deadlock no sistema é que poderíamos garantir que o impasse nunca ocorreria. Além disso, apesar do aumento no tempo de turnaround, todos os 5.000 jobs ainda poderiam executar. Um argumento contra a instalação do software de impedimento de deadlock é que os deadlocks ocorrem com pouca freqüência e eles custam pouco quando ocorrem. 7.5 Um sistema poderia detectar que alguns de seus processos estão estagnando? Se a sua resposta for “sim”, explique como isso pode ser feito. Se a resposta for “não”, explique como o sistema pode lidar com o problema de estagnação. Resposta: A estagnação é um tópico difícil de definir, pois pode significar diferentes coisas para diferentes sistemas. Para os propósitos desta pergunta, definiremos a estagnação como a situação na qual o processo precisa esperar além de um período de tempo razoável - talvez indefinidamente - antes de receber um recurso solicitado. Uma forma de detectar a estagnação seria primeiro identificar um período de tempo - T - que seja considerado excessivo. Quando um processo solicita um recurso, um temporizador é iniciado. Se o tempo gasto ultrapassar T, então o processo é considerado como estagnado. Uma estratégia para lidar com a estagnação seria adotar uma política onde os recursos são atribuídos apenas aos processos que estiveram esperando por mais tempo. Por exemplo, se o processo Pa estivesse esperando mais tempo pelo recurso X do que o processo Pb, a solicitação do processo Pb seria adiada até que a solicitação do processo Pa fosse satisfeita. Outra estratégia seria menos estrita do que o que foi mencionado. Nesse cenário, um recurso poderia ser concedido a um processo que esperou menos do que outro processo, desde que o outro processo não esteja estagnando. Porém, se outro processo for considerado como estando estagnando, sua solicitação seria satisfeita primeiro. 7.6 Considere a política de alocação de recursos a seguir. Solicitações e liberações de recursos são permitidas a qualquer momento. Se uma solicitação de recursos não puder ser satisfeita porque os recursos não estão disponíveis, então verificamos quaisquer processos que estão bloqueados, esperando pelos recursos. Se eles tiverem os recursos desejados, então esses recursos são retirados deles e dados aos processos solicitantes. O vetor de recursos pelos quais o processo está esperando é aumentado para incluir os recursos que foram retirados. Por exemplo, considere um sistema com três tipos de recurso e um vetor Available inicializado com (4,2,2). Se o processo P0 solicitar (2,2,1), ele os obtém. Se P1 solicitar (1,0,1), ele os obtém. Depois, se P0 solicitar (0,0,1), ele é bloqueado (recurso não disponível). Se P2 agora solicitar (2,0,0), ele recebe o recurso disponível (1,0,0) e um que foi alocado a P0 (pois P0 está bloqueado). O vetor Allocation de P0 chega até (1,2,1) e seu vetor Need vai até (1,0,1). a. O deadlock pode ocorrer? Se a sua resposta for “sim”, dê um exemplo. Se você responder “não”, especifique qual condição necessária não pode ocorrer. b. Pode ocorrer bloqueio indefinido? Explique sua resposta. Resposta: a. O deadlock não pode ocorrer porque a preempção existe. b. Sim. Um processo pode nunca adquirir todos os recursos de que precisa ser eles forem continuamente tomados por uma série de solicitações como aquelas do processo C. 7.7 Suponha que você tenha codificado o algoritmo de segurança de impedimento de deadlock e agora tenha que implementar o algoritmo de detecção de impasse. Você pode fazer isso usando simplesmente o código do algoritmo de segurança e redefinindo Maxi = Waitingi + Allocationi, onde Waitingi é um vetor especificando os recursos que o processo i está esperando, e Allocationi é conforme definido na Seção 7.5? Explique sua resposta. Resposta: Sim. O vetor Max representa a solicitação máxima que um processo pode fazer. Ao calcular o algoritmo de segurança, usamos a matriz Need que representa Max - Allocation. Outra forma de pensar nisso é Max = Need + Allocation. De acordo com a pergunta, a matriz Waiting preenche um papel semelhante à matriz Need, portanto, Max = Waiting + Allocation. 7.8 É possível ter um deadlock envolvendo apenas um único processo? Explique sua resposta. Resposta: Não. Isso segue diretamente da condição manter-e-esperar.
Compartilhar