Baixe o app para aproveitar ainda mais
Prévia do material em texto
Prof. Michael A. Pontes michaelpontes@unip.br [1] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne. Resumo: Sistemas Operacionais Parte 1 – Visão Geral (introdução) Um sistema operacional é o software que gerencia o hardware do computador e também fornece um ambiente em que os programas de aplicação podem ser executados. Talvez o aspecto mais visível de um sistema operacional seja a interface com o sistema computadorizado que ele provê ao usuário humano. Para um computador realizar sua tarefa de executar programas, estes precisam estar na memória principal. A memória principal é a única área de armazenamento grande que o processador pode acessar diretamente. Ela consiste em um arranjo de words ou bytes, variando em tamanho de milhões para bilhões. Cada word na memória tem seu próprio endereço. A memória principal normalmente é um dispositivo de armazenamento volátil, que perde seu conteúdo quando a alimentação é desligada ou perdida. A maioria dos sistemas computadorizados fornece armazenamento secundário como uma extensão da memória principal. O armazenamento secundário provê uma forma de armazenamento não-volátil que é capaz de manter grande quantidade de dados permanentemente. O dispositivo de armazenamento secundário mais comum é um disco magnético, que fornece armazenamento de programas e dados. A grande variedade de sistemas de armazenamento em um sistema computadorizado pode ser organizada em uma hierarquia, de acordo com a velocidade e o custo. Os níveis mais altos são caros, mas são rápidos. Enquanto descemos na hierarquia, o custo por bit geralmente diminui, enquanto o tempo de acesso geralmente aumenta. Existem várias estratégias diferentes para se projetar um sistema computadorizado. Os sistemas de processador único possuem apenas um único processador, enquanto os sistemas multiprocessados contêm dois ou mais processadores que compartilham memória física e dispositivos periféricos. O projeto multiprocessado mais comum é o multiprocessamento simétrico (ou SMP), onde todos os processadores são considerados peers e executados independentemente uns dos outros. Os sistemas em clusters são uma forma especializada de multiprocessador e consistem em múltiplos sistemas de computador conectados por uma rede local. Para melhor utilizar a CPU, os sistemas operacionais modernos empregam a multiprogramação, que permite que várias tarefas estejam na memória ao mesmo tempo, garantindo assim que a CPU sempre tenha uma tarefa a executar. Os sistemas de tempo compartilhado são uma extensão da multiprogramação na qual os algoritmos de escalonamento de CPU alternam rapidamente entre as tarefas, dando a ilusão de que cada tarefa está sendo executada simultaneamente. O sistema operacional precisa garantir a operação correta do sistema computadorizado. Para impedir que os programas do usuário interfiram com a operação correta do sistema, o hardware tem dois modos: modo usuário e modo kernel. Diversas instruções (como instruções de E/S e instruções de parada) são privilegiadas e só podem ser Prof. Michael A. Pontes michaelpontes@unip.br [2] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne. executadas no modo kernel. A memória em que o sistema operacional reside também precisa ser protegida contra modificação pelo usuário. Um temporizador impede loops infinitos. Esses recursos (modo dual, instruções privilegiadas, proteção de memória e interrupção de temporizador) são blocos de montagem básicos usados pelos sistemas operacionais para obter a operação correta. Um processo (ou tarefa) é a unidade de trabalho fundamental em um sistema operacional. A gerência de processos inclui a criação e exclusão de processos e o fornecimento de mecanismos para os processos se comunicarem e se sincronizarem entre si. Um sistema operacional gerencia a memória registrando quais partes da memória estão sendo usadas e por quem. O sistema operacional também é responsável por alocar dinamicamente e liberar o espaço de memória. O espaço do arnazenamento também é gerenciado pelo sistema operacional, incluindo fornecer os sitemas de arquivo para representar arquivos e diretórios e gerenciar o espaço nos dispositivos de armazenamento em massa. Os sistemas operacionais também precisam se preocupar com a proteção e a segurança do sistema operacional e dos usuários. A proteção inclui os mecanismos que controlam o acesso de processos ou usuários aos recursos que estão disponíveis pelo sistema computadorizado. As medidas de segurança são responsáveis por defenter um sistema de computador de ataques externos ou internos. Os sistemas distribuídos permitem que os usuários compartilhem recrusos em hosts espalhados geograficamente, conectados por uma rede de computadores. Os serviços podem ser fornecidos pelo modelo cliente-servidor ou peer-to-peer. Em um sistema em clusters, diversas máquinas podem realizar cálculos sobre os dados residindo no armazenamento compartilhado, e a computação pode continuar mesmo quando algum subconjunto de membros do cluster falha. LANs e WANs são os dois tipos básicos de redes. LANs permitem que os processadores distribuídos por uma pequena área geográfica se comuniquem, enquanto as WANs permitem que os processadores distribuídos por uma área maior se comuniquem. As LANs normalmente são mais rápidas que as WANs. Existem vários sistemas computadorizados que atendem a finalidades específicas. Eles incluem sistemas operacionais de tempo real projetados para ambientes embutidos, como dispositivos de consumidor, automóveis e robótica. Os sistemas operacionais de tempo real têm restrições de tempo bem definidas, fixadas. O processamento precisa ser feito dentro das restrições definidas ou então o sistema fracassará. Os sistemas multimídia envolvem a remessa de dados multimídia e normalmente têm requisitos especiais de exibir ou tocar um áudio, vídeo ou streams de áudio e vídeo sincronizados. Recentemente, a influência da Internet e da World Wide Web encorajou o desenvolvimento de sistemas operacionais modernos, que incluem navegadores Web e software de rede e comunicação como recursos integrados. Prof. Michael A. Pontes michaelpontes@unip.br [3] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne. Parte 2 – Estruturas do sistema operacional Os sistemas operacionais fornecem diversos serviços. No nível mais baixo, as chamadas de sistema permitem que um programa em execução faça requisições diretamente do sistema operacional. Em um nível mais alto, o interpretador de comandos ou shell provê um mecanismo para o usuário emitir uma requisição sem escrever um programa. Os comandos podem vir de arquivos durante a execução do modo de lote ou diretamente por um terminal, quando em um modo interativo ou de tempo compartilhado. Os programas do sistema são fornecidos para satisfazer muitas requisições comuns do usuário. Os tipos de requisições variam de acordo com o nível da requisição. O nível de chamada de sistema precisa prover as funções básicas, como controle de processos e manipulação de arquivos e dispositivos. As requisições de nível mais alto, satisfeitas pelo interpretador de comandos ou pelos programas do sistema, são traduzidas para uma sequência de chamadas de sistema. Os serviços do sistema podem ser classificados em diversas categorias: controle de programa, requisições de status e requisições de E/S. Os erros do programa podem ser considerados requisições de serviço implícitas. Quando os serviços do sistema são definidos, a estrutura do sistema operacional pode ser desenvolvida. Diversas tabelas são necessáriaspara registrar as informações que definem o estado do sistema computadorizado e o status da tarefa do sistema. O projeto de um sistema operacional novo é uma grande tarefa. É importante que os objetivos do sistema sejam bem definidos antes que o projeto seja iniciado. O tipo de sistema desejado é o alicerce para as escolhas entre diversos algoritmos e estratégias necessárias. Como um sistema operacional é grande, a modularidade é importante. Projetar um sistema como uma sequência de camadas ou usar um microkernel é considerado uma boa técnica. O conceito de máquina virtual usa a técnica em camadas e trata o kernel do sistema operacional e o hardware como se tudo fosse hardware. Até mesmo outros sistemas operacionais podem ser carregados em cima dessa máquina virtual. No decorrer de todo o ciclo de projeto do sistema operacional, temos de ter o cuidado de separar as decisões de política dos detalhes da implementação (mecanismos). Essa separação permite o máximo de flexibilidade se decisões de política tiverem de ser alteradas mais tarde. Os sistemas operacionais agora são quase sempre escritos em uma linguagem de implementação de sistemas ou em uma linguagem de alto nível. Esse recurso melhora sua implementação, manutenção e portabilidade. Para criar um sistema operacional para determinada configuração de máquina, temos de realizar a geração do sistema. Para um sistema computadorizado iniciar sua execução, a CPU precisa inicializar e começar a executar o programa de boot no firmware. O bootstrap pode executar o sistema operacional se o sistema operacional também estiver no firmware ou então pode completar uma sequência em que carrega programas progressivamente mais inteligentes a partir do firmware e disco, até que o próprio sistema operacional esteja carregado na memória e em execução. Prof. Michael A. Pontes michaelpontes@unip.br [4] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne. Parte 3 – Processos Um processo é um programa em execução. Enquanto um processo é executado, ele muda de estado. O estado de um processo é definido pela atividade atual desse processo. Cada processo pode estar em um dos seguintes estados: novo (new), pronto (ready), executando (running), aguardando (waiting) ou terminado (terminated). Cada processo é representado no sistema operacional por seu próprio bloco de controle de processo (PCB). Um processo, quando não estiver sendo executado, é colocado em alguma fila de espera. Existem duas classes principais de filas em um sistema operacional: fila de requisição de E/S e a fila de prontos (ready queue). A fila de prontos contém todos os processos que estão prontos para serem executados e aguardando pela CPU. Cada processo é representado por um PCB, e os PCBs podem ser vinculados para formar uma fila de prontos. O escalonamento de longo prazo (tarefas) é a seleção de processos para que tenham permissão para disputar a CPU. Normalmente, o escalonamento de longo prazo é bastante influenciado por considerações de alocação de recursos, em especial o gerenciamento de memória. O escalonamento de curto prazo (CPU) é a seleção de um processo a partir da fila de prontos. Os sistemas operacionais precisam fornecer um mecanismo para os processos pai criarem novos processos filhos. O pai pode esperar que seus filhos terminem antes de prosseguir ou o pai e os filhos podem ser executados concorrentemente. Existem vários motivos para permitir a execução simultânea: compartilhamento de informações, agilidade da computação, modularidade e conveniência. Os processos em execução no sistema operacional podem ser processos independentes ou processos cooperativos. Os processos cooperativos precisam de mecanismos de comunicação entre processos para se comunicarem entre si. A comunicação é obtida por meio de dois esquemas complementares: memória compartilhada e troca de mensagens. O método de memória compartilhada exige que os processos em comunicação compartilhem algumas variáveis. Os processos deverão trocar informações por meio dessas variáveis compartilhadas. Em um sistema de memória compartilhada, a responsabilidade por fornecer comunicação recai sobre os programadores de aplicações; o sistema operacional só precisa oferecer a memória compartilhada. O método de troca de mensagens permite aos processos trocarem mensagens. A responsabilidade por fornecer a comunicação pode ficar com o próprio sistema operacional. Esses dois esquemas não são mutuamente exclusivos, podendo ser usados ao mesmo tempo dentro de um único sistema operacional. A comunicação nos sistemas cliente-servidor pode utilizar (1) sockets, (2) remote procedure calls (RPCs) ou (3) o remote method invocation (RMI) da Java. Um socket é definido como uma extremidade para comunicação. Uma conexão entre um par de aplicações consiste em um par de sockets, um em cada ponta do canal de comunicação. RPCs são outra forma de comunicação distribuída. Uma RPC ocorre quando um processo (ou thread) chama um procedimento em uma aplicação remota. RMI é a versão Java de uma RPC. RMI permite que uma thread invoque um método em um objeto remoto da mesma forma como invocaria um método em um objeto local. A principal distinção entre RPCs e RMI é que, na RPC, os dados Prof. Michael A. Pontes michaelpontes@unip.br [5] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne. são passados a um procedimento remoto na forma de uma estrutura de dados comum, enquanto a RMI permite que os objetos sejam passados nas chamadas ao método remoto. Prof. Michael A. Pontes michaelpontes@unip.br [6] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne. Parte 4 – Threads Uma thread é um fluxo de controle dentro de um processo. Um processo multithreads contém diversos fluxos de controle diferentes dentro do mesmo espaço de endereços. Os benefícios do uso de multithreads incluem maior responsividade ao usuário, compartilhamento de recursos dentro do processo, economia e a capacidade de tirar proveito das arquiteturas multiprocessadas. As threads no nível do usuário são threads visíveis ao programador e desconhecidas do kernel. O kernel do sistema operacional aceita e gerencia as threads no nível de kernel. Em geral, as threads no nível do usuário são mais rápidas de criar e gerenciar do que as threads no nível de kernel, portanto não é necessário nenhuma intervenção do kernel. Três tipos diferentes de modelos relacionam as threads de usuário e de kernel. O modelo muitos para um associa muitas threads de usuário a uma única thread de kernel. O modelo um para um associa cada thread de usuário a uma thread de kernel correspondente. O modelo muitos para muitos multiplexa muitas threads de usuário a um número menor ou igual de threads de kernel. A maior parte dos sistemas operacionais modernos provê suporte do kernel para as threads; entre eles estão Windows 98, NT, 2000, XP, Vista e Seven, além do Solaris e do Linux. Bibliotecas de threads fornecem ao programador de aplicação uma API para criar e gerenciar threads. Três bibliotecas de threads principais são as mais utilizadas: Pthreads POSIX, threads Win32 para sistemas Windows e threads Java. Os programas multithreads apresentam muitos desafios para o programador, incluindo a semântica das chamadas de sistema fork( ) e exec( ). Outras questões incluem o cancelamento da thread, o tratamento de sinais e os dados específicos da thread. A API Java resolve muitos desses problemas com threads. Prof. Michael A. Pontes michaelpontes@unip.br [7] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne.Parte 5 – Escalonamento de CPU O escalonamento de CPU é a tarefa de selecionar um processo em espera na fila de prontos e alocar a CPU para ele. A CPU é alocada ao processo selecionado pelo despachante. O escalonamento FCFS (First Come, First Served) é o algoritmo de escalonamento mais simples, mas pode fazer com que processos curtos esperem pelos processos muito longos. O escalonamento SJF (Shortest Job First) provavelmente é o ideal, fornecendo a menor média de tempo de espera. A implementação do escalonamento SJF é difícil porque também é difícil prever a duração do próximo burst de CPU. O algoritmo SJF é um caso especial do algoritmo geral de escalonamento por prioridade, que simplesmente aloca a CPU ao processo de mais alta prioridade. Tanto o escalonamento por prioridade quanto o SJF podem sofrer de starvation. O envelhecimento é uma técnica para impedir a starvation. O escalonamento Round-Robin (RR) é mais apropriado para um sistema de tempo compartilhado (interativo). O escalonamento RR aloca a CPU ao primeiro processo da fila de prontos para q unidades de tempo, onde q é o quantum de tempo. Depois de q unidades de tempo, se o processo não tiver abandonado a CPU, ele é preemptado, e o processo é colocado no final da fila de prontos (ready queue). O problema principal é a seleção do quantum de tempo. Se o quantum for muito grande, o escalonamento RR se degenera para o escalonamento FCFS; se for muito pequeno, o custo adicional do escalonamento, na forma de tempo para troca de contexto, se torna excessivo. O algoritmo FCFS é não preemptivo; o algoritmo RR é preemptivo. Os algoritmos SJF e de prioridade podem ser preemptivos ou não. Os algoritmos multilevel queue permitem a diferentes algoritmos serem utilizados para diversas classes de processos. O mais comum é uma fila interativa de primeiro plano, que utiliza o escalonamento RR, e a fila batch do segundo plano, que utiliza o escalonamento FCFS. A multilevel feedbak-queue permite aos processos mudarem de uma fila para outra. Muitos sistemas computadorizados contemporâneos admitem vários processadores; cada processador realiza seu escalonamento de forma independente. Em geral, cada processador mantém sua própria fila privativa de processos (ou threads), todos disponíveis para execução. Questões relacionadas ao escalonamento de multiprocessadores incluem a afinidade de processador e balanceamento de carga. Os sistemas operacionais que admitem threads no nível do kernel precisam escalonar threads – e não processos – para serem executados. Isso acontece com o Solaris e o Windows. Os dois sistemas escalonam threads usando algoritmos de escalonamento preemptivos, baseados em prioridade, incluindo o suporte para threads em tempo real. O escalonador de processos do Linux também usa um algoritmo baseado em prioridade com suporte de tempo real. Os algoritmos de escalonamento para esses três sistemas operacionais normalmente favorecem os processos interativos em detrimento dos processos batch e CPU-bound. A especificação para escalonamento em Java é definida de forma solta: threads com prioridade mais alta terão preferência em relação a threads com prioridade mais baixa. A grande variedade de algoritmos de escalonamento exige que tenhamos métodos para selecionar entre os algoritmos. Os métodos analíticos utilizam a análise matemática para determinar o desempenho de um algoritmo. Os métodos por simulação determinam o Prof. Michael A. Pontes michaelpontes@unip.br [8] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne. desempenho imitando o algoritmo de escalonamento em uma amostra “representativa” de processos e calculando o desempenho resultante. Contudo, a simulação no máximo pode fornecer uma aproximação do desempenho real do sistema; a única técnica confiável para avaliar um algoritmo de escalonamento é implementar o algoritmo em um sistema real e monitorar seu desempenho em um ambiente no mundo real. Prof. Michael A. Pontes michaelpontes@unip.br [9] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne. Parte 6 – Sincronismo de processos Dada uma coleção de processos ou threads sequenciais cooperativas que compartilham dados, a exclusão mútua precisa ser fornecida. Uma solução é garantir que uma sessão crítica do código esteja em uso apenas por um processo ou thread de cada vez. Existem diversos algoritmos para solucionar o problema de sessão crítica, supondo que somente o interlock de armazenamento esteja disponível. A principal desvantagem dessas soluções codificadas pelo usuário é que todas exigem a espera ocupada. Os semáforos contornam essa dificuldade. Os semáforos podem ser usados para solucionar diversos problemas de sincronismo e podem ser implementados com eficiência especialmente se o suporte do hardware para operações atômicas estiver disponível. Diversos problemas de sincronismo (como os problemas de bounded buffer, leitores- escritores e filósofos à mesa de jantar) são importantes, em especial porque são exemplos de uma grande classe de problemas de controle de concorrência. Esses problemas são usados para testar quase todo esquema de sincronismo proposto recentemente. O sistema operacional precisa fornecer meios de proteger contra erros de temporização. Diversas construções da linguagem foram propostas para enfrentar esses problemas. Monitores fornecem o mecanismo de sincronismo para compartilhamento de tipos de dados abstratos. Uma variável de condição provê um método pelo qual um procedimento de monitor pode bloquear sua execução até que seja sinalizado para continuar. A linguagem Java provê várias ferramentas para coordenar as atividades multithread acessando dados compartilhados por meio dos mecanismos synchronized, wait(), notify() e notifyAll(). Além disso, a API Java fornece suporte para locks de exclusão mútua, semáforos e váriaveis de condição. Os sistemas operacionais também provêem suporte para sincronismo. Por exemplo, Solaris, Windows e Linux fornecem mecanismos como semáforos, mutexes, spinlocks e variáveis de condição, para controlar o acesso aos dados compartilhados. A API Pthreads provê suporte para mutexes e variáveis de condição. Uma transação é uma unidade de programa que precisa ser executada atomicamente, ou seja, ou todas as operações associadas a ela são executadas até o fim ou nenhuma é executada. Para garantir a atomicidade, apesar de falha do sistema, podemos usar um log de escrita antecipada. Todas as atualizações são registradas no log, que é mantido no arnazenamento estável. Se houver uma falha no sistema, as informações do log são usadas na restauração do estado dos itens de dados atualizados, o que é feito com o uso de operações undo e redo. Para reduzir o custo adicional na pesquisa do log após o surgimento de uma falha do sistema, podemos usar um esquema de checkpoint. Para garantir a serialização quando a execução de várias transações se sobrepõe, temos de usar um esquema de controle de concorrência. Vários esquemas de controle de concorrência garantem a serialização, atrasando uma operação ou abortando a transação que emitiu a operação. Os mais comuns são os protocolos de lock e os esquemas de ordenação com estampa de tempo. Prof. Michael A. Pontes michaelpontes@unip.br [10] Fonte: Sistemas Operacionais com Java – A. Silberschatz, P. B. Galvin, G. Gagne. Parte 7 – Deadlocks Um estado de deadlock ocorre quando dois ou mais processos estão esperando indefinidamente por um evento que só pode ser causado por um dos processos esperando. Em especial, existem três métodos para lidar com os deadlocks: Usar algum protocolo paraprevenir ou evitar deadlocks, garantindo que o sistema nunca entrará em um estado de deadlock. Permitir que o sistema entre no estado de deadlock, detectá-lo e depois recuperar. Ignorar o problema e fingir que os deadlocks nunca ocorrem no sistema. A terceira solução é utilizada pela maioria dos sistemas operacionais, incluindo UNIX e Windows, bem como a JVM. Um deadlock só pode ocorrer se quatro condições necessárias forem satisfeitas simultaneamente no sistema: exclusão mútua, manter e esperar, não preempção e espera circular. Para prevenir deadlocks, podemos garantir que pelo menos uma das condições necessárias nunca seja satisfeita. Um método para evitar deadlocks, menos rigoroso do que os algoritmos de prevenção, exige que o sistema operacional tenha informações a priori sobre como cada processo utilizará os recursos. O algoritmo do banqueiro, por exemplo, exige informações a priori sobre o número máximo de cada classe de recurso que pode ser requisitada em cada processo. Usando essas informações, podemos definir um algoritmo para evitar deadlock. Se um sistema não empregar um protocolo para garantir que os deadlocks nunca ocorrerão, então um esquema de detecção e recuperação terá de ser empregado. Um algoritmo de detecção de deadlock precisa ser invocado para determinar se ocorreu um deadlock. Se um deadlock for detectado, o sistema terá de se recuperar terminando alguns de seus processos em deadlock ou apropriando-se dos recursos de alguns dos processos em deadlock. Onde a preempção for usada para cuidar dos deadlocks, três questões precisam ser focalizadas: seleção de vítima, reversão e starvation. Em um sistema que seleciona vítimas para efetuar rollback principalmente com base nos fatores de curso, a startavion poderá ocorrer, e o processo selecionado nunca completa sua tarefa designada. Finalmente, os pesquisadores argumentaram que nenhuma das técnicas básicas isoladamente é apropriada para o espectro inteiro de problemas de alocação de recurso nos sistemas operacionais. As técnicas básicas podem ser combinadas, permitindo a seleção de uma técnica ideal para cada classe de recursos em um sistema.
Compartilhar