Prévia do material em texto
MINIX: Gerenciamento de Processos e Memória Introdução Este artigo tem por objetivo discorrer sobre gerenciamento de memória e de processos por Sistemas Operacionais, fazendo isto por meio de um estudo de caso do Sistema Operacional MINIX, desenvolvido por Andrew S. Tanenbaum, e descrito amplamente em [1]. De acordo com Tanenbaum[1], o Sistema Operacional pode ser definido como (1) uma extensão da arquitetura, e (2) um gerenciador de recursos. Segue uma breve análise dos dois pontos de vista: •SO como extensão da arquitetura: Tanenbaum afirma que programar a nível de máquina é muito difícil, especialmente ao realizar I/O. Programadores não gostariam de ter que escrever programas específicos para o hardware onde estão trabalhando, nem gostariam de ter que lidar com código extremamente maçante e complicado para realizar tarefas simples. A visão de SO como extensão da arquitetura enxerga o SO justamente como um facilitador do relacionamento entre o usuário do computador e o hardware. Por prover uma abstração de mais alto nível, o usuário de computador não precisa mais se preocupar, por exemplo, com o estado de rotação do motor do disco rígido, ou se a frequência usada para gravação deve ser modulada. Basta que enxergue o disco como uma coleção de arquivos nomeados, por exemplo. Detalhes da arquitetura física não devem ser apresentados ao usuário. Em resumo, nesta visão, a função do SO é apresentar ao usuário uma interface mais amigável e fácil de programar do que o hardware que está por baixo dele. •SO como gerenciador de recursos: A outra visão afirma que o computador é um sistema complexo de recursos compartilhados por centenas de programas (em sistemas multiprogramáveis, ao mesmo tempo). Seria o caos se os programas tivessem controle absoluto sobre os recursos, alocando-os e desalocando-os conforme desejam. O SO tem por função, segundo esta visão, gerenciar estes recursos para que cada aplicação receba uma fração justa dos recursos. Tanenbaum afirma que podem existir dois tipos de divisão de recurso (ou multiplexação, conforme nomeado em [1]): no espaço, e no tempo. Multiplexação no tempo implica em ordenar a execução dos programas de modo que se alternem para obter acesso ao mesmo recurso. Exemplos claros disso são o uso de CPU (assumindo só um núcleo), onde dois processos não podem rodar ao mesmo tempo, e impressoras (dois usuários não podem imprimir documentos diferentes simultaneamente). Multiplexação no espaço é dividir o recurso para que cada usuário tenha uma fração ao mesmo tempo. Um claro é o gerenciamento de memória, onde vários programas são armazenados simultaneamente, para concorrerem por tempo de execução na CPU. A visão de SO como gerenciador de recursos será a mais útil para este artigo, onde se discorrerá sobre a gerência de processos e de memória no MINIX. Serão abordados, dentre outros, temas como a modularização do Kernel do MINIX e em quais camadas ocorrem o gerenciamente e criação de processos, os algoritmos de escalonamento e como a informação se organiza na memória, e os algoritmos de escalonamento de processos na CPU. (2) Metodologia •Histórico Após o lançamento do UNIX(versão 7) licenciado com proibições de uso do código fonte pra estudos, Andrew S. Tanenbaum decidiu escrever um novo sistema operacional a partir do zero, mas que era compatível com o UNIX no modo usuário mas que internamente seria completamente diferente e sua licença aberta para estudos, esse sistema foi chamado de MINIX (mini-UNIX) e lançado em 1987. A segunda versão MINIX 2 foi lançada em 1997. Em 2004, após achar que o MINIX 2 estava aumentando suas linhas de código e ficando menos confiável, seu criador Tanenbaum resolveu reformular a estrutura do kernel reduzindo seu tamanho e colocando em primeiro lugar a modularidade e a confiabilidade. A diferença das versões 1 e 2 para a versão 3 é que as versões 1 e 2 foram projetadas para estudos, e a versão 3 é usada em computadores com recursos limitados e para aplicações que requeiram alta confiabilidade. Ao estudar o código do MINIX, Linus Torvalds achou que o Sistema Operacional necessitava de alguns melhoramentos. Torvalds criou em 1994, com a ajuda de outras pessoas, o kernel Linux 1.0, que evoluiu nas várias distribuições Linux que temos hoje. •Gerenciamento de Processos O MINIX 3 é uma coleção de processos que se comunicam entre si, e também com os processos de usuários através de primitivas de passagem de mensagem. Deste modo, torna o sistema mais modular e de estrutura mais flexível. O MINIX 3 é estruturado em quatro camadas, cada uma com funções específicas. As quatro camadas são ilustradas na figura 1. Figura 1 – Estrutura do MINIX O Kernel presente na última camada é o responsável pelo controle dos processos entre os estados executando, pronto e bloqueado. Ele também controla todas as trocas de mensagens entre processos. O Clock Task realiza o controle de sinais gerados pelo hardware. Esta camada 1 prove os privilégios para realizar chamadas de sistemas (system call) entre as camadas superiores. É a única com privilégios de modo supervisor. As chamadas de sistemas são implementadas pelo System Task. As três camadas superiores são , fundamentalmente, tratadas igualmente pelo Kernel. Todas limitadas a instruções modo usuário. A diferença está nos privilégios de cada camada. A camada 2 pode realizar algumas chamadas de sistema as quais as camadas 3 e 4 não podem. O mesmo pode ser dito para a camada 3. Enquanto a camada 4 não possui nenhum privilégio especial. Os processos no MINIX 3 seguem um modelo padrão de processo. Estes podem criar subprocessos, que por sua vez, podem criar outros subprocessos. Cria-se, assim, uma árvore de processos. De fato, todos os processos de usuários formam uma única árvore, contendo o processo Init como raiz. Os processos abaixo da camada 3 possuem um tratamento especial, visto que precisam ser iniciados antes de qualquer processo usuário. Durante a inicialização do MINIX 3, este procura hierarquicamente um dispositivo de boot, inicialmente disquete, CD-ROM e discos rígidos. Em discos rígidos, o primeiro setor conterá um pequeno programa e a tabela de partição. Este programa será carregado na memória, juntamente com a tabela, e será executado, encontrando a partição ativa. Esta partição contém o programa bootstrap no seu primeiro setor, que possui a finalidade de carregar o programa boot, que iniciará o sistema. Durante sua inicialização, o programa boot que irá realizar a comunicação com os dispositivos, visto que os processos encarregados para isso ainda não foram carregados. Os processos do sistema são carregados da camada mais inferior para as superiores. MINIX 3 possui processos especiais do sistema, como Clock e System. Tais processos são apenas visualizados pelo Kernel, e não possuem PID, nem pertencem a alguma árvore de processos. O gerente de processos é o primeiro processo executado no espaço usuário, recebendo PID 0, e não possui pai nem filhos. O processo raiz da árvore de processos é o reincarnation server que possui como filho os processos do sistema, e acompanha o funcionamento destes. Um deles é o processo Init, que recebe PID 1, assim como nos sistemas UNIX, ele é o antecessor de qualquer processo usuário. Para trabalhar com processos, o MINIX 3 oferece duas chamadas de sistemas principais, o (3) fork e o exec. O fork é o único modo de se criar um novo processo, e o exec permite carregar um programa num processo já criado. O tamanho de espaço a ser alocado para o processo é especificado no cabeçalho do programa. As informações dos processos são armazenados numa tabela de processos, dividida entre os processos, Kernel, Gerente de Processos e Sistema de Arquivos. A comunicação entre processos é realizada pro três primitivas, send, receive e sendrec. Cada um recebe dois parâmetros. O primeiro sendo o processo destino/origem, e o segundo o local de memória que contém a informação da mensagem. A transferência de mensagens, geralmente, ocorre de cima para baixo, nascamadas de processos. Processos usuários não podem enviar mensagens entre si diretamente, apenas tendo o sistema como intermediário. O sistema de troca de mensagens do MINIX 3 utiliza a técnica rendezvous. Deste modo, quando um processo A envia uma mensagem para outro processo B, A ficará bloqueado até que o processo B receba a mensagem. Assim, não é preciso buffers de caixa postal para controle de mensagens, e visto que todas as mensagens possuem tamanho fixo, erros de estouro de buffers são prevenidos. Uma outra primitiva oferecia pelo sistema é o notify. Esta chamada é utilizada para avisar outro processo de algo importante sem ser bloqueado. Deste modo, notify é assíncrono. Entretanto, um simples mapa de bits é suficiente para controlar os notify's dos processos. Em alguns casos, o notify já é uma mensagem suficiente para a comunicação, quando não há ambiguidade na mensagem. Para se criar um sistema operacional multi-programável, é necessário um controle de interrupções. A camada mais inferior do MINIX 3 é a responsável em aumentar o nível de abstração das interrupções para mensagens. Por exemplo, as chamadas send e receive, acima mencionadas são traps, ou seja, interrupções geradas pro software. O escalonador do MINIX 3 trabalha com sistema de filas em multi-nível. Dezesseis filas com prioridades são definidas pelo sistema. A camada de mais baixa prioridade é utilizada para processos de segundo plano, apenas executados quando não há nada a fazer. Processos usuários possuem uma prioridade intermediária. Os processos de camadas mais inferiores possuem maiores prioridades. Os processos Servers possuem prioridade acima do permitido para processos usuários. Processos Drivers possuem prioridade acima dos Servers e os processos do Kernel ocupam a fila de maior prioridade. As filas não são estáticas, os processos podem se mover entre as filas utilizando, por exemplo, o comando nice, igual ao sistema UNIX. Além de filas multi-nível, também é atribuído diferentes quantum para cada processos. Processos usuários possuem um valor de quantum baixo. Processos Drivers e Servers deveriam executar até serem bloqueados, ou seja, processos de tempo real. Mas, por segurança, são processos preemptiveis, entretanto possuem um alto valor de quantum. O processo Clock é o responsável pelo controle do tempo de execução dos processos. Para garantir que processos de mais baixa prioridade também possam ser executados, o MINIX 3 utiliza uma técnica semelhante ao de envelhecimento. Se um processo A que utilizou todo o seu quantum for reescalonado para a CPU, e isto estiver impedindo outros processos de executar, então A terá sua prioridade reduzida. Em algum momento, todos os processos terão sua chance de utilizar a CPU. Caso A utilize todo o seu quantum sem impedir outros processos de executar, então A terá sua prioridade promovida para o seu máximo permitido para o processo A. Dentro das filas o algoritmo de alternância obrigatória é utilizado, com uma pequena alteração. Se um processo, durante sua execução for bloqueado, quando este se tornar pronto novamente, será colocado no início da fila e irá utilizar o restante de seu quantum. Agora, se um processo utiliza todo o seu quantum, então será colocado no final da fila. O escalonador escolhe um processo da fila de mais alta prioridade para executar, se não tiver nenhum processo pronto na fila, então a fila de prioridade subsequente será analisada. Pela dependência dos processos Drivers com os Servers e deste último com os processos usuários, os processos Drivers e Servers, normalmente, ficam em estado bloqueado, permitindo a execução dos processos usuários. •Gerenciamento de memória O gerenciamento de memória no MINIX 3 é extremamente simples. Diferente de outros SO's, não são utilizados paginação nem swapping, sendo que o gerenciador de memória mantém uma lista de espaços que são classificados por ordem de endereçamento de memória. Esse tipo de gerenciamento foi implementado por três motivos principais: 1º) Manter o MINIX como ferramenta de estudo, de fácil compreensão; 2º) MINIX funcionando em todos os IBM PC: alguns sistemas simples dessa família(8088) na arquitetura de gerenciamento de memória não suportam memória virtual nem detecta estouro de pilha. Usando isso ele consegue ampliar a área de atuação do sistema operacional cobrindo essa categoria primitiva que ainda são aproveitadas e estão em uso. (4) 3º) Portabilidade: implementação simples e direta em computadores pequenos, ou seja, quanto menor for a gama de suposições de hardware maior será o numero de maquinas para poder portá-lo. Além desses motivos há outros, por exemplo o fato do MINIX ser destinado a computadores pessoais, que implica que a média de processos em execução será pequena, havendo assim memória disponível para armazenar todos os processos correntes. A implementação do gerenciamento de memória é diferente, pois separa a política(quais processos e onde irão ser alocados) do mecanismo(mapa de memoria para os processos), esse gerenciamento não faz parte do kernel, ele é manipulado pelo processo gerenciador de memória executado no espaço do usuário e faz comunicação com kernel pelo mecanismo padrão de mensagens. O layout de memória utiliza dois modelos, espaços de instruções e dados (I&D) combinados, onde todas as partes do processo compartilham o mesmo espaço de memória, ou seja, todas as partes do processo(texto, dados e pilha) são alocados e liberado como um bloco unico, ou separados, mantém separados os dados das instruções, pode ser mais eficiente mas requer um melhor gerenciamento. A alocação de memória é feita em duas ocasiões, executando um FORK (processo cria um filho) ou EXEC(processo altera sua imagem, ficando livre e alocando a nova imagem). O sistema abaixo utiliza para I&D combinado: Figura 2 – Alocação de memória. (a)Original com processos A e B (b) Após A ter executado FORK() (c) Após o filho de A ter executado um EXEC Agora quando o MINIX é implementado em um computador com gerenciamento melhor de memória é mais conveniente utilizar o I&D separado onde há o aproveitamento do modo expandido de gerenciamento chamado texto compartilhado, um exemplo seria se um processo executando um fork( ), somente a quantidade de memória necessária para a copiar dos dados e da pilha é alocado e codigo executável, já em uso pelo pai, é compartilhado pelo filho, ou quando executado um exec( ) onde é feita uma pesquisa na tabela de processos para verificar se outro processo já esta utilizando o código executável necessário, se caso encontre ele aloca na memória somente os dados e a pilha compartilhando a área de texto. A desvantagem desse modo é que após terminar o processo ele desaloca os dados e a pilha da memória mas antes de desalocar o segmento de texto ele percorre uma tabela de processos verificando se não há nenhum processo utilizando o segmento de texto. Figura 3 – (a)Programa armazenado em disco (b) Programa alocado na memória para um único processo No segmento cabeçalho tem-se informações sobre o tamanho que cada segmento ira ocupar na memória e o tamanho total que o processo ocupara no disco, esse espaço total pode ser alterado pelo comando chmem, exemplo se um processo tem 4K de texto, 2K de dados, 1K de pilha e o cabeçalho indica q o espaço total é 40K então a lacuna terá 33K. Essa lacuna fica disponível para que o segmento de dados e pilha consumam durante a execução, esse limite pode ser movido com a chamada de sistema BRK que apenas verifica se o novo segmento de dados entra em colisão com o ponteiro atual da pilha, e se não ele altera alguns dados nas tabelas internas, caso colidam gera uma falha, mas em hipótese alguma há alocação de novo espaço de memória. Conclusões Ao término do projeto, após analisar o MINIX e do estudo teórico de Sistemas Operacionais, foi possível encontrar diversas semelhanças entre outros SO’s modernos e o MINIX. Os SO’s, contudo, não são idênticos. Primando pela modularização e simplicidade de código, verificamos queo MINIX, apesar de simples, cumpre seu propósito de ser um SO didático, leve e portável. Seu modelo para memória, que não usa paginação, apesar de poder ser visto como muito simplificado, ajuda a entender o código, e não impede que o MINIX seja usado nas circunstâncias apropriadas. (5) As semelhanças são muitas também com o UNIX, apesar de seu código ter sido desenvolvido de forma independente. O modelo de criação e gerência de processos segue basicamente o mesmo, com a criação de um processo init, e os demais processos de usuário sendo criados como filhos deste. Dada sua simplicidade, o MINIX pode ser visto como a base de um SO. Ele possui módulos bem construídos, e um usuário de MINIX poderia criar suas próprias funcionalidades para suas necessidades (como foi o caso de Linus Torvalds, ao criar o Linux). Sendo de código totalmente aberto, seus algoritmos de escalonamento, paginação e diversos outros podem ser modificados de acordo com o uso, de modo que um usuário poderia aumentar seu desempenho em tarefas muito específicas. Também foi interessante estudar o papel do MINIX no nascimento dos softwares de código aberto, sendo um SO feito a partir de outro (que não era mais liberado para estudos acadêmicos) e conseguindo se sobrepor às limitações de licença de código e ganhando espaço nas aulas de Sistemas Operacionais. Por fim, concluímos que o MINIX é um sistema operacional que cumpre seu propósito como software desenvolvido para fins acadêmicos, simples, confiável, portável e modular. Para outras aplicações que não o ensino, o usuário talvez prefira algum SO mais robusto, ou ainda decida implementar soluções mais interessantes para problemas que o MINIX, devido ao seu compromisso com o minimalismo, prefere não tratar. Referências [1] - TANENBAUM, Andrew S; WOODHULL, Albert S. Operating systems: design and implementation. 3 ed. Upper Saddle River, N.J: Pearson/Prentice Hall, c2006. xvii