Programação Avançada em Linux Gleicon da Silveira Moraes Novatec Editora Capítulo 1 Características do Linux Neste capítulo são descritos detalhes e características do sistema operacional GNU/Linux importantes para a compreensão do funcionamento geral do kernel Linux e de sua trajetória até o estado atual. Algumas ferramentas são introduzidas, como compiladores e uma visão geral do processo de boot é exposta. 1.1 Um pouco de história O kernel do Linux é obra de Linus Torvalds e de vários colaboradores. Sua história é bem conhecida por todos. Foi feito um núcleo (kernel) baseado no padrão POSIX e cresceu de forma espantosa graças à colaboração de pessoas do mundo inteiro com drivers e partes do kernel. Onde for empregado o nome Linux, estaremos tratando apenas do kernel, e, quando utilizado o termo GNU/Linux, estaremos tratando de uma distribuição já instalada, ou do kernel somado a alguns programas externos. Sua criação e evolução divulgou um modo de trabalho cooperativo e sem interesse financeiro, que redesenhou o mercado de tecnologia, surgindo em sua esteira muitos projetos de diferentes graus de sucesso, utilizando o mesmo modo de trabalho. Seja por puro inconformismo, genialidade ou, mesmo, excesso de tempo livre, a sorte é que temos mais alternativas de sistemas operacionais acessíveis ao usuário final, democratizando o acesso às novas tecnologias e abrindo um campo de trabalho para muitas pessoas. Pense bem em quantos empregos foram gerados por esta idéia! 15 16 Programação Avançada em Linux 1.2 Clone do Unix O Linux é um “clone” do Unix e emprega os padrões e idéias desta categoria de sistemas operacionais, mas com código moderno e idéias inovadoras. As vantagens dessa “herança” são modularidade, flexibilidade e maturidade, inerentes ao Unix, robustez e segurança e modernidade conseguidas por um grupo de desenvolvedores experientes. Sistemas operacionais, sob o ponto de vista da máquina, são idênticos. Existem várias definições de sistemas operacionais, mas todas convergem ao ponto de ser uma camada entre o usuário e o hardware. A vantagem do Linux sobre sistemas de código fechado é justamente seu código disponível, sua grande base de usuários e aplicações, além da flexibilidade de sua interface de programação. Na arquitetura x86, um nome para processadores baseados no padrão Intel 386, desde 386, 486, Pentiums, AMD K5,6,7, Athlon, há limitações em função de desenho de hardware e herança de processadores antigos (compatibilidade reversa). Sendo assim, esta plataforma apresenta-se como complexa em termos de boot e gerenciamento de memória, mas, em compensação, é a que o Linux está bem mais desenvolvido. 1.3 Modo protegido O Linux, assim como o Windows, trabalha no que é chamado modo protegido do processador. Quando ligamos o computador, o processador desta arquitetura está em modo 8086. Nesse modo, ele pode acessar um máximo de 640 Kbytes de memória, não importa o quanto exista no sistema, e deve reservar certas áreas para entrada e saída de dispositivos (I/O). Neste ponto, o DOS é carregado para a memória, carrega seus drivers e vetores de interrupção, e entra no prompt de comando. Para o Windows e o Linux, o processo é um pouco diferente. Eles iniciam neste modo, mas mudam para o protegido, onde podem acessar a memória total do sistema, assim como contam com mais recursos de gerenciamento de memória, processos e dispositivos. Capítulo 1• Características do Linux 17 Em outras arquiteturas, este processo não é necessário. Tais limitações da arquitetura explicam decisões que, por exemplo, aumentaram complexidade do processo de inicialização do sistema. 1.4 Executando mais de um programa simultaneamente no DOS Como o DOS executa em modo 8086, para desfrutar de recursos multitarefa lançamos mão de bibliotecas, programas TSR (Terminate-and-Stay-Resident, ou termina ficando residente, programas que executam e ficam à espera de um comando para aparecerem ao usuário, utilizando, para tanto, interrupções do processador, de relógio ou teclado), e também de multitarefa cooperativa, ou seja, cada programa é projetado de forma a ter consciência dos outros, executando uma parte e cedendo espaço. 1.5 Múltiplos usuários simultâneos no DOS Não é possível emular esta característica, a não ser em sistemas antigos, como o MP/M 86, ou alternativos, que possuem uma estrutura semelhante ao do DOS. 1.6 Outras arquiteturas Para finalizar esta discussão de arquiteturas, basta procurar um pouco pela Internet para encontrar a especificação de outros processadores e máquinas para entender certas decisões tomadas pelos projetistas do Linux. Um exemplo comum é o Framebuffer, abstração de acesso ao vídeo, presente há muito tempo para a arquitetura da Motorola 680x0 e para Sparc (Sun), pois estas máquinas não possuem placa de vídeo como conhecemos no PC, mas, sim, uma região chamada de framebuffer, às vezes acelerado ou não. Portanto, não existe o “modo texto”, cabendo ao software suprir esta necessidade, muitas vezes emulando um modo semelhante para acesso ao console. Quem já utilizou GNU/Linux em arquitetura Sparc teve a oportunidade de notar a diferença que faz utilizar o driver de framebuffer do kernel e operar na tela original, com fundo branco e letras grandes. 18 Programação Avançada em Linux 1.7 Seqüência de boot do Linux na plataforma x86 A BIOS do computador guarda, além de informações sobre hardware, um pequeno programa, o qual é carregado logo após o teste de memória e I/O. Este programa lê, do primeiro setor do primeiro dispositivo de boot válido, um bloco de 512 bytes, copia para o endereço 0x07c00 e o executa. Tal código é chamado de bootstrap e, dentro do kernel do Linux, o setor de boot fica em bootsect.S (a extensão S é de arquivo em assembly). Na versão 2.6 parte deste código foi removida, como veremos no capítulo 9. Mesmo nas BIOS mais avançadas, esta rotina é semelhante, mesmo se executada sob algum tipo de emulação, pois sem ela nenhum sistema operacional para esta plataforma se inicia. Após a execução deste bloco, ele se move para o endereço 0x90000 (INITSEG), seta um stack em 0x9000:0x4000-12, com uma reserva para os parâmetros de disco, copia os parâmetros para esta localidade, termina de carregar outros setores contendo código para a memória, termina de capturar informações sobre o dispositivo que será o rootfs (a raiz do sistema) e imprime a mensagem “Loading”. Neste momento, ele começa a carregar o sistema para 0x100000 (SYSSEG) e, a cada trilha lida, imprime um “.” (ponto) até terminar, checando, então, o número do root device e pulando (jmp) para 0x90200 (SETUPSEG), que fica logo após o bootblock carregado anteriormente. Em linux/arch/i386/boot/setup.S temos o código para o primeiro reconhecimento de hardware, como tamanho da memória, adaptador de vídeo, HDs primário e secundário, além de checar um código ao fim do bloco de setup, para verificar a validade. Tudo conferido, checado e reconhecido, começa o processo de mudança do modo 8086 (modo real) para o modo protegido. São tomadas as medidas cabíveis de acordo com o processador (copiar tabelas de interrupção, ignorar interrupções e NMI, carregar o IDT e GDT e habilitar a linha A20 de memória), reprogramar as interrupções para os chips controladores e, finalmente, ligar o bit de modo protegido, pulando (jmp), em seguida, para o endereço KERNEl_CS:0x1000, que é a localidade onde o kernel foi colocado previamente. Capítulo 1• Características do Linux 19 Em linux/arch/i386/boot/compressed/head.S:startup_32, são efetuadas as checagens da linha A20, os registradores da CPU são limpos e é chamada a rotina decompress_kernel para descompactar e copiar o kernel para outro lugar (endereço 0x1000:0000 ou seja 1 Mb). Nota-se que o kernel mesmo após comprimido contém código para o boot, e só é descomprimido após todo o ambiente ter sido criado. Por esta descrição dá para perceber a complexidade gerada por um design equivocado. Tantas mudanças de endereço e cópia de trechos são necessários em decorrência da limitação de memória acessível e da impossibilidade de uso da BIOS em modo protegido por não ter código reentrante. 1.8 Linux para outros processadores Entre as várias arquiteturas suportadas pelo Linux, podem existir áreas em que uma não alcança a outra em termos técnicos. Por exemplo, as versões do kernel para a arquitetura baseada nos processadores Intel são a referência completa da implementação, enquanto a de PA-RISC ainda sofre da falta de drivers, dependendo do número de desenvolvedores interessados e com hardware disponível. Para processadores que possuem apenas o modo real, existe um antigo projeto chamado ELKS, o qual utiliza um compilador diferente do GCC chamado de BCC, especializado para sistemas baseados no 8086, de 16bits. Como outros projetos semelhantes baseados neste chip, seu estado é ainda imaturo e provavelmente abandonado. Existem versões previstas no Linux para o processador do videogame Dreamcast e plataformas como ARM e StrongARM (Pocket PCs). Para processadores maiores, além de Intel, é disponibilizado suporte para SPARC (Sun), MIPS (SGI), Alpha (Digital/Compac/HP), PA-RISC (HP), S/390 (IBM), PowerPC (Motorola/IBM) e 68K (Motorola). Vale a pena citar sistemas como o NetBSD (http://www.netbsd.org) que possuem versões em seu kernel para um sem-número de arquiteturas, algumas apenas regionais ou já extintas há muito tempo. Como curiosidade para o interessado em arquitetura de sistemas operacionais, o código é um prato cheio. 20 Programação Avançada em Linux 1.9 Linux como sistema em tempo real Sistemas em tempo real são usados em situações críticas que necessitam de exatidão em tarefas baseadas no tempo de sensoriamento, comutação e precisão em ferramentas pesadas ou sistemas delicados, como, por exemplo, controle de aeronaves e equipamentos de suporte vital encontrados em hospitais. Basicamente é o mesmo kernel, com APIs (Aplication Programming Interfaces funções específicas e interfaces de programação) voltadas para este tipo de aplicação, embora, às vezes, diferentes de padrões como um driver de porta serial privilegiando mais a precisão do que a compatibilidade com o padrão de drivers usado normalmente no Linux. Em muitos casos, como veremos no capítulo 9, é o mesmo kernel utilizado em uma estação de trabalho, sem adição de nenhum código extra, que é embutido em aplicações e aparelhos para atividades especializadas, apenas otimizado para economizar espaço. O projeto RTLinux é dedicado a criar extensões e recursos para a utilização do Linux em aplicações exigentes de tempo real. Uma camada que funciona como suporte entre o kernel do Linux e a CPU fornece as extensões necessárias para o controle de tempo e prioridades. O RTLinux empregado em sistemas médicos e de precisão absoluta, com um tempo de troca entre tarefas da ordem de nanossegundos. 1.10 Projeto uCLinux Este é uma versão do kernel para dispositivos baseados no processador da Motorola 68000 e também no MC68328, utilizado nos Palm Pilots e em inúmeras aplicações. O grupo responsável pelo projeto desenvolveu uma plataforma de hardware do tamanho de um pente de memória, completo com conexão Ethernet, portas paralelas e seriais e LCD. A maior diferença a ser notada é que esta versão não requer a presença de uma MMU (Memory Management Unit – Unidade de Gerenciamento da Memória) que pesa muito no desempenho e nas funções de alocação de memória. Capítulo 1• Características do Linux 21 Por exemplo, uma MMU pode trabalhar com memória virtual, por meio de hardware, enquanto emular uma MMU requer gasto de ciclos da CPU para gerenciar esta função. 1.11 Emulador para SCO e Solaris usando interrupções (LxRun) O projeto LxRun lança mão de um recurso muito interessante, que lembra muito as soluções utilizadas no DOS, e que veremos com mais detalhes a partir do capítulo 3. Ele funciona no SCO, OpenServer e Solaris para plataforma x86. Na realidade, trata-se de um módulo para o kernel destes sistemas, o qual mapeia uma interrupção de software dos processadores compatíveis com a família Intel, conhecida por int 0x80, que é a chamada das funções do usuário para o kernel (syscall). Com a interrupção de syscall instalada, as chamadas dos programas são redirecionadas para as chamadas correspondentes em seu próprio kernel. Portanto, o programa roda diretamente no sistema operacional, “acreditando” que está executando no Linux, sem mudanças nem necessidade de recompilação. Este conceito não é classificado como um emulador no sentido direto, mas sim como uma nova camada nestes sistemas, os quais usam o padrão iBCS (Intel Binary Compatibility System).