ESTUDO DA ESTRUTURA DO SISTEMA OPERACIONAL LINUX Bruno Peres¹, Karina Damasceno², Mariana da Costa Santos³ e Victor Amaral4 ¹ Bruno Peres – [email protected] ² Karina Damasceno – [email protected] ³ Mariana da Costa Santos – [email protected] 4 Victor Amaral – [email protected] Universidade Federal Fluminense – UFF Escola de engenharia - Departamento de Telecomunicações (TET) Rua Passos da Pátria, 156 – Bloco D – 5º andar – sala 504 São Domingos – Niterói - RJ RESUMO: O trabalho a seguir tem como objeto o estudo do Sistema Operacional Linux, dessa forma o grupo pretende abranger de maneira clara e objetiva os principais tópicos e recursos utilizados pelo Sistema Operacional assim como os principais procedimentos de inicialização, operação e segurança. PALAVRAS-CHAVE: Linux, sistema operacional, estudo 1 INTRODUÇÃO Desde o inicio dos tempos das relações humanas a humanidade busca por algum meio de fazer mais atividade em um menor espaço de tempo. A busca por recursos que tornassem isso possível sempre foi a motivação para a evolução intelectual do planeta. Podemos citar o século XVIII como sendo um século que possivelmente representou o maior avanço, pois trouxe a idéia de colocar máquinas para executar tarefas antes feitas por homens, o que gerou uma verdadeira reformulação no processo de produção. Com o passar do tempo a necessidade de resolução de problemas aumentou cada vez mais e a aplicação das máquinas a essas resoluções passou a ser utilizada em maior escala. As primeiras máquinas de calcular são consideradas computadores cujo objetivo é a execução de cálculos matemáticos. O primeiro computador aplicando um sistema operacional destinado à comercialização foi o UNIVAC e a partir dele o desenvolvimento tanto do hardware como do software aplicado aos computadores só cresceu. O sistema operacional Linux, foco do trabalho, começou a ser desenvolvido em 1991 e, nos dias atuais, é um dos sistemas mais conhecidos, além de possuir diversas e diferentes distribuições. 2 MOTIVAÇÃO Com o objetivo de agregar valores a nossa formação acadêmica e enriquecer o conhecimento sobre sistemas operacionais, a professora Natalia Castro Fernandes passou um trabalho de pesquisa para a turma da disciplina de Sistemas de Computação para Telecomunicações – TET00197. O grupo, dos sistemas operacionais disponíveis, optou pela escolha do Sistema Linux, pois avaliou que este sistema é largamente utilizado nas empresas não só de Telecomunicações, mas também em grande parte das empresas de outros ramos. Por isso, acreditamos que o estudo desse sistema possa ser um diferencial não só para nossa formação, mas também para qualquer aluno que possa vir a ler este trabalho. 3 OBJETIVO Apresentar de forma clara os principais recursos do sistema operacional Linux assim como fazer a correlação dos termos e conhecimentos adquiridos em sala de aula. 4 RELAÇÃO ENTRE LINUX E UNIX É muito comum no meio da computação a interligação entre os Sistemas Linux com os Sistemas Unix. O sistema Linux foi criado como uma nova elaboração e implementação do sistema UNIX. O Linux segue o padrão POSIX e pode ser executado em diversas plataformas de hardware, além de ser compatível com a maioria dos softwares UNIX disponíveis. De todas as diversas variantes do UNIX, o Linux se difere pelo fato de ser gratuito e possuir o seu código-fonte aberto, isso possibilita um desenvolvimento de maneira cooperativa, ou seja, vários indivíduos e corporações podem colaborar em seu desenvolvimento. Há autores que afirmam que o Linux não pode ser denominado como um “UNIX” pelo fato de incorporar alguns refinamentos técnicos que não existem em versões originais UNIX, dessa forma caracterizando-se por uma entidade juridicamente distinta. Como exemplos de outros sistemas operacionais baseados no sistema UNIX temos: OpenBSD, FreeBSD e o NetBSD. Vale ressaltar que todos os três citados são ramificações do Berkey Software Distribution da UC Berkley. Esses sistemas podem-se comparar ao Linux em termos de recursos e confiabilidade, porém possuem menos suporte por parte de fornecedores de software independentes. Devido ao Projeto GNU, a maioria dos principais programas e aplicativos, que dão aos sistemas UNIX seu valor, foi desenvolvida em alguma forma de modelo de código-fonte aberto. Dessa forma, sob o aspecto dos aplicativos, o sistema Linux é uma das variantes do UNIX mais bem compatíveis. Um exemplo típico é o servidor Web Apache, que quando está em operação, não faz muita distinção entre o sistema Linux ou sobre qualquer uma das variantes do sistema UNIX. 4.1 HISTÓRICO DO LINUX Pelo fato do sistema Linux estar diretamente relacionado ao UNIX, impossível não levar em consideração primeiramente a história deste para só assim chegarmos ao Linux. Em 1969, o UNIX se iniciou como um projeto de pesquisa no AT&T Bell Labs, porém foi em 1976 que o UNIX se tornou gratuitamente disponível nas universidades e, dessa forma, tornando-se a base de vários cursos de sistemas operacionais e de projetos científicos e de pesquisa acadêmicos. O Berkley UNIX iniciou-se em 1977 quando o Computer Systems Research Group (CSRG) na Universidade de California licenciou o código da AT&T. As versões do Berkeley começaram com o 1BSD para o PDP-11 e resultaram no 4.4BSD, em 1993. À medida que o UNIX ganhava popularidade e aceitação comercial, o preço de suas licenças crescia mais rapidamente. A Berkley estabeleceu como objetivo à longo prazo, eliminar do BDS o código pertencente à AT&T, porém antes que o trabalho pudesse ser completado, Berkeley perdeu as verbas para suas pesquisas e o CSRG foi abandonado. A 4.4BSD-Lite foi a versão final lançada pelo CSRG, antes de sua dissociação. Esta versão saiu sem o código da AT&T. A maioria das versões do BSD UNIX, disponíveis no mercado atualmente afirmam ter, como seu antepassado, o pacote 4.4BSD-Lite. O Linux propriamente dito acabou se originando em 1991, como um projeto pessoal de um universitário finlandês chamado Linus Torvalds. Ele concebeu originalmente seu projeto como uma pequena ramificação de um sistema operacional escrito por Andrew S. Tannenbaum, o Minix. Porém, o Linux acabou gerando um interesse substancial no mundo todo. Linus foi capaz de empreender uma tarefa muito mais ambiciosa explorando o poder do desenvolvimento cooperativo. Segundo as referências [1] e [2], a versão 1.0 do kernel foi lançado em 1994, o bloco de versões mais estáveis do Linux atualmente é o 2.6, variando seu uso entre 2.6.17 até 2.6.35. O Linux difere das outras versões do sistema UNIX pelo fato de que o projeto de seu kernel básico define apenas um kernel de Sistema Operacional. Dessa forma, este deve ser empacotado com comandos, daemons e outros softwares para formar um sistema operacional devidamente completo e utilizável. Tal forma de fornecer o sistema Linux é caracterizado por “Distribuição”. Todas as distribuições do Linux compartilham da mesma linhagem do kernel, porém vale ressaltar que o que pode variar nelas são os materiais auxiliares que vêm junto com esse kernel. Sendo assim, as distribuições do Linux variam em seu objetivo, suporte popularidade. Abaixo, na tabela 1 explicitamos cinco das principais distribuições de uso geral. Tabela 1: Distribuições do Sistema Linux. 4.2 SISTEMA LINUX O sistema Linux é mantido por uma rede de desenvolvedores livres que colaboram através da Internet, porém essa “comunidade Linux” deve manter o documento Padrão da Hierarquia do Sistema de Arquivos (File System Hierarchy Satandard). Onde é especificado um formato geral de arquivos-padrão (como nomes de diretórios, bibliotecas, os binários de sistema e os arquivos de dados de tempo de execução). 4.2.1 COMPONENTES DO SISTEMA LINUX O sistema Linux é composto por três partes principais: Kernel: É o responsável por dar suporte a diferentes periféricos. Bibliotecas de sistema: Definem um conjunto-padrão de funções através das quais as aplicações podem interagir com o kernel. Utilitários do sistema: São programas que executam tarefas de gerenciamento individuais e especializadas. Figura 1: Diagrama do sistema do Linux Na modalidade de kernel, o código de kernel executa em modalidade privilegiada do processador, com acesso pleno a todos os recursos físicos do computador. Os códigos em modalidade de usuário não são inseridos no kernel. Os códigos de suporte que não precisam executar na modalidade de kernel são colocados nas bibliotecas do sistema. O Linux manteve o modelo do Unix em que o kernel é criado como um binário monolítico, ou seja, executa em um único espaço de endereçamento inteiramente no modo kernel. Também possui um microkernel: possui um projeto modular com kernel preemptivo, suporte a threads de kernel e a capacidade de carregar dinamicamente as bibliotecas separadas do kernel. 4.2.2 MÓDULOS DO KERNEL Como o Linux tem o código fonte livre, ao escrever um código no kernel é necessário executar uma reinicialização para carregar uma nova funcionalidade. Esse processo pode ser incomodo e trabalhoso. Como solução podemos utilizar os módulos do kernel, pois o driver pode ser compilado sozinho e carregado no kernel já em execução. Assim pode ser distribuído como um módulo e os que se interessarem por ele não terão que reconstruir o kernel. Um kernel padrão mínimo pode ser configurado por causa dos módulos de kernel sem que drivers de dispositivos adicionais sejam embutidos. Existem três componentes de suporte de Linux: Gerenciamento de módulo: permite que os módulos sejam carregados na memória e conversem com o restante do kernel O registro de drivers: permite que os módulos informem ao restante do kernel que um novo driver está disponível. Um mecanismo de resolução de conflitos: proteção de recursos do driver por uso acidental de outros drives reservando recursos de hardware. 4.2.3 LICENCIAMENTO DO LINUX Há uma idéia equivocada quanto ao licenciamento do Linux, pois ele não é um software de domínio público. Domínio público significa que os autores abriram mão dos seus direitos autorais, mas os direitos autorais sobre o código do Linux são mantidos por vários autores do código. O kernel do Linux é um software aberto, o que proporciona às pessoas a possibilidade de modificação, utilização e distribuição de suas cópias, sem quaisquer restrições. O Linux é licenciado sob a GNU General Public License (GPL). No entanto, ele possui algumas intervenções que implicam que nenhum criador pode tornar-se proprietário do produto customizado. Se o desenvolvedor criar um software qualquer com algum componente protegido pela GPL, então o código-fonte deve ser disponível. Ou seja, você pode alterar qualquer parte do Linux, modificá-lo e até comercializá-lo, mas você não pode fechá-lo (não permitir que outros usuários o modifiquem) e vendê-lo. 4.3 INSTALAÇÃO DO LINUX Todas as distribuições do Linux possuem facilidades na instalação, pois são bem diretas e simples. Contamos também com uma série de possibilidades de instalação. O Linux pode ser instalado pela rede, total ou parcialmente, através de um Live CD/DVD, um Live USB ou instalando no HD. Nas formas ditas “Live”, podemos usá-lo sem mexer nos dados do computador, bastando apenas que o dispositivo de boot seja um CD/DVD ou um pendrive (USB). Pode ainda ser instalado sozinho (sistema operacional único), junto com o Windows, mas em partições separadas do HD (o que se costuma chamar de "dual boot"), dentro do Windows (funciona como um programa do Windows) ou junto com outro Linux (distribuições diferentes), também em partições separadas do HD. 4.3.1 INSTALANDO PROGRAMAS NO LINUX Uma das grandes dificuldades que usuários Linux possuem é a instalação de novos programas em seu sistema. Inicialmente, é altamente recomendável a leitura do arquivo de texto Readme ou Install após a descompactação do arquivo (comando TAR), pois ele pode ter observações especiais ou comando diferentes para a instalação, mas de um modo geral funciona da seguinte forma: 1°) Para iniciar a instalação é necessário descompactar o arquivo utilizando os seguintes comandos de acordo com o formato do arquivo: Arquivos tar.bz2: tar -jxvf nomedoprograma.tar.bz2 Arquivos tar.gz: tar -zxvf nomedoprograma.tar.gz Arquivos tar: tar -zxvf nomedoprograma.tar.gz Arquivos tar.Z: tar -Zxvf nomedoprograma.tar.Z Arquivos zip: unzip nomedoprograma.zip Arquivos tgz: tar -zxvf nomedoprograma.tgz 2°) Utilizando o comando “su”, que serve para logar como SuperUsuário, mas é necessário colocar a senha cadastrada durante a instalação do sistema. 3°) Utiliza-se : pasta_criada_pelo_tar. 4°) Leitura do arquivo Readme ou Install para verificar se o processo deve ser diferente. 5°) Utiliza-se: ./configure (O configure é de extrema importância e pode ser utilizado com diversas opções como: onde você quer instalar o programa, onde estão suas bibliotecas, se você quer ativar determinados recursos, etc.). make (O make faz a compilação do código utilizando o Makefile criado pelo configure como referência e, finalmente, o make install faz a instalação do programa.) Caso aconteça algum problema por falta de dependências, devemos instalar todas as dependência requeridas, antes de instalarmos o programa. 4.3.2 DESINSTALANDO PROGRAMAS NO LINUX Alguns programas são acompanhados da opção make uninstall, mas para utilizálo é necessário que você tenha guardado o código-fonte. Para não ficar dependendo dessas coisas, o melhor é fazer um pacote de instalação, assim é possível instalar e desinstalar quantas vezes quiser e ainda distribuir para outros usuários que possuam o mesmo sistema. Existem diversas opções para isso, como rpmrebuild, Debian-builder e o CheckInstall. Todos são bastante simples de usar. Para utilizar o CheckInstall por exemplo, ao invés de dar o comando make install no final, é só usar checkinstall e responder algumas perguntas. Ele pode criar pacotes para o Slackware, Debian ou Fedora. 5 INICIALIZAÇÃO DO LINUX A realização dos procedimentos necessários para a inicialização do sistema operacional é feita pelo próprio computador para que só assim os recursos normais do sistema operacionais estejam disponíveis. O kernel, durante essa inicialização, é carregado na memória para que, dessa forma, possa começar a ser executado. No mesmo momento, é executado também um conjunto de tarefas de inicialização e, assim, o sistema passa estar disponível aos usuários. É comum que sistemas de arquivos danificados, equipamentos faltantes ou não confiáveis e erros em arquivos de configuração impeçam que o computador inicie seu processo de funcionamento. Dessa forma, podemos concluir que o momento de inicialização do sistema é um período de certa vulnerabilidade, logo é possível perceber que a configuração de inicialização é a primeira das tarefas que o administrador de sistema tem que levar em consideração. 5.1 INICIALIZAÇÃO AUTOMÁTICA E MANUAL Existem dois modos de se inicializar o sistema Linux, o modo manual e o modo automático, porém o mais utilizado é o modo automático. Nele, o sistema executa, sem qualquer ajuda externa, o procedimento de inicialização por completo. Como exemplo prático, temos a maioria dos nossos computadores pessoais que, quando inicializados, executam todo o procedimento até que o usuário possa utilizá-lo. No modo de inicialização manual, o sistema segue o procedimento automático até um determinado ponto antes da maioria dos scripts de inicialização ter sido executada, só aí é passado o controle a um operador. Quando isso ocorre, o sistema é caracterizado operando em “modo monousuário”, como é citado na referência [1]. De forma resumida, podemos descrever o processo de inicialização do Linux em etapas, sendo elas: carregamento e inicialização do kernel, detecção e configuração de dispositivos, criação de threads do kernel para processos de sistema espontâneos, intervenção do operador, execução dos scripts de inicialização do sistema e por fim, a operação multiusuário. Vale ressaltar que essas etapas serão descritas de forma geral. 5.2 A INICIALIZAÇÃO DO KERNEL Segundo o livro de referência [1], “o kernel do Linux por si só já é um programa, e a primeira tarefa de inicialização é carregar esse programa na memória de forma que ele possa ser executado.” O processo de carregamento no Linux ocorre em duas etapas distintas. Na primeira etapa, é preparado o ambiente para que o kernel seja carregado. Isso é feito pelo carregamento de um pequeno programa de inicialização a partir do disco. Após isto, o kernel passa a fazer testes na memória para determinar o quanto de memória RAM está disponível. Os testes de memória servem para que o kernel separe uma quantidade fixa de memória real para seu uso próprio, não estando disponível para os processos a nível de usuário. Ao final dessa etapa, é mostrada na tela a quantidade de memória física e a quantidade de memória disponível. 5.3 CONFIGURAÇÃO DO HARDWARE Após o preparo do ambiente para o carregamento do kernel, é feita uma verificação do ambiente da máquina para saber os dispositivos físicos (hardware) que estão presentes. Quando o kernel começa sua execução, ele tenta localizar e inicializar cada dispositivo necessário para seu funcionamento. Na maioria dos casos, essas informações não são especificadas completamente durante o tempo de configuração do kernel sendo assim, o próprio kernel as busca sondando o barramento a procura dos dispositivos consultando os drives apropriados. Os drives que não estão relacionados a algum dispositivo ou então não responderem a essa busca serão desativados. Vale ressaltar que é comum e muito possível que o usuário ou administrador queira conectar algum dispositivo após o sistema estar carregado. Caso isso ocorra, é possível carregar um driver para este novo dispositivo. 5.4 THREADS DO KERNEL Após o termino da inicialização básica do sistema, o kernel começa a criar vários processos filhos, porém a nível de usuário. Esses processos filhos são considerados espontâneos por não serem criados por mecanismo normais de criação de processos, ou seja, utilizando o comando fork() convencional. Vale ressaltar que a quantidade e finalidade dos processos criados irão variar de acordo com o sistema a ser utilizado. De todos os processos criados nessa etapa, somente o init é considerado um processo completo. Os demais são partes do kernel arrumados para parecerem processos devido a escalonamento e arquitetura adotada. Assim que os processos filhos, anteriormente citados, forem criados, o papel do kernel na inicialização estará terminado e a maioria das tarefas de execução de funções básicas após essa etapa, ficam a cargo do processo init. 5.5 INTERVENÇÃO DO OPERADOR A intervenção do operador, como dito anteriormente, só ocorre para inicialização manual e, como o livro de referência [1] diz, “se o sistema for executado no modo monousuário, um flag de linha de comando passado para o kernel notifica o init desse fato quando ele inicializa. O processo init passa, então, o controle para sulogin, uma versão de login que solicita a senha da conta root.”. Depois de se fazer isto, o sistema gera um shell de root. A partir do Shell monousuário, é possível a execução de comandos de forma muito similar quando se está conectado num sistema completamente inicializado. É importante ressaltar que, nos ambientes de monousuário que montam o diretório-raiz como sendo de leitura, os comandos que utilizam arquivos que estão dentro das pastas inseridas no diretório-raiz não irão funcionar. A solução para isso está na tarefa de iniciar a sessão monousuário remontando o diretório-raiz no modo leitura/gravação. Nos sistemas Red Hat e Fedora, o modo monousuário é um pouco mais complicado de ser acessado comparado aos outros sistemas. Quando se conseguir acessar o prompt de Shell dessas duas distribuições, os sistemas de arquivos locais já terão sido montados. De certa forma, isso pode até ser um ponto positivo, porém representa um problema quando se tem um sistema de arquivos falho. 5.6 EXECUÇÃO DE SCRIPTS DE INICIALIZAÇÃO O processo init é o primeiro processo a ser inicializado em sistemas Linux, logo após o carregamento do kernel na memória. Além de ser responsável por dar continuidade ao carregamento do sistema, ele define sete níveis de execução. Cada nível carrega consigo um determinado complemento de serviços que o sistema deve executar. Os níveis juntamente com seus complementos são mostrados na tabela 2. Tabela 2: Níveis de execução e sua representação Os scripts de inicialização são scripts comuns e são selecionados e executados pelo processo init de acordo com um algoritmo, relativamente compreensível e exclusivo para executar essa função. 5.7 OPERAÇÃO MULTIUSUÁRIO Após a execução dos scripts de inicialização, o sistema está, teoricamente, pronto para receber o login de algum usuário. O processo getty é o responsável por “habilitar” o login de um usuário. Eles são gerados diretamente pelo processo init completando assim o processo de inicialização do sistema. Além disso, se o sistema estiver configurado para isso, o init gera sistemas de login gráficos. Temos como exemplo o xdm e o gdm. Após o término do processo de inicialização, o processo init continua a atuar na execução de outros processos. Na maioria dos sistemas, ele possui somente um modo monousuário e vários níveis de multiusuário, porém, em sistemas baseados em BSD, o init possui dois estados, um a nível de monousuário e outro a nível de multiusuário. 6 SISTEMA DE ARQUIVOS No sistema de arquivos do Linux, que mantém o modelo do sistema de arquivos padrão do UNIX, podemos encontrar: processos, portas seriais, estruturas de dados do kernel e parâmetros de configuração e canais de comunicação entre processos. Um benefício que o Linux tem é o fato de suportar mais do que um sistema de arquivos baseado em disco. Entre um dos melhores e mais modernos, é o sistema de arquivos ext4fs (extended file system) que serve como padrão para a maioria das distribuições. O ext2fs, mais antigo, precursor do ext3fs, ainda é suportado por todas as distribuições. Características do ext2fs: 1. Nomes de arquivos não podem exceder 255 caracteres. 2. O tamanho máximo de um único arquivo varia entre 16GB e 2TB (dependendo do tamanho dos blocos). 3. O tamanho máximo de uma partição pode ser entre 2 e 32TB. O ext3fs é baseado em journaling, o que permite que o aumento de confiabilidade do sistema de arquivos e permite que o sistema se recupere mais rapidamente após um desligamento incorreto. No ext2fs, após um desligamento incorreto, era necessário um utilitário que verificava todos os blocos do sistema de arquivos procurando por inconsistências. Oferece 3 níveis de journaling (journal, writeback e ordered). O modo default é o modo ordered, onde o journal é atualizado no final de cada operação. As desvantagens do ext3fs são as seguintes: 1. Limite de 31.998 subdiretórios por diretórios; 2. Não há suporte para recuperação de arquivos deletados; 3. Ausência do checksum no journaling. O sucessor do ext3fs é o ext4fs, adotado como padrão na versão 2.6.28 do kernel do Linux. Algumas características do ext4fs: 1. Suporta até 16TB por arquivo; 2. O ext4fs é totalmente compatível com as versões anteriores, ext3fs e ext2fs; 3. Permite a pré-alocação de espaço no disco para um arquivo; 4. Limite de 64.000 subdiretórios que um diretório pode conter; 5. Journal checksumming; Também há muitas implementações de sistemas de arquivos de diferentes extensões, como os sistemas de arquivos NTFS e FAT, utilizado pelo Microsoft Windows. O Linux suporta mais tipos de sistemas de arquivos do que qualquer outra variante do UNIX, nos dando mais flexibilidade e tornando mais fácil o compartilhamento de arquivos entre sistemas. 6.1 NOMES DE CAMINHOS O sistema de arquivos do Linux é apresentado com uma única hierarquia unificada, que inicia no diretório “/” e continua descendo por um número arbitrário de subdiretórios. O diretório / também é chamado de diretório raiz. O sistema de arquivos pode ser arbitrariamente profundo. Entretanto, cada componente de um nome de caminho tem de ter um nome que não supere 255 caracteres, e um caminho único não pode conter mais do que 4.095 caracteres. Para acessar um arquivo com um nome de caminho superior a isso, você tem de mudar de diretório com o comando cd para um diretório intermediário e utilizar um nome de caminho relativo. A nomeação de arquivos e diretórios é essencialmente irrestrita, exceto que os nomes têm comprimento limitado e não devem conter o caractere de barra ou nulos. Em particular, os espaços são permitidos. Infelizmente, o UNIX tem uma longa tradição de separar argumentos de linha de comando com espaços em branco, de modo que softwares mais antigos tendem a parar quando encontram espaços dentro de nomes de arquivos. É possível “escapar” espaços individuais com uma barra invertida. O recurso de complemento de nome de arquivo de shells comuns (normalmente a tecla TAB) faz isso para você. 6.2 ORGANIZAÇÃO DA ÁRVORE DE ARQUIVOS Os sistemas de arquivos na família UNIX nunca foram muito organizados. Várias convenções de nomes incompatíveis são utilizadas ao mesmo tempo e diferentes tipos de arquivos estão espalhados aleatoriamente pelo espaço de nomes. Em muitos casos, os arquivos são divididos pela função e não pela probabilidade com que eles mudam, dificultando a atualização do sistema operacional. Há um local culturalmente correto para tudo. É particularmente importante não bagunçar a estrutura padrão da árvore de arquivos sob o Linux, pois os pacotes de software e suas ferramentas de instalação normalmente fazem suposições abrangentes em relação à localização de arquivos. O sistema de arquivos-raiz inclui o diretório-raiz e um conjunto mínimo de arquivos e subdiretórios. O arquivo que contém o kernel reside dentro do sistema de arquivos-raiz no diretório /boot, seu nome normalmente inicia com vmlinuz. Também fazem parte do sistema de arquivos-raiz os /dev para arquivos de dispositivos, /etc para arquivos de sistema críticos, /sbin e /bin para utilitários importantes e, às vezes, /tmp para arquivos temporários. Os diretórios /usr e /var são também de grande importância. /usr é onde a maioria de programas padrão é mantida, junto com vários outros materiais sobre inicialização, como manuais on-line e a maioria das bibliotecas. Não é estritamente necessário que /usr seja um sistema de arquivos separado, mas, para conveniência administrativa, geralmente é. Tanto /usr quanto /var tem de estar disponíveis para ativar o sistema a trilhar todo o caminho até o modo multiusuário. Alguns diretórios padrões mais importantes são listados na Tabela 3 a seguir: Tabela 3: Diretórios padrões e seus conteúdos. 6.3 TIPOS DE ARQUIVOS O Linux define sete tipos de arquivos. Mesmo quando os desenvolvedores adicionam alguma coisa nova, ela ainda tem de ser feita de maneira a se encaixar como um dos sete tipos a seguir: Arquivos regulares; Diretórios; Arquivos de dispositivo de caracteres; Arquivos de dispositivo de blocos; Sockets de domínio local; Pipes identificados (FIFOs); Links simbólicos. É possível determinar o tipo de um arquivo existente com o comando ls –l. O primeiro caractere do relatório de saída do ls codifica o tipo. A tabela 4 mostra os códigos usados pelo ls para representar os vários tipos de arquivos. Tabela 4: Codificação de tipos de arquivos utilizados por ls. 6.3.1 ARQUIVOS REGULARES Os arquivos de texto, arquivos de dados, programas executáveis e bibliotecas compartilhadas são armazenados como arquivos regulares. Tanto o acesso sequencial como o aleatório são permitidos. 6.3.2 DIRETÓRIOS Um diretório contém referências com nomes para outros arquivos. Você pode criar diretórios com mkdir e excluí-los com rmdir se estiverem vazios. Você pode excluir diretórios não vazios com rm –r. 6.3.3 ARQUIVOS DE DISPOSITIVO DE BLOCOS Os arquivos de dispositivo permitem que os programas se comuniquem com o hardware e periféricos do sistema. Quando o kernel é configurado, os módulos que sabem como se comunicar com cada um dos dispositivos do sistema são associados. O módulo para um determinado dispositivo, chamado de driver de dispositivo, cuida dos confusos detalhes do gerenciamento de dispositivos. Os drivers de dispositivo apresentam uma interface de comunicação padrão que se parece com um arquivo regular. Quando o kernel recebe uma requisição que se refere a um arquivo de dispositivo de blocos ou caracteres, ele simplesmente passa a requisição ao driver de dispositivo apropriado. 6.3.4 SOCKETS DE DOMÍNIO LOCAL Sockets são conexões entre processos que permitem que comunicação hierarquica. O Linux fornece vários tipos de sockets, a maioria dos quais envolve o uso de uma rede. Sockets de domínio local são acessíveis somente a partir do host local e são referenciados por meio de um objeto do sistema de arquivos em vez de uma porta de rede. Embora os arquivos de socket sejam visíveis para outros processos na forma de entradas de diretório eles não podem ser lidos ou gravados por processos não envolvidos na conexão. 6.3.5 PIPES IDENTIFICADOS Os pipes identificados permitem a comunicação entre dois processos sendo executados no mesmo host. Eles também são conhecidos como “arquivos FIFO” (FIFO é a abreviatura para “first in, first out”, isto é, “o primeiro a entrar é o primeiro a sair”). 6.3.6 LINKS SIMBÓLICOS Um link simbólico ou soft link aponta para um arquivo pelo nome. Quando o kernel se depara com um link simbólico no curso de uma pesquisa de um nome de caminho, ele redireciona sua atenção para o nome de caminho armazenado como conteúdo do link. A diferença entre links físicos e links simbólicos é que o link físico é uma referência direta enquanto um link simbólico é uma referência por nome; links simbólicos são distintos dos arquivos para os quais eles apontam. Como os links simbólicos podem conter caminhos arbitrários, eles podem referenciar arquivos em outros sistemas de arquivos ou arquivos inexistentes. Links simbólicos múltiplos também podem formar um loop. 6.4 ATRIBUTOS DE ARQUIVO Sob o modelo de sistema de arquivos UNIX e Linux tradicional, todo arquivo tem um conjunto de nove bits de permissão que controlam quem pode ler, gravar e executar o conteúdo do arquivo. Junto com três outros bits que afetam primariamente a operação dos programas executáveis, esses bits constituem o “modo” do arquivo. Os 12 bits de modo são armazenados juntos com quatro bits de informações sobre o tipo de arquivo. Os quatro bits de tipo de arquivo são configurados quando o arquivo é criado pela primeira vez e não podem ser modificados, mas o proprietário do arquivo e o super-usuário (root) podem modificar os 12 bits de modo usando o comando chmod (change mode). Utilizamos o comando ls –l (ou ls –ld para um diretório) para inspecionar os valores desses bits. 7 GERENCIAMENTO DE MEMÓRIA A memória de um sistema de computação consiste em um grande array de bytes ou palavras, cada um contendo o seu próprio endereço. A interação é feita através de uma sequência de leituras e escritas a endereços de memória específicos. Um programa para ser executado deve ser carregado na memória e ser inserido no processo. Esse processo é movimentado durante a sua execução entre a memória e o disco. O gerenciamento de memória é responsável pelo controle de quais partes da memória estão em uso e quais não estão, a fim de alocar os processos na memória, liberar a memória que estava sendo ocupada por um processo que terminou e tratar do problema de swapping entre a memória principal e o disco, quando a memória principal não tiver espaço suficiente para suportar vários processos e diversos outros aspectos. O gerenciamento de memória no Linux possui dois componentes: o primeiro lida com a liberação e alocação da memória – páginas, grupos de páginas e pequenos blocos de memória. Já o segundo gerencia a memória virtual, na qual a memória é mapeada no espaço de endereçamento de processos em execução, o que possibilita a execução de vários processos que não estejam necessariamente armazenados na memória principal. 7.1 GERENCIAMENTO DE MEMÓRIA FÍSICA O kernel trata a página física como unidade básica para gerência de memória. Quando aparece uma solicitação de memória física, o kernel atende a solicitação utilizando uma zona apropriada. No Linux, a memória física é dividida em três zonas: ZONE_DMA, ZONE_NORMAL e ZONE_HIGHMEM. Essas zonas variam com a arquitetura do computador. Por exemplo, na arquitetura Intel de 32 bits, o kernel é mapeado nos primeiros 896 MB do espaço de endereçamento; a memória restante é chamada de memória alta e é alocada a partir da ZONE_HIGHMEM. O alocador de páginas é o gerenciador principal da memória física. Ele é responsável por alocar e liberar todas as páginas físicas para a zona e é capaz de alocar intervalos de páginas fisicamente contíguas sob demanda. O alocador utiliza um algoritmo de agrupamento de parceiros ou mais conhecido como sistema de pares para gerenciar páginas físicas disponíveis, na qual as unidades adjacentes de memória alocável são reunidas em duas a duas. Sempre que duas regiões parceiras alocadas forem liberadas, elas serão combinadas para formar uma região maior, região essa que também tem um par, com a qual ela pode combinar para a formação de uma região maior ainda. Se por exemplo, tivermos uma solicitação de pouca memória, e não tivermos uma pequena região livre, devemos subdividir a região maior em dois parceiros para atender tal solicitação. Todas as alocações de memória no kernel ocorrem tanto de forma estática, na qual é feita por drives que reservam uma área de memória adjacente em tempo de inicialização do sistema,como também de forma dinâmica feita pelo alocador de páginas. A gerência de memória possui subsistemas importantes do kernel: o alocador de tamanho variável kmalloc, e os dois caches de dados persistentes do kernel, o cache de páginas e o cache-buffer. O kmalloc aloca páginas inteiras sob demanda, mas depois divide em partes menores. Este serviço tem utilidade para solicitações de diferentes tamanhos, onde o tamanho não é conhecido com antecedência e pode corresponder a poucos bytes e não a uma página inteira. Uma função que queira alocar memória deve passar uma prioridade de solicitação para a função alocação. As regiões das memórias solicitadas pelo sistema de kmalloc ficam permanentemente alocadas até que sejam explicitamente liberadas. Outra forma de alocar memória no Linux é a alocação de placas. Uma placa é utilizada para alocar a memória para estruturas de dados do kernel e é composta por uma ou mais páginas fisicamente contínuas. Um cache é formado por uma ou mais placas e existe um para cada estrutura de dados. Quando um cache é criado, um determinado número de objetos é alocado a ele e o algoritmo de alocação de placas utiliza caches para armazenar esses objetos do kernel. No Linux, uma placa pode estar em um dos três estados possíveis: Cheia: todos os objetos na placa estão marcados como utilizados; Vazia: todos os objetos na placa estão marcados como livres; Parcial: A placa consiste tanto em objetos livres como objetos utilizados. O alocador age primeiro tentando atender a solicitação com o objeto livre de uma placa parcial. Se não existir qualquer objeto, é atribuído um objeto livre a partir de uma placa vazia. Se não existirem placas vazias disponíveis, uma nova placa é alocada a partir das páginas físicas contíguas e atribuída a um cache. O cache-buffer e o sistema de memória virtual também fazem seu próprio gerenciamento de memória. O cache-buffer é o principal cache do kernel para dispositivos orientados a blocos e é o mecanismo principal através do qual é realizado I/O para estes dispositivos. Este mecanismo aloca páginas inteiras de conteúdo de arquivos em caches e não é limitado aos dispositivos de blocos. Ele também pode alocar em caches os dados da rede. Já o sistema de memória virtual gerencia o conteúdo do espaço de endereçamento virtual de cada processo. Esses dois sistemas interagem fortemente uns com os outros. 7.2 MEMÓRIA VIRTUAL A técnica de memória virtual foi criada para permitir a execução de vários processos que não estejam necessariamente armazenados por inteiro na memória principal, aumentando a capacidade de multiprocessamento de um sistema de computação. A implementação é feita de duas formas: com paginação sob demanda ou com segmentação sob demanda. As versões mais modernas do UNIX, baseiam-se na paginação, que é a transferência de páginas individuais de memória virtual entre memória física e o disco. O kernel do Linux gerencia sua própria memória e, também, o espaço de endereçamento do usuário, mantendo o espaço de endereços visível para cada processo. As páginas de memória virtual são criadas sob demanda (paginação sob demanda). O gerenciador de memória virtual possui duas visões separadas do espaço de endereços de um processo: como um conjunto de regiões separadas e como um conjunto de páginas. A primeira, do espaço de endereços de um processo como um conjunto de regiões separadas, é uma visão lógica. Ela descreve as instruções que o sistema de memória virtual recebeu em relação ao formato do espaço de endereços. Esse espaço de endereços é composto de um conjunto de regiões não sobrepostas. Cada região é descrita por uma única estrutura vm_area_struct que define as propriedades da região, incluindo permissões de leitura, gravação e execução na região. A outra visão é a do espaço de endereços como um conjunto de páginas que armazena as páginas em tabela. As entradas de tais tabelas definem a localização exata corrente de cada página da memória virtual. A visão física é gerenciada por um conjunto de rotinas invocadas a partir dos manipuladores de interrupções de software do kernel sempre que um processo tentar acessar a página que no momento não esteja presente na tabela de páginas. O Linux implementa diferentes tipos de regiões de memória virtual. A memória da retaguarda (memória secundária) descreve de onde provém as páginas para uma região. As regiões da memória costumam ser reservadas tanto por um arquivo como por nada. A região reservada por nada são conhecidas como memória de demanda zero, ou seja, quando um processo tenta ler uma página em tal região, é retornada uma página de memória preenchida de zeros. Já uma região reservada por um arquivo atua como uma porta de visualização para uma seção de arquivo. Por exemplo, quando um processo tentar acessar uma página dentro da região, a tabela de páginas será ocupada com o endereço de uma página dentro do cache de páginas do kernel, correspondente ao deslocamento apropriado do arquivo. A região é também definida por sua reação às gravações. O mapeamento de uma região no espaço de endereçamento do processo pode ser compartilhado ou privado. Se um processo gravar de forma privada em região mapeada, o paginador verá que é necessária uma operação de cópia-após-gravação, para manter as alterações na estrutura do processo. Já as gravações de modo compartilhado resultam na atualização do objeto mapeado nessa região, fazendo com que a alteração possa ser vista por qualquer outro processo que estiver mapeando o objeto. 7.2.1 TEMPO DE VIDA DE UM ESPAÇO DE ENDEREÇAMENTO VIRTUAL Para a criação de um novo espaço de endereçamento virtual, o kernel tem duas opções: quando um processo executa um novo programa com a chamada de sistema exec( ) e quando é criado um novo processo a partir da chamada de sistema fork ( ). O primeiro caso é simples, pois quando um novo programa é executado, o processo recebe um espaço de endereçamento de memória virtual vazio e é tarefa das rotinas carregarem o programa para popularem o espaço. No segundo caso é mais complexo, pois tem a necessidade de criação de uma cópia completa do espaço de endereçamento virtual do processo já existente. O kernel copia do processo pai, seus descritores vm_area_struct, ocasionando a criação de um novo conjunto de tabelas de páginas para o filho. Essas tabelas são copiadas diretamente nas tabelas do filho, incrementando-se o contador de referência de cada página. Após o fork( ), pai e filho compartilham as mesmas páginas físicas de memória nos seus espaços de endereços. 7.2.2 PERMUTA E PAGINAÇÃO Um sistema de memória virtual possui a tarefa importante de definir a expulsão de páginas da memória física para o disco, quando a memória for necessária. O mecanismo utilizado é o de paginação. Nele, o algoritmo decide que páginas gravar no disco e quando gravá-las. Logo depois, a paginação realiza a transferência e pagina os dados de volta na memória física quando forem necessários novamente. A política de expulsão utiliza uma versão modificada do algoritmo-padrão do relógio. No entanto, o Linux utiliza um relógio de passos múltiplos e toda página possui uma idade que é ajustada a cada passo do relógio. A idade mede a juventude da página ou quanto a página realizou recentemente. Quando as páginas forem mais acessadas elas terão uma maior idade, mas enquanto as páginas forem pouco acessadas a idade tenderá a zero. Esse mecanismo de contagem da idade permite ao paginador expulsar páginas que são freqüentemente menos utilizadas. 7.3 MAPEAMENTO DE PROGRAMAS NA MEMÓRIA A chamada de sistema exec ( ) faz a execução de programas do usuário pelo kernel. Ela ordena ao kernel que execute um novo programa dentro do processo corrente, superpondo o contexto de execução corrente com o contexto inicial do novo programa. O primeiro job deste serviço do sistema é verificar se o processo que invocou o novo programa possui direitos de permissão para o arquivo que está sendo executado e depois o kernel invoca uma rotina de carga para começar a execução do programa. As páginas do arquivo binário são mapeadas em regiões de memória virtual. Quando o programa tentar acessar uma página, irá ocorrer um erro de página com o objetivo de carregar a página desejada na memória física. A figura 7.3, apresenta o formato típico das regiões de memória estabelecidas pelo carregador ELF. Um arquivo binário no formato ELF, consiste em um cabeçalho seguido por diversas seções de páginas alinhadas. O carregador ELF funciona lendo o cabeçalho e mapeando as seções do arquivo em regiões separadas da memória virtual. O kernel permanece em uma região reservada numa das extremidades do espaço de endereços. Os programas em modalidade usuário não podem acessar a memória virtual do kernel. O restante da memória virtual é disponibilizado às aplicações que podem utilizar as funções de mapeamento em memória do kernel para criar regiões que mapeiam uma parte do arquivo ou que ficam disponíveis para os dados das aplicações. A função do carregador é estabelecer o mapeamento inicial na memória para permitir o início da execução do programa. As regiões que precisam ser inicializadas são a pilha e o texto do programa e suas regiões de dados. A pilha é criada no topo da memória virtual de modalidade usuário e cresce para baixo em direção aos endereços de numeração mais baixa. Na pilha, encontramos cópias das variáveis de argumentos e do ambiente, que são fornecidas pela chamada de sistema exec( ). As outras regiões são criadas perto da extremidade da base da memória virtual. Logo após, são mapeadas as seções de arquivo binário que contêm texto de programa ou dados de leitura. Depois das regiões de tamanho fixo, fica uma região de tamanho variável que os programas podem expandir conforme o necessário para manter dados alocados em tempo de execução. Após realizados todos os mapeamentos,o carregador inicializa o registrador contador de programas do processo com o ponto de início registrado no cabeçalho do ELF e o processo pode ser submetido ao escalonador. Quando o programa inicia sua execução, todos os conteúdos do arquivo binário já estão no espaço de endereços virtuais do processo. Figura 2: Formato de memória para programas ELF 8 GERENCIAMENTO DE PROCESSOS Processo é definido por um programa em execução. Podemos dizer de uma maneira mais abrangente que é, na verdade, um programa ativo com seus recursos relacionados. Para o kernel o objetivo do processo é atuar como uma entidade para a qual os recursos de sistema (como tempo de CPU, memória e etc) são alocados. 8.1 CRIAÇÃO DE PROCESSO No Linux a criação do processo é feita pela chamada fork(). O processo-pai chama fork() e o processo filho é criado. O processo-pai segue sua execução e o processo-filho começa a partir de sua criação. O processo-pai e o processo-filho têm o PID, que é o identificador do processo único de cada processo, diferentes. Um programa é executado em seguida da chamada fork(), para isso a família exec() de chamada de função é usada para assim criar um novo espaço de endereço e carregar um novo programa nele. A chamada exit() é usada quando um programa termina sua execução ou seja o processo é encerrado e são liberados seus recursos. A chamada wait() informa ao processo-pai sobre o estado do processo-filho terminado. Quando um processo termina, ele é posto no estado Zumbi que representa que o processo filho terminou até que o processo-pai chame wait() ou waitpid(). A chamada fork() é implementada via chamada clone() no Linux, que possui uma série de flags que especificam que recursos devem ser compartilhados entre o processo-pai e o processo-filho. A chamada clone() é invocada através das chamadas fork(), vfork() e clone(). A chamada clone(), por sua vez, chama a do_fork() que é definida em <kernel/fork.c>. E em seguida é chamada a função copy_process() e é iniciada a execução dos processos. 8.2 DESCRITOR DE PROCESSO E A ESTRUTURA DE TAREFAS Uma lista de tarefas (encadeada e duplamente circular) é armazenada no kernel onde cada elemento é um descritor de processo do tipo struct_task_struct que é definido em <linux/sched.h> e contém todas as informações sobre um processo específico. O bloco de processo é implementado nesta estrutura. Na figura 3 abaixo temos o descritor de processo e a lista de tarefas: Figura 3: Descritores. Os estados dos processos no kernel do Linux podem ser vistos na figura 4 a seguir: Figura 4: Estados dos processos. Para mudar de estado o mecanismo que o kernel utiliza é a função set_task_state(task_state), que seta a tarefa para o estado task_state. 8.3 CONTEXTO DO PROCESSO No momento que um programa executa uma chamada de sistema ou dispara uma execução ele entra no espaço do kernel, dessa forma, o kernel é dito operando em contexto do processo. Chamas de sistema e tratadores de execução são interfaces do kernel. Através de um desses dois recursos pode-se dar início a uma execução de processo. 8.4 ÁRVORE DA FAMÍLIA DE PROCESSOS No Linux, os processos são descendentes do processo init que possui PID=1. O init é inicializado no kernel no último passo do processo boot, que, em seguida, lê os scripts de inicialização e executa mais programas completando o processo de boot. O relacionamento entre processos é armazenado no descritor de processo. Cada task_struct tem um ponteiro para a task_struct do pai denominado parent e uma lista de filhos denominada children. É possível seguir a hierarquia de qualquer processo no sistema já que a lista de tarefa é uma lista duplamente encadeada circular. 8.5 IMPLEMENTAÇÃO DE THREADS No Linux não há conceito de threads, os threads são uma forma de compartilhar recursos entre processos. Nas chamadas de sistema clone() são passadas flags correspondentes aos recursos específicos a sempre compartilhados: clone(CLONE_VM |CLONE_FS|CLONE_SIGHAND); CLONE_VM: pai e filho compartilham o espaço de endereçamento; CLONE_FS: pai e filho compartilham informações do sistema de arquivos; CLONE_FILES: pai e filho compartilham arquivos abertos; CLONE_SIGHAND: pai e filho compartilham tratadores de sinais bloqueados. 8.5 COMUNICAÇÃO INTERPROCESSO O mecanismo padrão que o Linux utiliza para informar a um processo que ocorreu um evento é o sinal. Para se comunicar com processos em execução o kernel utiliza a comunicação sobre eventos assíncronos recebidos através de estado de scheduling e de estruturas wait_queue. É o que permite que processos em modalidade de kernel informem uns aos outros sobre eventos relevantes e também permitem que eventos sejam gerados por drivers de dispositivos ou pelo sistema de rede. O mecanismo de pipe do Unix permite que um processo filho herde um canal de comunicação de seu processo-pai. No Linux, cada pipe tem um par de filas de espera para sincronizar o leitor e gravador. Um conjunto de recursos de rede também é definido pelo Unix que podem enviar fluxos de dados para processos locais e remotos. A memória compartilhada é outro método de comunicação entre processos que oferece uma maneira rápida de comunicar grandes ou pequenas quantidades de dados. A principal desvantagem da memória compartilhada é que não oferece sincronização sozinha. Os processos podem criar ou remover uma região compartilhada vista como objeto pelo Linux. É tratado como espaço de endereçamento independente. 9 ESCALONAMENTO DE PROCESSO O escalonador é o mecanismo que define qual processo será executado. Ao fazer isso o escalonador é responsável pelo melhor desempenho do sistema. O sistema Linux é multitarefa e preemptivo. Ou seja, decide quando um processo deve interromper a execução e um novo processo será escalonado para retomar a execução. O timeslice é o tempo que o processo executa antes de ser interrompido. Ele concede a cada processo pronto para ser executado fatias de tempo do processador. Para realizar o escalonamento é necessário algumas políticas: Política de escalonamento: É o procedimento usado para otimizar a utilização do tempo do escalonador. Existem duas classificações para o processo: Processo I/O-bound: utilizam a maior parte do tempo submetendo e esperando por requisições de I/O. Processo CPU-bound: utilizam a maior parte do tempo executando códigos. O Linux favorece mais os processos I/O-bound do que o CPU-bound. Para o Linux, existem dois algoritmos separados de scheduling de processo. Um algoritmo de compartilhamento de tempo, afim de ter um scheduling justo e preemptivo entre múltiplos processos, e outro que é projetado para tarefas de tempo real. 9.1 ALGORITMO DE COMPARTILHAMENTO DE TEMPO É baseado em prioridade com prioridades de dois intervalos de prioridade separados: intervalo de tempo real (0-99) e um intervalo de valor de ajuste (100-140). Os valores numericamente mais baixos indicam prioridades mais altas. Neste caso, atribuem parcelas de tempo mais longas a tarefas de prioridade mais alta e parcelas de tempo mais curtas a tarefas de prioridade mais baixa. Em virtude de o sistema operacional Linux ser preemptivo, ao entrar no estado task_RUNNING, o kernel verifica se a sua prioridade é maior que a prioridade do processo que está atualmente executando. Caso esteja, o escalonador é invocado para interromper o processo em execução e colocar para executar um novo processo que estiver pronto para executar. No momento que o timeslice chegar a 0 ele será interrompido e o escalonador é novamente invocado para selecionar o novo processo. A estrutura de dados básica no escalonador está na fila de execução que é definida como struct runqueue. Cada fila de execução contém dois vetores de prioridade (ativo e expirado). Estes vetores são definidos em kernel/sched.c com struct prio_array. Cada um desses arrays incluem uma lista de tarefas indexadas de acordo com a prioridade, ou seja, o scheduling seleciona a tarefa que tem a maior prioridade no array ativo para que passe para o estado de execução da CPU. O ativo contém todas as tarefas com tempo remanescente em suas parcelas de tempo e o expirado contém todas as tarefas expiradas. Quando o array ativo estiver vazio, o de menor prioridade toma o seu lugar. A troca de contexto no Linux é realizada pela função context_switch() definida em kernel/sched.c. 9.2 ALGORITMO PROJETADO DE TEMPO REAL Neste caso, são implementadas duas classes de scheduling requeridas pelo POSIX: o primeiro a chegar é o primeiro a ser servido (FCFS) e round-robin. Cada processo tem prioridade e classe de scheduling. O algoritmo sempre executa os processos respeitando suas prioridades, ou seja, o processo de maior prioridade é executado primeiro que o processo de menor prioridade. O scheduling oferece garantias estritas quanto às prioridades relativas dos processos de tempo real, mas o kernel não oferece qualquer garantia quanto a rapidez com que um processo de tempo real será alocado ao scheduling uma vez que se torne executável. No Linux, existe uma família de chamadas de sistema para gerenciar os parâmetros do escalonador, algumas delas estão na tabela 5. Tabela 5: Algumas chamadas do sistema relacionadas ao escalonador. 9.3 SICRONIZAÇÃO DO KERNEL A sincronização do Kernel requer uma estrutura de que permita que as tarefas do kernel sejam executadas sem violar a integridade dos dados compartilhados. O Linux fornece spinlock e semáforos (bem como versões de leitor e gravador) para fechamento do kernel. O spinlock é usado para períodos curtos. Para as máquinas com um único processador é usada a habilitação e desabilitação da preempção do kernel. O kernel não é suscetível a sofrer com preempção se estiver mantendo um lock. Cada tarefa do sistema tem uma estrutura thread-info que inclui o campo preempt_count que é o contador indicando o número de locks sendo mantidos pela tarefa. Se for maior que zero não é seguro fazer preempção e se for igual o kernel pode ser interrompido tendo em vista que não existe chamadas pendentes para preempt_disable(). Os semáforos são utilizados quando um lock for mantido por longos períodos. As seções críticas são outra técnica de proteção que ocorrem em rotinas de serviço de interrupção. Para isto utilizamos o hardware de controle de interrupções do processador. Ao desabilitar interrupções ou utilizar spinlocks durante a seção critica, o kernel garante seu prosseguimento sem correr o risco de acesso simultâneo às estruturas de dados compartilhadas. Possuem a desvantagem de serem dispendiosas. A arquitetura de sincronização usada pelo kernel do Linux permite a execução de seções criticas longas sem que as interrupções sejam desabilitadas durante toda a seção critica. O que é útil no código de conexão de rede. Para o perfeito funcionamento do sistema, temos ainda as rotinas de serviços. As rotinas de serviços são separadas em duas seções: Metade do topo: é executada com instruções recursivas desabilitadas. As instruções de mais altas prioridades podem interromper a rotina e as de mesma prioridade ou de baixa prioridade são desabilitadas. Metade da base: uma rotina de serviço é executada, com todas as interrupções habilitadas por um scheduler miniatura que garante que essas metades da base nunca interromperam a si mesmas. O scheduler da base é invocado automaticamente sempre que uma rotina de serviço de interrupção é encerrada. Isto garante que o kernel pode concluir qualquer processamento complexo que precise ser executado em resposta a uma interrupção sem preocupações quanto a ser interrompido. A arquitetura é completada com o mecanismo para desabilitar metade das bases selecionadas enquanto o código normal do kernel estiver em execução em foreground. 10 API DE CHAMADAS DO SISTEMA Chamadas do sistema fornecem uma interface de programação para os serviços fornecidos pelo SO. São acessadas pelos programas por meio de uma interface de programação de aplicações (Application Program Interface - API), ao invés de uma chamada direta do sistema. A API especifica um conjunto de funções que estão disponíveis para o programador de aplicações, inclusive os parâmetros que são passados para cada função e os valores de retorno que o programador pode esperar. Praticamente todas as versões do UNIX utilizam uma API denominada POSIX (Portable Operating System Interface). Um dos benefícios de se programar de acordo com as funções de uma API é a portabilidade do programa. Quando um programador projeta um programa usando uma API ele espera que seu programa seja compilado e executado em qualquer sistema que utilize aquela mesma API sem nenhum problema. Outro fato importante é que as chamadas de sistemas originais normalmente são mais detalhadas e difíceis de manipular do que através das funções disponíveis em uma API. Figura 5: Exemplo de chamadas de sistema para copiar o conteúdo de um arquivo para outro. A lista das chamadas de sistema do Linux pode ser encontrada no arquivo /usr/include/asm/unistd.h ou pode ser obtida com o comando "man syscalls". 10.1 OS TIPOS DE CHAMADAS DE SISTEMA As chamadas de sistemas podem ser divididas em seis categorias: Controle de processo; Manipulação de arquivo; Manipulação de dispositivo; Manutenção de informação; Comunicações; Proteção. Cada um dos tipos de chamadass de sistema dá suporte a um tipo especifico de funções. 10.1.1 CONTROLE DE PROCESSO As funções de chamadas de sistema do tipo controle de processos servem para dar suporte à manipulação de processos. Normalmente, neste tipo de chamadas de sistema, estão funções do tipo: end, abort, load, execute, create process, terminate process, etc. 10.1.2 MANIPULAÇÃO DE ARQUIVOS Funções deste tipo dão suporte à manipulação de arquivos. Como por exemplo: open, read, write, close, create file, delete file, etc. 10.1.3 MANIPULAÇÃO DE DISPOSITIVOS São as funções que gerenciam o uso de dispositivos pelos processos. Exemplo: request device, release device, read, write, reposition. 10.1.4 MANUTENÇÃO DE INFORMAÇÃO As funções que fazem parte deste tipo de chamadas de sistema se encarregam de manipular informações entre os processos, o sistema operacional e o usuário. São funções do tipo: getpid, alarm, sleep. 10.1.5 COMUNICAÇÕES São funções que tratam das comunicações entre processos, através de memória compartilhada e/ou transmissão de mensagens. Entre estas funções, estão funções como: send, receive, pipe, shmget, mmap, etc. 10.1.6 PROTEÇÃO As funções de proteção criam um mecanismo de política de controle de acesso à recursos. Por exemplo: chmod, umask chown. Na tabela 6 temos vários exemplos de chamadas de sistemas usadas nos sistemas Windows e UNIX, vale ressaltar que as utilizadas pelo Linux são as mesmas utilizadas pelo UNIX. Tabela 6: Tipos de chamadas de sistema. 11 ENTRADA E SÁIDA Do ponto de vista do usuário, o sistema de I/O é próximo do sistema Unix. Os dispositivos são divididos em três classes: dispositivos de blocos, dispositivos de caracteres e dispositivos de rede. Dispositivo de blocos: São os dispositivos que permitem o acesso aleatório a blocos de dados de tamanho fixos completamente independentes, incluindo discos rígidos, CD-ROM’s e memória flash. São utilizados para armazenar sistemas de arquivos, mas em acesso direto a um dispositivo de blocos também é permitido de modo que os programas possam criar e reparar o sistema de arquivos que o dispositivo contém. Podem ser acessados aleatoriamente. Dispositivo de caracteres: Incluem dispositivos como mouses e teclados. E esses dispositivos são acessados serialmente. Dispositivo de rede: Uma conexão para o subsistema de conexão de rede do kernel será aberta para comunicação indireta, pois os usuários não podem transferir dados para dispositivo de rede. 12 SEGURANÇA NO LINUX Em termos de segurança temos dois problemas a serem considerados, são eles a autenticação e o controle de acesso. Em linhas gerais a autenticação garante que só quem possui direito de acesso ao sistema são usuários autenticados e o controle de acesso fornece um mecanismo de verificação de acesso dos usuários à determinadas partições e funções. 12.1 O PROBLEMA DA AUTENTICAÇÃO A autenticação no Linux utiliza o mesmo padrão de autenticação do UNIX. Que, por sua vez, consiste na execução de um arquivo de senhas publicamente legíveis. No processo de codificação e armazenamento desse arquivo de forma a garantir a segurança da senha de um usuário, combina os caracteres digitados pelo usuário, no momento do cadastramento da senha com um valor aleatório “salt”. O resultado dessa combinação é ainda codificado utilizando uma função unidirecional e só depois disso é armazenado num arquivo destinado ao armazenamento das senhas de usuários. Uma utilização desse mecanismo têm apresentado problemas quanto a segurança dos arquivos armazenados pois, além das senhas serem limitadas a oito caracteres, o número de valores de salt eram tão baixos que algum invasor poderia criar um algoritmo que decodificasse o arquivo de senhas com relativa facilidade apenas combinando um banco de dados com senhas comumente utilizadas com outro banco de dados com todos os valores possíveis de salt. Visando contornar essa situação, os fornecedores do UNIX criaram o PAM (em português: Módulos de Autenticação Conectáveis) que consiste num novo mecanismo de segurança que segundo o livro de referência [1], “é baseado em uma biblioteca compartilhada que pode ser utilizada por qualquer componente do sistema que precise autenticar usuário.” e podem “ especificar métodos de autenticação, restrições de contas, funções de estabelecimento de sessões e funções de mudança de senhas”. O Linux faz uso do mecanismo PAM. 12.2 O PROBLEMA DO CONTROLE DE ACESSO Tanto nos sistemas UNIX quanto nos sistemas Linux, o controle de acessos é feito utilizando-se dois identificadores numéricos específicos, um identificador numérico destinado aos usuários (UID-user identifier) e outro destinado ao grupo (GIDgroup identifier). Devido ao fato do controle de acesso ser aplicado a diversos objetos, no sistema UNIX, podemos ter dois tipos de direitos conferidos aos processos que estão em andamento. O direito de usuário, sobre um objeto, é conferido ao processo que possuir seu UID igual ao UID do objeto. Se por outro lado, o UID não for igual e tiver algum GID do processo coincidindo com o do objeto então, é conferido o direito de grupo ao processo sobre o objeto. Caso nenhumas das duas possibilidades ocorrerem, então é dado ao processo o direito universal sobre o objeto. No caso do sistema Linux, independente dos diretos de acesso que forem atribuídos aos processos, o controle de acesso atribui, uma máscara de proteção aos objetos. Essa máscara de proteção serve para especificar as quais modalidade de acesso serão feitas, podendo ser: leitura, gravação ou execução. No caso do acesso do UID privilegiado root, seus processos têm acesso automático a qualquer objeto do sistema além de receberem permissões para execução de operações privilegiadas. Esse mecanismo impede que usuários comuns acessem determinados recursos do sistema, um exemplo são os recursos internos essenciais do kernel, pois só quem possui permissão de utilizá-los, é o UID root. O Linux implementa o mecanismo setuid padrão do UNIX, este consiste em permitir que um programa execute privilégios diferentes dos privilégios dos usuários que estão executando um programa ativo. Como implementação própria, o Linux fornece também um mecanismo para permitir uma transmissão flexível de direitos de um programa para outro, ou seja, permite que um cliente passe o acesso a um único arquivo para algum processo de servidor sem conceber ao processo qualquer outro privilégio. Esse mecanismos passou a ser implementado também em versões mais atuais do sistema UNIX. 13 PROGRAMAS UTILIZADOS NO LINUX Os programas que podem ser usados no Linux são semelhantes aos programas que se utiliza no Windows. A grande vantagem do Linux é que as distribuições vêm com uma grande quantidade de aplicativos e a dificuldade passa a ser na escolha dos programas mais usuais. A seguir apresentaremos alguns programas usados no Linux: 13.1 OPENOFFICE Ele é composto pelo Writer (editor de texto), Calc (planilha), Draw (programa de desenho vetorial), Impress (gerador de apresentações) e Math (editor de equações). Possui compatibilidade com mais formatos diferentes e também salva o arquivo em PDF. Figura 6: OpenOffice. Para instalar acesse: http://www.openoffice.org.br. Outros programas: Koffice: É uma estrutura alternativa mais simples que o OpenOffice. Abiword: É um dos considerados mais próximos do Word. 13.2 GIMP O GIMP é um editor de imagens similar ao PhotoShop. Figura 7: GIMP. Pode instalar acesse: http://www.ogimp.com.br ou http://www.gimp.com.br 13.3 QCAD É um aplicativo usando para elaboração para projetos em duas dimensões. Como desenhos técnicos como plantas de prédios, interiores, esquemas e diagramas. Figura 8: QCAD. Para instalar acesse: http://www.baixaki.com.br/linux/download/qcad.htm#ixzz1v2gnW3VV 13.4 OPERA É um navegador rápido e prático, que está integrado a todos os serviços web e tem diversas funcionalidades extras. Como além de navegar na web, bate-papo, compartilha arquivos e imagens e etc. Figura 9: Opera. Para instalar acesse: http://www.baixaki.com.br/linux/download/opera.html 13.5 MOZILLA THUNDERBIRD É um cliente de e-mails e notícias da Mozilla Foundation, mesma criadora do Mozilla Firefox . Figura 10: Mozilla Thunderbird. Para instalar acesse: http://br.mozdev.org/thunderbird/download/ 13.6 EVOLUTION O Evolution é dedicado para o público corporativo, que inclui funções de agenda, suporte a Palms e, principalmente, suporte a LDAP e servidores MS Exchange, o que permite a integração com a estrutura de e-mail e gerenciamento de tarefas usadas em muitas empresas. Figura 11: Evolution. Para instalar acesse: http://www.baixaki.com.br/linux/download/evolution.htm 13.7 KAFFEINE O Kaffeine é o player de mídia padrão do KDE, com capacidade de exibir vídeos em vários formatos, música, DVDs e até TV, através de uma placa de captura. Figura 12: Kaffeine. Para instalar acesse: http://www.baixaki.com.br/linux/download/kaffeine.htm 13.8 MPLAYER Ganhou fama por ter sido o primeiro player de vídeo "completo" para Linux, capaz de exibir vídeos na maioria dos formatos e DVDs protegidos.É um dos players mais usados, ainda que não venha incluído por padrão na maioria das distribuições, que optam em usar o Kaffeine. Figura 13: Mplayer. Para instalar acesse: http://www.baixaki.com.br/linux/download/mplayer.htm 13.9 K3B É utilizado para gravação de CDs e DVDs no Linux. Figura 14: K3B. Para instalar acesse: http://k3b.org 14 CONCLUSÕES Diante do que foi exposto podemos observar que o Linux é uma excelente alternativa para os que desejam utilizar os recursos de um sistema operacional. Pelo fato da gratuidade de utilização e por possuir uma grande quantidade de softwares estáveis necessários para execução de tarefas e trabalhos, vem tendo grande aceitação no mercado e dessa forma vem sendo muito utilizado. 15 BIBLIOGRAFIA [1] Nemeth E. & Snyder G. & Hein T.R., Manual completo do Linux – Guia do Administrador, 2aEd., 2004 [2] Fórum Ubuntu. <http://ubuntuforum-br.org/index.php?topic=80108.0>. Acessado em: 17 de maio de 2012. [3] <http://conectiva.com/doc/livros/online/10.0/servidor/pt_BR/ch03s03.html>. Acessado em: 17 de maio 2012. [4] Silberschatz A. & Gagne G. & Galvin P.B., Fundamentos de Sistemas Operacionais, 2004. [5] <http://pt.wikipedia.org/wiki/Univac_1107>. Acessado em: 15 de maio de 2012. [6] <http://www.museudocomputador.com.br/1950dc_1960dc.php>. Acessado em: 15 de maio de 2012. [7] <http://www.tecmundo.com.br/sistema-operacional/4228-a-historia-do-linux.htm>. Acessado em: 15 de maio de 2012. [8] UNICAMP. A evolução dos sistemas de arquivos: do Ext ao Ext4. Disponível em: <http://www.lsd.ic.unicamp.br/mc514/sites/default/files/proj.mc514.pdf>. Acessado em: 18 de maio de 2012. [9] Carlos E. Morimoto. Guia do Hardware. Disponível em: <http://www.hardware.com.br/termos/ext3> Acessado em: 18 de maio de 2012.