Um Estudo Baseado na Visão de Silberschatz sobre a Complexidade dos Sistemas Operacionais Jonathan Luís Hackenhaar Faculdade Cenecista de Osório (FACOS) Rua 24 de maio, 141 – 95.520-000 – Osório – RS – Brasil [email protected] Resumo. O presente artigo tem como objetivo falar sobre o funcionamento dos Sistemas Operacionais modernos, descrevendo a forma como esse software atua fazendo a ligação entre o hardware e o usuário que o utiliza, partindo dos pressupostos desenvolvidos por Abraham Silberschatz que introduz aos Sistemas Operacionais uma abordagem didática para melhor entendimento de sua complexidade. Palavras-chave: Sistemas operacionais, Abraham Silberchatz, Abordagem didática 1. Introdução Os sistemas operacionais foram desenvolvidos inicialmente para tentar escalonar as atividades do sistema de computação, garantindo o bom funcionamento deste e em segundo lugar para fazer uma interação entre usuário e o ambiente computacional sendo possível realizar a execução de programas. No inicio os sistemas operacionais eram usados como console principal, com o passar do tempo ele foi sendo incrementado por outros programas que melhoraram a conveniência de programação do sistema, porém em contra ponto exigiam determinado tempo em sua configuração. A fim de melhorar e aperfeiçoar esse tempo desperdiçado, o sistema operacional recebeu lots com jobs semelhantes. O Sistema Operacional é um programa que funciona como ponte entre o hardware e o usuário do computador. O seu funcionamento facilita a vida do usuário em função da interface que ele disponibiliza dando conveniência à utilização dos programas e gerenciamento da máquina com o objetivo de usar o hardware de forma eficiente. Eles podem ser divididos em hardware, CPU, memória, e dispositivos de entrada e saída (I/O). Ele fica responsável por gerenciar o funcionamento de todos esses componentes, dessa forma podemos destacar como um dos objetivos desse sistema coordenar o uso do hardware entre os usuários e os aplicativos que são executados. Também compõe parte dessa estrutura que envolve o Sistema Operacional os programas (aplicativos) e os usuários, que podem ser pessoas e outras máquinas. Os sistemas de computadores pessoais, que foram adaptados dando prioridade ao uso e conveniência, que se beneficiaram da forma de desenvolvimento dos sistemas operacionais de mainframes, exemplo Windows e Linux. Os sistemas paralelos que utilizam multiprocessadores, compartilhando o barramento têm como vantagem uma maior produção (thoughput). Nos sistemas em rede, que fornecem recursos como o Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 32 compartilhamento de arquivos e uma metodologia diferenciada, que permite a troca de mensagem e processos em computadores diferentes, e os sistemas operacionais distribuídos que são aqueles em que cada processador tem a sua memória local e se comunicam por barramentos ou linhas telefônicas. Os sistemas operacionais existem porque é uma forma razoável de resolver o problema de criar um sistema de computação que possa ser usado. O objetivo primordial dos sistemas de computação é executar programas de usuário e tornar fácil a resolução dos problemas de usuário. Para atingir essa meta, o hardware é construído. Como o hardware por si só não é particularmente fácil de usar, programas aplicativos são desenvolvidos. Esses vários programas exigem certas operações comuns, como aquelas que controlam os dispositivos de I/O. As funções comuns de controle e alocação de recursos são então reunidas em um único software: o sistema operacional. O artigo está estruturado da seguinte forma: na seção dois o tema de discussão é sobre as estruturas de sistemas operacionais, já na seção três a temática é processos, na seção quatro o assunto exposto é sobre as threads, o escalonamento de CPU é o tema da seção cinco, na seção seis a sincronização de processos será agregado ao estudo do artigo, já na seção sete o assunto abordado é sobre deadlocks, a seção oito é responsável pela gerência de memória, a memória virtual é o assunto da seção nove e a seção dez fala sobre o sistema de arquivos encerrando com as conclusões. 2. Estruturas de Sistemas Operacionais O sistema operacional tem como objetivo oferecer um conjunto de funções que são úteis ao usuário, que a interface com o usuário, a execução de programas, operações de entrada e saída, manipulação de sistema de arquivos, comunicação entre os componentes, detecção de erros, depuração (para aumentar a eficiência) alocação de recursos, contabilização, proteção e segurança de todos os seus componentes. Um dos recursos mais visíveis e utilizados em sistema operacional é a interface com o usuário, dessa forma as tarefas começam a serem feitas com a interação do mesmo, que, por exemplo, pode ser feita por linha de comando. Essa interface pode ser gráfica, amigável ou ambas simultaneamente (GUI e CLI). O sistema operacional se comunica através de chamadas de sistema, que geralmente são escritas em linguagens de alto nível, como C++, por exemplo. Essas chamadas são usadas para que as tarefas solicitadas ao sistema operacional pelo usuário e softwares possam ser feitas. A implementação das chamadas de sistema acontece quando a sua interface evoca ao kernel do sistema e retorna o status e os valores de retorno, as chamadas de sistemas podem ser de vários tipos, tais como: controle de processos, gerenciamento de arquivos e dispositivos, manutenção de informações, comunicação e proteção. Dentro dessa estrutura temos alguns programas (as interfaces de usuário) que propiciam um ambiente conveniente para a execução de programas que acabam por compor a visão do usuário do sistema operacional. Esses programas permitem o gerenciamento de arquivos, modificação, suporta linguagem de programação, permite comunicação tornando o sistema operacional útil ao usuário. O projeto de implementação de um sistema operacional deve estar em equilíbrio com os objetivos do usuário e os objetivos do próprio sistema, ele deve ser fácil de Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 33 projetar, flexível, confiável e eficiente levando em conta a política e o mecanismo envolvidos nesse processo. O sistema operacional se divide em duas estruturas: Estruturas simples – nesse tipo de estrutura relação de custo-benefício baseado em menor espaço é priorizada, como por exemplo, nos sistemas monolíticos, sua estrutura, interface e níveis de funcionalidade não são bem separados (exemplo MS-DOS). Estrutura em camadas – nesse contexto o sistema operacional é dividido em camadas (níveis), onde cada uma é constituída respeitando a hierarquia das camadas, a camada inferior é o hardware e a camada mais alta é a interface com o usuário, é projetado para que cada uma use as funções e serviços somente das camadas de baixo nível, como mostrado na Figura 1, que exemplifica a estrutura em questão. Figura 1. Modelo de Estrutura em Camadas (MS-DOS) Fonte: http://professor.unisinos.br/barbosa/SO/so.htm Temos também a Estrutura Microkernel – move o kernel para o espaço do usuário e a comunicação ocorre entre módulos em nível de usuário usando a troca de mensagens. Possui vantagens tais como se adaptar em novas arquiteturas e é mais seguro, porém sobrecarrega em função da comunicação entre o modo usuário e o modo kernel. E finalizando com a estrutura em Módulos – cada módulo (no kernel) é responsável se comunica através de interfaces e o módulo é carregado no kernel quando necessário. As máquinas virtuais nos levam a abordagem de sistemas em camadas ao extremo, tratando o hardware e o kernel como se ambos fossem hardware. Elas fornecem uma interface idêntica a do puro hardware, criando a ilusão de múltiplos processos cada um executando em seu próprio processador com sua própria memória (virtual). Elas são boas para testes, desenvolvimento e não ocupam espaço demasiado, em alguns casos permitem o compartilhamento de arquivos (controlados), mas que facilitam muito a vida de quem as utiliza. O sistema operacional também conta com a depuração, que é responsável por encontrar e corrigir erros, gerando logs que contém informações desses erros, por exemplo, a falha de um programa pode capturar a memória do processo e falhas no Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 34 sistema operacional que podem consumir memória do kernel, além de travamentos. Depurar é algo duas vezes mais difícil que escrever códigos pela primeira vez, entretanto, se tu escreves o código tão inteligente quanto possível, tu não és, por definição, inteligente suficiente para depurá-lo. Os sistemas operacionais são projetados para executar em qualquer máquina de uma determinada classe. O sistema operacional conta com alguns programas que o ajudam em algumas tarefas, tais como: SYSGEM – obtém informações a respeito da configuração específica do hardware. BOOTING – inicia o computador carregando o kernel. BOOTSTRAP program – código armazenado em memória ROM que localiza o kernel e carrega-o na memória para iniciar a sua execução. Dessa forma o sistema operacional é carregado e começa o seu funcionamento, através do boot. 3. Processos Um processo é uma fatia de programa que está executando, ele executa uma variedade de programas, como os sistemas batch e os sistemas de time sharing. O processo progride em sua execução de maneira sequencial, incluindo um contador, uma pilha e uma seção de dados. Durante a sua execução sofre alterações de seu estado, que pode ser: Novo – quando o processo está sendo criado. Executando – quando ele está executando as suas instruções. Esperando – quando está a espera de algum evento acontecer. Pronto – quando o processo está esperando ser associado a algum processador e Terminado – quando o processo termina sua execução, como mostra a Figura 2 abaixo. Figura 2. Diagrama de Estados do Processo Fonte: http://professor.unisinos.br/barbosa/SO/so.htm A Figura 3 encarrega-se de expor os dados que são armazenados pelo PCB – Process Control Block, que carrega as informações associadas com cada processo, como o seu estado, contador de programa, número ID, registradores de CPU, informações de escalonamento da CPU, informações do gerenciamento de memória, contabilidade e o status de E/S. Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 35 Figura 3. PCB – Process Control Block Fonte: http://professor.unisinos.br/barbosa/SO/so.htm Como são muitos os processos que sempre estão querendo executar e assim dar continuidade as suas instruções, surgiu a necessidade de ter o escalonamento de processos. O escalonamento de processos pode ser em fila de jobs, que é o conjunto de todos os processos do sistema, fila de processos prontos, que é o conjunto de todos os processos residentes na memória principal prontos e esperando para executar e a fila de dispositivos, quando há um conjunto de processos esperando por um dispositivo de E/S, ressalvando que devido ao seu estado, o processo pode migrar entre as várias filas de execução. Quando temos qualquer tipo de escalonamento, esse deve ser gerenciado pelo escalonador, no caso dos processos podemos contar com dois escalonadores: o de longo prazo (ou escalonador de jobs) que seleciona qual processo deve ser executado e aloca a CPU para ele. A CPU, quando alterna para o outro processo, faz com que o sistema salve o estado do processo anteriormente salvo do processo novo via troca de contexto, que é representado na PCB. Um processo pode criar outro processo (pai-filho), formando uma árvore de processos. Na relação pai-filho todos os recursos são compartilhados, porém, o filho compartilha um subconjunto dos recursos do pai, na execução os dois (pai e filho) executam concorrentemente e o pai espera até o filho terminar. Os processos em um sistema podem ser independentes ou cooperantes. Os que são independentes não podem afetar ou serem afetados pela execução de outro processo, já os cooperantes podem ser afetados pela execução de outro processo, a cooperação entre processos tem como razões: o compartilhamento de informações, aumento na velocidade da computação, modularidade e conveniência. Os processos cooperantes precisam da comunicação entre processos (IPC – interprocess communication) que pode ser memória compartilhada e troca de mensagens. A troca de mensagens é o mecanismo mais usado para os processos se comunicarem e sincronizarem suas ações funciona tendo um send que é quem está mandando a mensagem e o receive que é quem recebe o que está sendo enviado, através de um link de Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 36 comunicação entre eles. Nesse tipo de comunicação temos duas subdivisões em relação ao seu funcionamento: comunicação direta, onde os processos envolvidos enviam mensagens identificando qual mandou e qual irá receber. E a comunicação indireta, que a mensagem cai em um mailbox e se o processo que vai receber também deve estar compartilhando a mesma mailbox. A troca de mensagens pode ser bloqueante ou não bloqueante. Outro tipo de comunicação entre os processos é a comunicação cliente-servidor, que subdivide em sockets, pipes, RPC (chamadas a procedimentos remoto) e a RMI (invocação remota de método). Um socket é definido como um ponto final de comunicação, a concatenação de um endereço IP e porta e a comunicação ocorre entre pares de sockets. As pipes agem como canalizações permitindo a comunicação entre processos, elas permitem a comunicação no estilo produtor-consumidor, as pipes nomeadas são mais poderosas que os pipes comuns, p o i s não é necessária a relação paifilho entre processos comunicadores. Já a chamada a procedimento remoto (RPC) abstrai chamadas de procedimento entre os processos executando nos sistemas de rede e a invocação remota de método (RMI) é um mecanismo Java similar a RPC. 4. Threads Um pequeno programa que trabalha como um subsistema independente de um programa maior, executando tarefas específicas. Um software dividido em várias threads pode executar mais rápido que um programa monolítico, pois tem várias tarefas. O processo existe para que a thread possa existir, ela funciona “em cima” dele, existem processos com uma e com múltiplas threads. A Figura 4 mostra a comparação entre os dois modelos de threads (uma e múltiplas). Figura 4. Processos com uma e com múltiplas threads. Fonte: http://professor.unisinos.br/barbosa/SO/so.htm Os benefícios de ter as threads são responsividade e o compartilhamento de Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 37 recursos, por exemplo, em um servidor multithread (de um site de notícias, por exemplo) cada uma de suas threads é responsável por cuidar de cada conexão com cada usuário. Podem ser divididas em dois níveis: usuário e kernel. As threads de nível usuário tem menores permissões de acesso e executam funções, como as API’s (Posix Pthreads, Win32 threads e as Java threads). Já as threads em nivel kernel são mais poderosas e liberam a CPU para a execução das de nível usuário. O sistema operacional está organizado para ter as threads kernel. O mapeamento das threads usuário e kernel se realiza das seguintes maneiras: Muitos- para-um: quando várias threads são mapeadas por uma única thread kernel. Umpara-um: quando uma thread é mapeada por uma thread kernel. Muitos-para-muitos: quando as threads usuário são mapeadas por várias threads kernel, essa é considerada a melhor política de mapeamento. Modelo de dois níveis: quando dois tipos diferentes de mapeamento são usados simultaneamente. As threads trazem mais funcionalidade e agilidade na execução sendo que a melhora já consolidada pelos processos sofre uma mutação positiva com a melhora advinda da utilização das threads. 5. Escalonamento de CPU Com o surgimento dos processos, threads, a ideia básica de multitarefa e a concorrência, o sistema operacional precisou escalonar a CPU para que suportasse todos esses elementos. Por exemplo, se temos uma CPU para duas mil threads, o sistema operacional tem que gerenciar todas elas, os seus acessos aos recursos limitados, fazendo com que elas executem de forma organizada, isso é o escalonamento, que se baseia em uma heurística (algoritmo), um exemplo de escalonamento são as filas do banco, que antes eram uma fila por cada caixa e hoje é por senha com as filas, no caso podemos apontar duas heurísticas nesse exemplo. No contexto de escalonamento da CPU temos o Dispatcher (despachante) ele é o responsável por escolher quem vai receber a CPU, através da heurística que prioriza no sistema operacional em questão. Para que as heurísticas funcionem, elas precisam de algoritmos para se basearem e no escalonamento de CPU, temos alguns algoritmos que são usados como o FCFS (primeiro a chegar, primeiro a ser servido), usado no time sharing, esse algoritmo faz com que os processos que chegam primeiro sejam executados primeiro, não importando o tempo de cada um. Outro algoritmo que é usado é o SJF (menor job primeiro) que tem como prioridade reduzir o tempo de espera dos processos na fila de execução. O processo com menor tempo é executado antes. O SJF é usado quando nós sabemos o tempo dos processos (através de uma simulação, por exemplo). O próximo algoritmo é o de Prioridades. Cada processo tem um número de prioridade e a CPU aloca o processo com a maior prioridade primeiro, que pode ser preemptivo (que permite interrupções) e não preemptivo (que permite interrupções). Esse algoritmo pode ocasionar um problema conhecido como Starvation (abandono de processo) no caso os processos de baixa prioridade podem nunca executar. A solução para esse problema é o aging (envelhecimento) que ao passar do tempo aumenta a prioridade dos processos fazendo com sejam executados e não ocorra a starvation. Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 38 O Round Robin (algoritmo de escalonamento circular) determina o escalonamento através de fatias de tempo de processamento – quantum - , quando acaba esse tempo o processo perde a CPU e essa vai para o próximo processo. A combinação dos algoritmos Round Robin e Prioridades compõe o modo de escalonamento dos sistemas operacionais modernos. A alocação com múltiplas filas tem por objetivos criar essas filas com escalonamento e dar prioridades. O escalonador começa de cima para baixo. Somente desce da fila quando está fica completa. O sistema operacional organiza as filas conforme as suas prioridades. A desvantagem é que os processos com maior prioridades ficam sem executar. O último algoritmo apresentado pela Figura 5 que é o de múltiplas filas com realimentação. Nesse algoritmo os processos transitam entre as filas (algoritmo time sharing) os processos vai sendo medidos pela sua prioridade e são executados. Cada fila pode ser gerenciada por um algoritmo diferente. Figura 5. Escalonamento com múltiplas filas Fonte: http://professor.unisinos.br/barbosa/SO/so.htm O escalonamento de threads funciona da seguinte maneira, as threads kernel são escalonadas pela CPU e recebem o tempo, fazendo com que as threads usuário executem as suas instruções. No contexto do escalonamento temos o escalonamento com vários processadores homogêneos que dão mais poder de processamento na fila, a CPU é entregue mais rápido e o escalonamento vai mais ligeiro. E com processadores multicore quando há um processador que suporta vários tipos de execução. 6. Sincronização de Processos O compartilhamento de recursos entre processos pode gerar situações indesejáveis, os mecanismos de sincronização garantem a comunicação entre processos concorrentes. Com o acesso dos dados compartilhados surgiu o problema da região crítica e a sincronização apresenta soluções para esse problema. A sincronização tem como um de seus objetivos manter a consistência de dados, requerendo a utilização de mecanismos para garantir a execução ordenada de processos Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 39 cooperantes. Algumas soluções para o problema da região crítica são a exclusão mútua, progresso e a espera limitada. A exclusão mútua acontece quando um processo está executando sua região critica e outro processo pode estar querendo executar a sua região crítica. O progresso é quando nenhum processo está executando uma região crítica e existem processos que desejam entrar nas regiões críticas deles, então a escolha do próximo processo que irá entrar na região crítica ao pode ser adiada indefinidamente. A espera limitada acontece quando existe um limite para o número de vezes que os outros processos são selecionados para entrar nas regiões criticas deles, depois que um processo fez uma requisição para entrar em sua região crítica e antes que essa requisição seja atendida. Muitos sistemas fornecem suporte de hardware para código de seção crítica como os sistemas monoprocessados, que podem desabilitar interrupção, o código em execução pode executar sem preempção, geralmente muito ineficiente em sistemas multiprocessados, os sistemas operacionais que usam isso não escalam. As arquiteturas modernas fornecem instruções atômicas especiais de hardware como testar uma posição de memória e setar um valor ou trocar conteúdos de duas posições na memória. Dentre as soluções também podemos contar com o uso de Lock (bloqueios), Instruções test and set, Swap, Semáforo e Monitores. O Semáforo é a solução mais geral e simples de ser implementada, variável inteira que só pode ser manipulada por duas instruções wait e signal, na exclusão mútua essas instruções funcionam como protocolos de entrada e saída, valor maior a zero, recurso liberado, valor igual à zero, processo impedido de acesso. A solução trazida pelos semáforos é aplicada ao problema de sincronização condicional, em geral se existe um processo que deve ser notificado sobre a ocorrência, pode-se utilizar um semáforo associado ao evento esperado para sincronizar ambos os recursos ao mesmo tempo. Existe o Semáforo Circular que permite somente valores inteiros e o Semáforo Binário com valores em zero e um. O problema do Semáforo é que é difícil para programar, identificando as regiões críticas colocando um wait e signal, caso aconteça o esquecimento de uma só pode gerar vários problemas. Os monitores, mostrado pela Figura 6, são abstrações de alto nível que fornecem um mecanismo conveniente de sincronização de processos, somente um processo pode estar ativo dentro do monitor, é como se fosse uma classe, os processos “chamam” os procedimentos e após liberar o outro processo entra. Cada linguagem tem sua sintaxe para ter os monitores. Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 40 Figura 6. Funcionamento de um Monitor Fonte: http://professor.unisinos.br/barbosa/SO/so.htm 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 em espera. Em princípio existem três métodos para tratar deadlocks: Usar algum protocolo para garantir que o sistema nunca entre em estado de deadlock; Permitir que o sistema entre em estado de deadlock e depois se recupere; Ignorar o problema e fingir que os deadlocks nunca ocorrem no sistema. A terceira solução é usada pela maioria dos sistemas operacionais, incluindo o UNIX e a JVM. Uma situação de deadlock poderá ocorrer se e somente quatro condições necessárias forem válidas ao mesmo tempo no sistema: exclusão mútua, posse e espera, não-preempção e espera circular. Para prevenir deadlocks, é preciso garantir que ao menos uma das condições necessárias nunca seja válida. Outro método para evitar deadlocks, menos estrito do que os algoritmos de prevenção é ter informações, primeiramente, sobre como cada processo estará utilizando o recurso. Usando essas informações, é possível definir um algoritmo que poderá impedir os deadlocks. Se um sistema não utilizar protocolo para garantir que os deadlocks nunca ocorrerão, um esquema de detecção e recuperação deverá ser empregado. Um algoritmo de detecção de deadlocks deverá ser acionado para verificar se este ocorreu ou não, caso seja verificado e tenha ocorrido, o sistema deverá se recuperar terminado alguns dos processos em deadlock ou efetuando a preempção de recursos a partir de alguns dos processos em deadlock. Em um sistema que seleciona as vitimas para o rollback (exclusão, preempção) principalmente com base nos fatores de custo, poderá ocorrer uma paralisação. Como resultado, o processo selecionado nunca concluirá a tarefa que lhe foi designada. Segundo Silberschatz (2004, p.174) os pesquisadores vêm discutindo o fato de que Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 41 nenhuma dessas abordagens por si só é apropriada para todo o espectro de problemas de alocação de recursos nos sistemas operacionais. As abordagens básicas podem ser combinadas, permitindo a seleção separada de uma solução ótima para cada classe de recursos em um sistema. 8. Gerência de Memória O gerenciador de memória controla quais partes da memória estão sendo utilizadas e quais não estão. Além disso, ele é responsável por alocar espaço em memória aos processos que serão executados e liberar as posições de memória ocupadas quando os processos são finalizados. Outra funcionalidade do gerenciador de memória é controlar o swapping de informação, constante na execução das aplicações. Para iniciar a discussão sobre o tema da aula vamos entender, primeiramente, a função da MMU. A MMU é um módulo de hardware que faz o mapeamento entre os endereços lógicos (end. da memória virtual) e os endereços físicos da memória (RAM), ou seja, é um dispositivo que transforma endereços virtuais em endereços físicos. Para isso, a MMU normalmente traduz número de páginas virtuais para número de páginas físicas utilizando uma cache chamada Translation Lookside Buffer (TLB). Na figura abaixo temos ilustrado o mecanismo de tradução dos endereços. Podemos classificar os gerenciadores de memória em dois tipos: os que permitem as trocas de processos entre a memória principal e o disco (troca de processos e paginação, mais complexos) e os que não permitem (muito mais simplificados e limitados). A necessidade da troca de processos e paginação acontece devido a quantidade insuficiente de memória principal para armazenar vários programas ao mesmo tempo. Hoje em dia, as máquinas adotam um modelo denominado multiprogramação e, portanto, os algoritmos necessitam gerenciar várias aplicações que concorrem ao uso das unidades de processamento e armazenamento de dados. 9. Memória Virtual A memória virtual é uma técnica que permite o mapeamento de um espaço de endereçamento lógico grande em uma memória física menor. A memória virtual permite que processos extremamente grandes sejam executados, e que o grau de multiprogramação seja elevado, melhorando o desempenho da CPU. Além disso, ela libera os programadores de aplicações de preocupação de pensar na disponibilidade da memória, como mostra a figura abaixo, onde a memória virtual mapeia os endereços para dar sequência à execução das instruções selecionadas. Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 42 Figura 7. Funcionamento da paginação Fonte: http://professor.unisinos.br/barbosa/SO/so.htm Uma das estratégias de implementação dessa técnica é a paginação, como mostra a Figura 7, que nunca leva uma sequência de páginas para a memória até que esta seja referenciada. A primeira referência causa uma falta de páginas para o monitor residente do sistema operacional que consulta uma tabela interna de páginas para determinar onde a página está localizada no armazenamento auxiliar. A tabela de páginas é atualizada para refletir essa mudança, e a instrução que causou a falta de página é reiniciada. Essa abordagem permite que um processo execute mesmo que sua imagem de memória completa não esteja na memória principal de uma vez. Desde que a taxa de falta de página seja razoavelmente baixa, o desempenho será aceitável. A paginação pode ser usada para reduzir o número de quadros alocados a um processo. Esse arranjo pode aumentar o grau de multiprogramação (permitindo a execução de vários processos disponíveis para execução ao mesmo tempo) e, ao menos em teoria, aumenta a utilização da CPU do sistema. Permitindo assim que os processos sejam executados mesmo que suas exigências de memória superem a memória física total disponível, pois estes executam na memória virtual. Se os requisitos de memória total excederem a memória física, pode ser necessário substituir as páginas da memória para liberar quadros para novas páginas. Vários algoritmos de são utilizados com este intuito. A substituição de página FIFO é fácil de programar, já a substituição de página ótima requer conhecimento futuro. A substituição LRU é uma aproximação da substituição ótima, mas mesmo ela pode ser difícil de implementar. A maioria dos algoritmos de substituição de página, tais como o de segunda chance, são aproximações da substituição LRU. Além de um algoritmo de substituição de página, é necessária uma política de alocação de quadros. A alocação pode ser fixa, sugerindo a substituição de página local, ou dinâmica, sugerindo a substituição global. O modelo de conjunto de trabalho assume que os processos executam em localidades. O conjunto de trabalho é o conjunto de páginas na localidade atual. Da mesma forma, cada processo deve receber um número de quadros suficientes para seu conjunto de trabalho atual. Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 43 Se um processo não tiver memória suficiente para seu conjunto de trabalho, ele entrará em thrashing. Fornecer quadros suficientes para cada processo a fim de evitar o thrashing pode exigir swapping e o escalonamento de processos. Além de exigir a resolução dos principais problemas de substituição de página e alocação de quadros, o projeto adequado de um sistema de paginação requer que consideremos o tamanho de página, I/O, travamento, pré-paginação, estrutura dos programas e outros tópicos. A memória virtual pode ser considerada um nível de uma hierarquia de níveis de armazenamento em um sistema de computação. Cada nível tem seu próprio tempo de acesso, tamanho e parâmetros de custo. 10. Sistema de Arquivos Um arquivo é um tipo abstrato de dados definido e implementado pelo sistema operacional. É uma sequência de registros lógicos. Um registro lógico pode ser um byte, uma linha (de tamanho fixo ou variável) ou um item de dados mais complexo. O sistema operacional pode suportar especificamente vários tipos de registro ou pode deixar o suporte ao programa aplicativo. A principal tarefa do sistema operacional é mapear o conceito de arquivo lógico em dispositivos de armazenamento físico tais como fita ou disco magnético. Como o tamanho do registro físico do dispositivo talvez não seja igual ao tamanho do registro lógico, pode ser necessário encaixar registros lógicos em registros físicos. Mais uma vez, essa tarefa pode ser suportada pelo sistema operacional ou deixada para o programa aplicativo. Os sistemas de arquivos baseados em fita são limitados; a maioria dos sistemas de arquivos são baseados em disco. As fitas são comumente usadas para transporte de dados entre máquinas, ou para armazenamento de backup ou arquivamento. Cada dispositivo em um sistema de arquivos mantêm um índice de volume ou diretório de dispositivo listando a posição dos arquivos no dispositivo. Além disso, é útil criar diretórios para permitir a organização dos arquivos. Um diretório de nível único em um sistema multiusuário causa problemas de nomeação, já que cada arquivo deve ter um nome exclusivo. Um diretório de dois níveis resolve esse problema criando um diretório separado para cada usuário. Cada usuário tem seu próprio diretório, que contém seus próprios arquivos. O diretório lista os arquivos por nome, e inclui informações como a posição do arquivo no disco, seu tamanho, tipo, proprietário, hora da criação, hora da última utilização etc. A generalização natural de um diretório de dois níveis é um diretório estruturado em árvore. Um diretório em árvore permite que o usuário crie subdiretórios para organizar seus arquivos. Estruturas de diretórios de grafos acíclicos permitem o compartilhamento de arquivos e diretórios, mas complicam a pesquisa e exclusão. Uma estrutura de grafo genérico permite flexibilidade total no compartilhamento de arquivos e diretórios, mas às vezes requer o uso da coleta de lixo para recuperar espaço em disco não utilizado. Como os arquivos são o principal mecanismo de armazenamento de informações na maioria dos sistemas de computação, a proteção de arquivo é necessária. O acesso aos arquivos pode ser controlado de forma separada para cada tipo de acesso: ler, gravar, executar, anexar, listar diretório e assim por diante. A proteção de arquivo pode ser fornecida por senhas, listas de acesso, ou por técnicas Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 44 especiais ad hoc. O sistema de arquivos reside permanentemente no armazenamento secundário, que tem como exigência principal o fato de poder armazenar grandes quantidades de dados de forma permanente. O meio de armazenamento secundário mais comum é o disco. Os sistemas de arquivos são muitas vezes implementados em uma estrutura em camadas ou modular. Os níveis inferiores tratam das propriedades físicas dos dispositivos de armazenamento. Os níveis superiores lidam com nomes de arquivo simbólicos e as propriedades lógicas dos arquivos. Os níveis intermediários mapeiam os conceitos de arquivo lógico em propriedades de dispositivos físicos. Os vários arquivos podem ser alocados no disco de três formas: através de alocação contígua, encadeada ou indexada. A alocação contígua pode sofrer de fragmentação externa. O acesso direto é muito ineficiente com a alocação encadeada. A alocação indexada pode exigir custo substancial para seu bloco de índice. Existem muitas formas nas quais esses algoritmos podem ser otimizados. O espaço contíguo pode ser ampliado por meio de extensões para aumentar a flexibilidade e diminuir a fragmentação externa. A alocação indexada pode ser feita em clusters de múltiplos blocos para aumentar o throughput e reduzir o número de entradas de índice necessárias. A indexação em clusters grandes é semelhante à alocação contígua com extensões. Os métodos de alocação de espaço livre também influenciam a eficiência de uso do espaço em disco, o desempenho do sistema de arquivos, e a confiabilidade do armazenamento secundário. Os métodos usados incluem vetores de bits e listas encadeadas. As otimizações incluem agrupamento, contadores e a FAT, que coloca a lista encadeada em uma área contígua. As rotinas de gerência de diretórios devem considerar os aspectos de eficiência, desempenho e confiabilidade. Uma tabela de dispersão é o método mais frequentemente usado; é rápido e eficiente. Infelizmente, danos à tabela ou uma falha no sistema podem fazer com que as informações do diretório não correspondam ao conteúdo do disco. Um verificador de consistência - um programa de sistema como o chkdsk no MS-DOS - pode ser usado para reparar o dano. 7. Conclusão Os sistemas operacionais são softwares extremante complexos, que tem como missão manter em equilíbrio a execução de programas e o hardware se comunicando com o usuário, ou seja, interagindo com todos os componentes envolvidos no sistema de computação. Entende-los não é tarefa fácil devida tamanha complexidade e estrutura da qual gerenciam de forma a ter um ótimo custo-benefício para ambas às partes hardware e usuário. A facilidade de interação entre o usuário e máquina é tamanha que não nos damos conta da complexidade desse sistema, é ele quem administra e executa todos os pedidos do usuário. O sistema computacional tem a forma atual devido aos sistemas operacionais que são os responsáveis por fazer essa conexão entre quem utiliza o hardware e o próprio usuário. Todos os usuários, na atualidade usam e não se imaginam em um computador que não tenha um sistema operacional sendo executado. O sistema operacional se tornou Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 45 tão importante que para muitos somente ele transmite a ideia de ser o “próprio computador”. Esse software é de extrema importância, já que atua como mediador entre o hardware e os softwares que estão instalados no computador. Os sistemas operacionais estão sofrendo grandes atualizações e tem versões para vários estilos de usuários com a intenção de tentar se adaptar, fazendo com que o seu usuário ao interagir de forma a tornar-se imperceptível. Referências Silberschatz, A.; Galvin, P.B.; Gagne, G. Fundamentos de Sistemas Operacionais. 6ª ed. Rio de Janeiro: LTC, 2004. Revista iTEC – Vol. 4, Nº 4, Jul. 2012 Página 46