estudo da estrutura do sistema operacional … · o sistema operacional linux, foco do trabalho,...
TRANSCRIPT
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 (GID-
group 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.