Organização do Sistema Operativo | Resumos LEIC-A
Constituição do Sistema Operativo
Iniciação de um Sistema Operativo
Micro-Núcleo vs Monolítico
O utilizador típico de um computador não é um programador com conhecimento profundo de como a memória, processos e outros recursos do computador funcionam, e no caso do utilizador o ser, este não necessita de controlar todos os aspetos da sua máquina, assim sendo o SO tem de apresentar certas características que facilitam muito o trabalho do programador.
O programador típico não deve precisar de se preocupar com os detalhes de comunicar com os vários periféricos (que podem estar ligados de n formas diferentes - SPI, I²C, etc. - ter m bugs que precisam de ser contornados, e w interfaces com paradigmas diferentes)
Estas abstrações podem ser mais ou menos complexas, e abstrações de vários níveis podem coexistir. P.ex. discos tanto são acedidos indiretamente por sistemas de ficheiros/mounts, como por ficheiros diretamente como block devices (deixando literalmente escrever todo o disco de início ao fim); interfaces de rede tanto podem ser geridas pelo SO e manipuladas como sockets como expostas diretamente a processos
O utilizador quer correr várias coisas, mas nem ele nem o programador se devem ter de preocupar com fazer isto funcionar - o SO abstrai a existência de outros processos a executar em concorrência e deixa o programador de aplicação usar o computador como se fosse o único processo a executar - todas as questões de sincronizar acessos a recursos partilhados são tratadas por SO.
O SO responsabiliza-se por controlar acessos aos vários recursos do computador (não deixa processos escrever na memória uns dos outros - excepto quando ambos concordam com shared memory, permissões do sistema de ficheiros). Desta forma, permitir que o utilizador típico de um computador tivesse acesso a estes recursos podia ser bastante destrutivo para a "segurança do computador". Desta forma, o Sistema Operativo deve ser uma interface de proteção dos recursos essenciais do computador.
Alem de que o SO deve garantir que:
os processos não interferem diretamente com recursos físicos do computador;
os processos não interferem uns com os outros.
Para responder a esta necessidade, qualquer Sistema Operativo distingue dois modos de execução:
Processos em modo utilizador têm acesso restrito a privilégios. Isto é, não conseguem aceder diretamente a qualquer endereço de memória, periféricos, ficheiros, etc (os recursos bloqueados em modo utilizador dependem do SO). Além de certas instruções do CPU que impactam o sistema globalmente serem restritas. A maioria das aplicações que correm no nosso computador estão em modo utilizador. O espaço de endereçamento acessível a um processo neste modo está limitado e por omissão deve ser disjunto do dos outros processos (para que estes não comuniquem entre si) a menos que estejam a usar memória partilhada. Podem aceder às suas variáveis e executar operações aritméticas, entre outros, mas estão proibidas de executar operações "perigosas" sobre os recursos físicos.
Quando um processo em modo utilizador pretende aceder a um recurso lógico, pede ao Sistema Operativo através de uma chamada de sistema. É da responsabilidade do SO aceder ao recurso pretendido de forma segura e oferecer ao processo a resposta.
Modo priveligiado em que o código do Sistema Operativo é executado, de forma a poder interagir diretamente com os recursos físicos. Podemos dizer por poucas palavras que o modo núcleo funciona como executor de chamadas sistema. Permite assim aos processos que correm em modo utilizador uma biblioteca de funções sistema que lhes permitam aceder a recursos físicos de forma segura.
Só o código do SO pode correr neste modo (de outra forma, estariamos a dar privilégios a processos que não os deviam ter).
Todas as atividades do modo núcleo são desencadeadas por exceções. Estas podem ser:
Assíncronas: provocadas por algo ortogonal ao programa em execução (exemplos: timer, falta de energia, etc). Estas excepções também são frequentemente designadas por interrupções;
Síncronas: provocadas por um acontecimento relacionado com o programa em execução (exemplos: divisão por zero, acesso a zona de memória proíbida, chamada de sistema, etc);
Quanto à origem, podemos classificar as interrupções como:
Desencadeadas por Hardware: periféricos (teclado, rato, etc);
Desencadeadas por Software (traps): para chamadas a funções de sistema;
Existem exceções que podem ser desencadeadas por Hardware ou Software:
Exceções de aritmética (divisão por zero) e acesso a memória indevido.
Informação Conflituosa
A informação nos últimos dois parágrafos contradiz-se mutualmente. É assim que é apresentada num dos recursos da cadeira e estamos à espera de clarificação dos professores.
O SO deve então oferecer às aplicações uma implementação segura de:
Input/output com periféricos;
Gestão de atividades paralelas dentro de um processo;
Comunicação pela rede;
Gráficos e gestão de janelas;
Exemplo: Interação com Periférico
Para exemplificar o que acontece quando um processo faz uma chamada de sistema, vamos ver o que acontece quando um processo quer fazer algo sobre um periférico:
Processo pede (através de syscall) ação sobre um periférico;
O núcleo (através do Gestor de Periféricos) envia pedido ao periférico e bloqueia o processo;
Quando o pedido for servido, o periférico lança uma interrupção;
Consequentemente, o núcleo (mais concretamente, o Gestor de Periféricos) é ativado, entrega a resposta ao processo e torna-o executável;
Interações Assíncronas
Enviar n pedidos ao SO por syscalls para fazer coisas com recurso, que não bloqueiam.
Fazer um pedido ao SO para bloquear até que uma ou todas essas n operações acabem;
Ou um primeiro pedido - p.ex. select
que bloqueia até que um ou mais dos n recursos em que se queira mexer esteja pronto para interações, e só depois mandar o pedido de leitura/escrita/etc a esse recurso, que nunca bloqueia: fazendo a operação, ou diz para tentar mais tarde
Quando o nosso computador está desligado, não passa de um objeto inanimado. Contudo, quando carregamos num botãozinho, esse objeto inanimado converte-se numa caixa mágica capaz de fazer as mais diversas operações. Vamos agora ver como é que isso é possível.
Quando uma máquina recebe energia, o PC (Program Counter) aponta para um programa (firmware) na Boot ROM. Nos computadores pessoais é habitual este programa ser uma implementação de BIOS (Basic Input/Output System) ou UEFI (Unified Extensible Firmware Interface). Este programa faz algumas verificações sobre o computador (nomeadamente se está em condições de ser iniciado) e, de seguida, determina a localização do bootloader (no limite definido estaticamente no firmware, mas tanto em BIOS como UEFI há uma "ordem de arranque" que diz em que sítios procurar e com que ordem), copia-o para RAM, e passa-lhe o controlo (salta)
O bootloader, por sua vez, carrega o programa do núcleo em RAM e salta para a rotina de inicialização do núcleo. A inicialização do núcleo passa por:
incializar as suas estruturas de dados;
copiar rotinas de tratamento de cada interrupção para RAM;
preencher a tabela de interrupções em RAM;
lançar os processos inicias do sistema, incluindo o processo de login;
O Sistema Operativo não passa de um pedaço (muito grande) de código. Consequentemente, a implementação do SO vem acompanhada das preocupações normais com projetos de código grandes. Nomeadamente, a organização interna do SO deve facilitar a sua manutenção e evolução.