Como os sistemas operacionais ... executam ... sem ter um sistema operacional para executar?


167

Estou realmente curioso agora. Eu sou um programador Python, e essa pergunta me surpreendeu: você escreve um sistema operacional. Como você executa isso? Tem que ser executado de alguma forma, e dessa forma está dentro de outro sistema operacional?

Como um aplicativo pode ser executado sem estar em um sistema operacional? Como você instrui o computador a executar, digamos, C, e executar esses comandos na tela, se ele não possui um sistema operacional para executar?

Isso tem a ver com um kernel UNIX? Se sim, o que é um kernel Unix ou um kernel em geral?

Tenho certeza que os sistemas operacionais são mais complicados do que isso, mas como isso funciona?


14
Tenho certeza de que é para isso que serve um BIOS - é um sistema operacional realmente pequeno que inicia a execução do sistema operacional maior.
sevenseacat

64
Um sistema operacional é conveniente , mas você não precisa de um para executar programas em um computador.
Andres F.

10
É perfeitamente possível escrever software que não seja do sistema operacional sem um sistema operacional. Muitos intérpretes da Forth tradicionalmente rodavam sem um sistema operacional (ou você poderia dizer que eram sistemas operacionais). Nem é tão difícil assim. Se você conhece C, pode gostar de escrever um programa como esse (um jogo pequeno, talvez) como um exercício de aprendizado.
Max

44
Essa confusão é um dos custos dos sistemas de computação maravilhosos, seguros e altamente abstratos que usamos hoje em dia: as pessoas podem ser programadores muito bons e competentes e nem conhecem os fundamentos sobre como o computador funciona. Quão baixo você quer ir? Para muito baixo, mas ainda acima da física, consulte Como os primeiros microprocessadores foram programados? em Electronics.SE.
dmckee

2
A programação foi feita antes da invenção do atual conceito de SO. Obviamente, algo nesse nível é o que inicia o sistema operacional. Os sistemas operacionais são inicializados. Isso geralmente é mencionado pelo menos em um programa de CS de 4 anos em algum momento, já que a maioria exige um curso de teoria dos computadores sobre sistemas operacionais.
Rig

Respostas:


263

Existem muitos sites que passam pelo processo de inicialização (como Como os computadores são inicializados ). Em poucas palavras, é um processo de vários estágios que continua construindo o sistema um pouco de cada vez, até que possa finalmente iniciar os processos do SO.

Começa com o firmware na placa-mãe que tenta colocar a CPU em funcionamento. Em seguida, ele carrega o BIOS, que é como um mini sistema operacional que coloca o outro hardware em funcionamento. Feito isso, ele procura um dispositivo de inicialização (disco, CD, etc) e, uma vez encontrado, localiza o MBR (registro mestre de inicialização) e o carrega na memória e o executa. É esse pequeno pedaço de código que sabe como inicializar e iniciar o sistema operacional (ou outros gerenciadores de inicialização, à medida que as coisas ficam mais complicadas). É nesse ponto que coisas como o kernel serão carregadas e começarão a ser executadas.

É incrível que funcione!


108
+1 para a frase final.
um CVn

39
Há uma razão para isso ser chamado de "inicialização"; o termo é abreviação de "bootstrapping", como em "puxando-se pelos seus bootstraps".
Keiths

5
Houve um tempo em que alguém precisou digitar ou alternar no código de inicialização. Às vezes, era um simples salto para a primeira instrução de um programa em ROM. Outras vezes, era código ler de um dispositivo e pular para a primeira instrução do programa nos dados lidos. As coisas estão muito mais simples agora.
BillThor


15
@ BillThor: Onde por "muito mais simples" você, é claro, quer dizer "muito mais complicado". Eles são apenas mais simples de usar .
Raphael Schweikert

173

Um sistema operacional "bare metal" não roda em nada. Ele executa o conjunto completo de instruções na máquina física e tem acesso a toda a memória física, a todos os registros de dispositivos e a todas as instruções privilegiadas, incluindo aquelas que controlam o hardware de suporte à memória virtual.

(Se o sistema operacional estiver sendo executado em uma máquina virtual, é possível que esteja na mesma situação acima. A diferença é que certas coisas são emuladas ou de alguma outra forma manipuladas pelo hipervisor; ou seja, o nível que executa as máquinas virtuais .)

De qualquer forma, embora o sistema operacional possa ser implementado em (por exemplo) C, ele não terá todas as bibliotecas C normais disponíveis. Em particular, ele não terá as bibliotecas 'stdio' normais. Em vez disso, implementará (por exemplo) um driver de dispositivo de disco que permita ler e gravar blocos de disco. Ele implementará um sistema de arquivos na parte superior da camada de bloco de disco e, além disso, implementará as chamadas do sistema que as bibliotecas de tempo de execução de um aplicativo de usuário fazem para (por exemplo) criar, ler e gravar arquivos ... e assim por diante.

Como um aplicativo pode ser executado sem estar em um sistema operacional?

Ele precisa ser um tipo especial de aplicativo (por exemplo, um sistema operacional) que saiba interagir diretamente com o hardware de E / S, etc.

Como você instrui o computador a executar, digamos, C, e executar esses comandos na tela, se ele não possui um sistema operacional para executar?

Você não

O aplicativo (que era para fins de argumento escrito em C) é compilado e vinculado em alguma outra máquina para fornecer uma imagem de código nativa. Em seguida, a imagem é gravada no disco rígido em um local que o BIOS possa encontrá-la. O BIOS carrega a imagem na memória e executa uma instrução para pular para o ponto de entrada do aplicativo.

Geralmente, não existe "C e execução de comandos" no aplicativo, a menos que seja um sistema operacional completo. E, nesse caso, é responsabilidade do sistema operacional implementar toda a infraestrutura necessária para que isso aconteça. Nenhuma mágica. Apenas muito código.

A resposta de Bill cobre a inicialização, que é o processo no qual você passa de uma máquina desligada para uma máquina na qual o sistema operacional normal está em funcionamento. No entanto, é importante notar que, quando o BIOS conclui suas tarefas, ele (normalmente) cede o controle completo do hardware ao sistema operacional principal e não desempenha outra parte - até a próxima reinicialização do sistema. O sistema operacional principal certamente não está sendo executado "dentro" do BIOS no sentido convencional.

Isso tem a ver com um kernel UNIX? Se sim, o que é um kernel unix ou um kernel em geral?

Sim.

O kernel UNIX é o núcleo do sistema operacional UNIX. É a parte do UNIX que faz todo o material "bare metal" descrito acima.

A idéia de um "kernel" é que você tenta separar o software do sistema em itens essenciais (que requerem acesso ao dispositivo físico, toda a memória, etc.) e itens não essenciais. O kernel consiste no material principal.

Na realidade, a distinção entre kernel / core e não-kernel / non-core é mais complicada do que isso. E tem havido muito debate sobre o que realmente pertence a um kernel e o que não pertence. (Procure o micro-kernel, por exemplo.)


6
Resposta fenomenal. Eu daria a você vários outros votos, se possível.
weberc2

7
Muitas respostas boas aqui, então adicionarei isso como um comentário, já que ninguém mencionou isso até agora: um dos principais recursos de um sistema operacional é permitir que vários aplicativos sejam executados "simultaneamente" da perspectiva do usuário. A capacidade de agendar processos e proteger os processos de alterar o comportamento uns dos outros, normalmente são recursos encontrados apenas em um sistema operacional, e não em firmware ou BIOS.
23612 Sean Bareau

2
The idea of a "kernel" is that you try to separate the system software into core stuffFácil de lembrar, observando que o termo kernelé do alemão Kern, que significa núcleo / núcleo.
deed02392

1
Amo essa resposta aqui porque ele menciona que ele é compilado e vinculado código binário em execução não C.
Travis Pessetto

3
"Ficar longe disso tornou os usuários de PC menos inteligentes". - Não menos inteligente ... menos com conhecimentos de informática. Você também pode dizer que aumentou o número de usuários de PC.
Stephen C

62

No começo, não havia energia na CPU.

E o Homem disse "deixe que haja energia", e a CPU começou a ler de um determinado endereço na memória e executar a instrução que estava presente lá. Depois, o próximo e assim por diante até o fim do poder.

Esta foi a inicialização. Sua tarefa era carregar outro software para obter acesso ao ambiente, onde estava o software principal, e carregá-lo.

Por fim, uma tela amigável convidou você para fazer logon.


58
Esta resposta deve ser movida para christianity.stackexchange.com
Coomie 24/10/12

6
o que é "um determinado endereço" de onde vem. Desculpe por jogar Charles Darwin aqui.
Midhat

7
@ Midhat - O primeiro endereço a ser buscado pela CPU está conectado dentro dele. Normalmente é 0.
mouviciel 24/10/12

17
... E no sétimo dia, o Homem descansou com seus jogos
Luke canadense

9
@mouviciel O endereço na memória é 0x7C00para qualquer x86arquitetura compatível e primeiro tem de ser preenchido pelo BIOS, que normalmente carrega o primeiro setor do dispositivo qualquer que ele prefere arranque ... resposta agradável embora: -7
Tobias KIENZLER

29

Desculpe o atraso, mas vou descrevê-lo da seguinte maneira:

  • A placa-mãe recebe energia.

  • Os circuitos de temporização iniciam e estabilizam, se necessário, com base apenas em suas características elétricas. Alguns dispositivos mais recentes podem realmente usar um microprocessador ou seqüenciador muito limitado.

    Deve-se notar que muitas coisas como "os circuitos de temporização são iniciados e estabilizados, se necessário", não acontecem mais no hardware. Uma quantidade enorme desse trabalho é realmente um software extremamente especializado, executado em subprocessadores / seqüenciadores muito limitados.

    - jkerian às 5:20 de 25 de outubro

  • A energia é dada à CPU e à RAM.

  • A CPU carrega (com base na fiação interna) dados do BIOS. Em algumas máquinas, o BIOS pode ser espelhado na RAM e depois executado a partir daí, mas isso é raro IIRC.

    Quando ativadas, as CPUs compatíveis com x86 iniciam no endereço 0xFFFFFFF0 no espaço de endereço ...

    -Micheal Steil, 17 erros cometidos pela Microsoft no sistema de segurança Xbox ( arquivo )

  • O BIOS faz chamadas para as portas e endereços de hardware usados ​​pela placa-mãe para E / S de disco e outros hardwares e gira discos, coloca o restante da RAM funcionando, entre outras coisas.

  • O código do BIOS (por meio das configurações do CMOS, armazenadas no hardware) usa comandos IDE ou SATA de baixo nível para ler o setor de inicialização de cada disco, na ordem especificada pelo CMOS ou pela substituição de um usuário por um menu.

  • O primeiro disco com um setor de inicialização executa seu setor de inicialização. Esse setor de inicialização é um Assembly que possui instruções para carregar mais dados do disco, carregar um maior NTLDR, estágios posteriores GRUB, etc.

  • Por fim, o código da máquina do SO é executado pelo carregador de inicialização, direta ou indiretamente, através do carregamento em cadeia, carregando um setor de inicialização a partir de um local alternativo ou offset.

Você então sente um pânico amigável no kernel, um pinguim sufocado ou seu disco pára devido a um choque na cabeça. =) No cenário alternativo, seu kernel configura tabelas de processo, estruturas na memória e monta discos, carregando drivers, módulos e uma GUI ou conjunto de serviços (se estiver em um servidor). Em seguida, os programas são executados à medida que seus cabeçalhos são lidos e seu assembly é trazido para a memória e mapeado de acordo.


2
Deve-se notar que muitas coisas como "os circuitos de temporização são iniciados e estabilizados, se necessário", não acontecem mais no hardware. Uma quantidade enorme desse trabalho é realmente um software extremamente especializado, executado em subprocessadores / seqüenciadores muito limitados. - Um engenheiro de firmware de bairro amigável
jkerian

@jkerian Você se importa que eu citei seu comentário no meu post?
ζ--

heh, de jeito nenhum.
fácil

BIOS não é um sistema operacional. O BIOS é uma abreviação de Basic Input / Output System, e é isso que o BIOS faz. Ele permite que os programadores usem recursos de baixo nível com drivers fornecidos pelo fabricante. Quando o SO entra no modo Protegido (32 bits) ou Longo (64 bits), o BIOS não está mais disponível e o SO usa seus próprios drivers que basicamente substituem a funcionalidade fornecida pelo BIOS em níveis "inferiores". Os sistemas operacionais modernos, por exemplo, Linux e Windows, usam o BIOS apenas para detectar seções de RAM utilizáveis ​​e carregar seu próprio carregador mais avançado que pode carregar os drivers necessários.
Hannes Karppila 23/02

1
@HannesKarppila Atualizado; agora ele tem cerca de quatro anos e não estou mais ativo neste site.
ζ--

15

Há muitas boas respostas, mas eu gostaria de acrescentar: Você mencionou que é originário de um Python. Python é uma linguagem não interpretada (ou "interpilada" ou qualquer outra coisa, pelo menos nos casos de uso típicos do CPython). Isso significa que você tem algum outro software (o interpretador Python) olhando a fonte e executando-a de alguma maneira. Este é um modelo fino e permite linguagens de alto nível bastante agradáveis, abstraídas do hardware real. A desvantagem é que você sempre precisa desse software de intérprete primeiro.

Esse software de intérprete, normalmente, é escrito em uma linguagem que é compilada no código da máquina, por exemplo, C ou C ++. Código de máquina é o que a CPU pode suportar. O que uma CPU pode fazer é ler alguns bytes da memória e, dependendo dos valores dos bytes, iniciar uma operação específica. Portanto, uma sequência de bytes é um comando para carregar alguns dados da memória em um registro, outra sequência para adicionar dois valores, outra para armazenar o valor de um registro na memória principal e em breve (um registro é uma área de memória especial que faz parte da cpu onde pode funcionar melhor), a maioria desses comandos é bastante baixa nesse nível. O legível para humanos para essas instruções de código de máquina é o código do montador. Esse código de máquina é basicamente o que é armazenado nos arquivos .exe ou.com no Windows ou nos binários Linux / Unix.

Agora, se um computador é iniciado, ele é burro, mas ele possui alguma fiação, a qual lerá essas instruções de código de máquina. Em um PC, normalmente (atualmente) é um chip EEPROM na placa principal que contém o BIOS (sistema de saída básica de entrada), esse sistema não pode fazer muito, pode facilitar o acesso a alguns hardwares etc. e, em seguida, executar uma operação importante: vá para o inicialize e copie os primeiros bytes (também conhecido como registro mestre de inicialização, MBR) na memória e diga à CPU "aqui, aqui está o seu programa"; a CPU tratará esses bytes como código de máquina e executará. Normalmente, este é um carregador de sistema operacional que carrega o kernel com alguns parâmetros e, em seguida, transfere o controle para o kernel, que carrega todos os seus drivers para acessar todo o hardware, carrega algum programa de desktop ou shell ou qualquer outra coisa e permite que o usuário faça o login e use o sistema


6
"interpilado"? Eu nunca ouvi esse termo antes.
Bryan Oakley

3
Esse termo foi usado há quase cinco anos, para descrever intérpretes "modernos" que possuem uma fase de compilação distinta e separada da execução. Não faço ideia se este termo sobreviveu qualquer lugar ;-)
Johannes

1
"não interpretado"? Eu nunca ouvi esse termo antes.
Cole Johnson

12

Você pergunta "Como um aplicativo pode ser executado sem estar em um sistema operacional". A resposta fácil é "um sistema operacional não é um aplicativo". Embora um sistema operacional possa ser criado com as mesmas ferramentas que um aplicativo e feito da mesma matéria-prima, eles não são a mesma coisa. Um sistema operacional não precisa jogar pelas mesmas regras que um aplicativo.

OTOH, você pode pensar no hardware e no firmware reais como o "SO" no qual o "aplicativo" do SO é executado. O hardware é um sistema operacional muito simples - ele sabe como executar instruções escritas em código de máquina e sabe que, quando inicializado, deve procurar um endereço de memória muito específico para sua primeira instrução. Portanto, ele inicia e executa imediatamente a primeira instrução, seguida pela segunda e assim por diante.

Portanto, o sistema operacional é simplesmente um código de máquina que existe em um local conhecido e que pode interagir diretamente com o hardware.


1
+1 Acho que esta é a melhor resposta. Em termos de abstração, acho que você está acertando nos níveis certos.
Preet Sangha

6

A resposta à sua pergunta requer o conhecimento de como o código nativo (para CPU) se parece e como é interpretado pela CPU.

Normalmente, todo o processo de compilação é baseado na tradução de coisas que você escreve em C, Pascal ou até Python (usando pypy) e C # em coisas que a CPU entende, isto é, instruções simples como "armazenar algo em [endereço de memória]", "adicionar números armazenados em registros eax e ebx "," chame a função foo "," compare eax a 10 ". Essas instruções, executadas uma a uma, fazem o que você queria fazer com seu código.

Agora pense sobre isso: você realmente não precisa de um sistema operacional para executar este código nativo! Tudo o que você precisa é carregar esse código na memória e informar à CPU que está lá e você deseja que ele seja executado. Não se preocupe muito com isso, no entanto. Esse é o trabalho que o BIOS deve se preocupar - ele carrega seu código (apenas um setor), logo após o início da CPU, no endereço físico 0x7C00. A CPU começa a executar esse setor (512 B) do seu código. E você pode fazer o que você imaginar! Sem, é claro, qualquer suporte do sistema operacional. Isso porque você é o sistema operacional. Legal né? Sem biblioteca padrão, sem impulso, sem python, sem programas, sem drivers! Você tem que escrever tudo sozinho.

E como você se comunica com o hardware? Bem, você tem duas opções:

  1. Você fica dentro do "Modo Real" - modo de execução da CPU com apenas 1 MB de memória (e até menos), sem recursos avançados de CPU, como extensões de CPU, proteção de memória, multitarefa; Código executável de 16 bits, modo de endereçamento antigo ... Mas com algumas rotinas fornecidas pelo BIOS, incluindo saída de tela simples, suporte ao teclado, E / S de disco e gerenciamento de energia. Em uma palavra, você voltou aos tempos de MS-DOS e CPUs de 16 bits.
  2. Você entra em "Modo protegido" com todos os recursos que sua CPU possui, toda a memória que você instalou e assim por diante. Porém, no Modo Protegido, você ESTÁ completamente sozinho e precisa fazer TUDO sozinho (e se comunica com o hardware usando as instruções "in" e "out") para entrada / saída de dados nas portas de E / S e interrupções. / O). Preciso dizer que todos os sistemas operacionais desde o Windows 95 e o primeiro Linux escolhem essa opção?

Agora você está perguntando o que é o kernel. Logo, o kernel é tudo o que você não vê e experimenta diretamente. Ele gerencia, juntamente com os drivers, tudo, desde o teclado até quase todas as peças de hardware do seu PC. Você se comunica com ele por shell ou terminal gráfico. Ou por funções dentro do seu código, agora executadas, felizmente, com suporte do sistema operacional.

Para uma melhor compreensão, posso lhe dar um conselho: tente escrever seu próprio sistema operacional. Mesmo que ele escreva "Hello world" na tela.


3

Existem algumas diferenças na maneira como um sistema operacional opera que são extremamente dependentes do sistema. Para ser útil, um sistema precisa ter algum comportamento previsível na inicialização, como "iniciar a execução no endereço X". Para sistemas que possuem armazenamento não volátil (como memória Flash) mapeado em seu espaço de programa, isso é bastante fácil, pois você apenas coloca o código de inicialização no local correto no espaço de programa do processador. Isso é extremamente comum para microcontroladores. Alguns sistemas precisam recuperar seus programas de inicialização de outro local antes de executá-lo. Esses sistemas terão algumas operações com fio (ou quase com fio) neles. Existem alguns processadores que recuperam seu código de inicialização via i2c de outro chip,

Os sistemas que usam a família de processadores x86 geralmente usam um processo de inicialização em vários estágios que é bastante complexo devido a sua evolução e problemas de compatibilidade com versões anteriores. O sistema executa algum firmware (chamado BIOS - Basic Input / Output System, ou similar) que está em alguma memória não volátil da placa-mãe. Às vezes, parte ou todo esse firmware é copiado (realocado) para a RAM para torná-lo mais rápido. Este código foi escrito com o conhecimento de qual hardware estaria presente e utilizável na inicialização.

O firmware de inicialização geralmente é escrito com suposições sobre qual hardware estará presente no sistema. Anos atrás, em uma máquina 286, provavelmente haveria uma suposição de que haveria um controlador de unidade de disquete no endereço de E / S X e carregaria o setor 0 em um determinado local de memória se determinado conjunto de comandos (e o código no setor 0 sabe como usar as próprias funções do BIOS para carregar mais código e, eventualmente, código suficiente para carregar um sistema operacional). Em um microcontrolador, pode-se supor que exista uma porta serial operando com determinadas configurações que ela deve esperar pelos comandos (para atualizar firmware mais complexo) por um período de tempo X antes de continuar com o processo de inicialização.

O processo exato de inicialização de um determinado sistema não é tão importante para você quanto saber que ele difere em sistemas diferentes, mas também que todos eles têm coisas em comum. Geralmente, no código de inicialização (inicialização), quando é necessário executar a E / S, os dispositivos de E / S são pesquisados ​​em vez de depender de interrupções. Isso ocorre porque as interrupções são complexas, use a pilha RAM (que ainda não está totalmente configurada) e você não precisa se preocupar em bloquear outras operações quando for a única operação.

Ao ser carregado pela primeira vez, o kernel do sistema operacional (o kernel é a parte principal da maioria dos sistemas operacionais) atuará inicialmente como o firmware. Ele precisará ser programado com o conhecimento ou descobrir o hardware presente, configurar alguma RAM como espaço de pilha, fazer vários testes, configurar várias estruturas de dados, possivelmente descobrir e montar um sistema de arquivos e, provavelmente, iniciar algum programa que seja mais como os programas que você está acostumado a escrever (um programa que conta com a presença de um sistema operacional).

O código do SO geralmente é escrito em uma mistura de C e montagem. O primeiro código para o kernel do sistema operacional provavelmente está sempre em montagem e faz coisas como configurar a pilha, na qual o código C se baseia, e depois chama uma função C. Outra montagem escrita à mão também estará lá porque algumas operações que um sistema operacional precisa realizar geralmente não são expressáveis ​​em C (como pilhas de troca / troca de contexto). Freqüentemente, sinalizadores especiais precisam ser passados ​​para o compilador C para dizer a ele para não confiar nas bibliotecas padrão usadas pela maioria dos programas em C e para não esperar que exista umint main(int argc, char *argv[])no programa. Além disso, opções especiais de vinculador que a maioria dos programadores de aplicativos nunca usa precisam ser usadas. Isso pode fazer com que o programa do kernel espere ser carregado em um determinado endereço ou configure coisas para parecer que existem variáveis ​​externas em determinados locais, embora essas variáveis ​​nunca tenham sido declaradas em nenhum código C (isso é útil para E / S mapeada na memória ou outros locais especiais de memória).

Toda a operação parece mágica no começo, mas depois que você a examina e entende partes dela, a mágica se torna apenas um conjunto de programas que exigem muito mais planejamento e conhecimento do sistema para serem implementados. Depurá-los, no entanto, requer mágica.


3

Para entender como os sistemas operacionais funcionam, pode ser útil dividi-los em duas categorias: aqueles que simplesmente fornecem serviços aos aplicativos mediante solicitação e aqueles que usam recursos de hardware na CPU para impedir que os aplicativos façam coisas que não deveriam. O MS-DOS era do estilo anterior; todas as versões do Windows desde a versão 3.0 têm o último estilo (pelo menos ao executar algo mais poderoso que um 8086).

O IBM PC original executando o PC-DOS ou MS-DOS teria sido um exemplo do estilo antigo de "OS". Se um aplicativo desejasse exibir um personagem na tela, haveria algumas maneiras de fazê-lo. Pode chamar a rotina que solicitaria ao MS-DOS para enviá-la para "saída padrão". Se isso acontecesse, o MS-DOS verificaria se a saída estava sendo redirecionada e, se não, chamaria uma rotina armazenada na ROM (em uma coleção de rotinas que a IBM denominou Sistema Básico de Entrada / Saída) que exibirá um caractere no posição do cursor e mova o cursor ("escrever teletipo"). Essa rotina do BIOS armazenaria um par de bytes em algum lugar no intervalo 0xB800: 0 a 0xB800: 3999; o hardware no adaptador gráfico colorido buscará repetidamente pares de bytes dentro desse intervalo, usando o primeiro byte de cada par para selecionar uma forma de caractere e o segundo para selecionar as cores de primeiro e segundo plano. Os bytes são buscados e processados ​​em sinais vermelhos, verdes e azuis, em uma sequência que produz uma exibição de texto legível.

Os programas no PC IBM podem exibir texto usando a rotina de "saída padrão" do DOS ou a rotina de "gravação teletipo" do BIOS ou armazenando-a diretamente para exibir a memória. Muitos programas que precisavam exibir muito texto optaram rapidamente pela última abordagem, pois ela pode ser literalmente centenas de vezes mais rápida que usar as rotinas do DOS. Isso não ocorreu porque as rotinas de DOS e BIOS eram excepcionalmente ineficientes; a menos que a tela esteja em branco, ela só poderá ser gravada em determinados momentos. A rotina do BIOS para gerar um caractere foi projetada para poder ser chamada a qualquer momento; portanto, cada solicitação teve que começar novamente, aguardando o momento certo para executar uma operação de gravação. Por outro lado, o código do aplicativo que sabia o que precisava fazer poderia se organizar em torno das oportunidades disponíveis para gravar a exibição.

Um ponto importante aqui é que, embora o DOS e o BIOS forneçam um meio de enviar texto para a tela, não havia nada de particularmente "mágico" nessas habilidades. Um aplicativo que desejasse escrever texto no monitor poderia fazê-lo com a mesma eficácia, pelo menos se o hardware do monitor funcionasse da maneira esperada pelo aplicativo (se alguém tivesse instalado um adaptador de monitor monocromático, semelhante ao CGA, mas com memória de caracteres localizado em 0xB000: 0000-0xB000: 3999), o BIOS geraria automaticamente caracteres ali; um aplicativo programado para funcionar com o MDA ou o CGA também poderia fazê-lo, mas um aplicativo programado apenas para o CGA seria totalmente inútil no MDA).

Em sistemas mais novos, as coisas são um pouco diferentes. Os processadores têm vários modos de "privilégio". Eles começam no modo mais privilegiado, onde o código pode fazer o que quiser. Eles podem alternar para um modo restrito, onde apenas intervalos de memória selecionados ou recursos de E / S estão disponíveis. O código não pode alternar diretamente de um modo restrito para o modo privilégio, mas o processador definiu pontos de entrada no modo privilegiado e o código no modo restrito pode solicitar ao processador que comece a executar o código em um desses pontos de entrada no modo privilegiado. Além disso, existem pontos de entrada no modo privilegiado associados a várias operações que seriam proibidas no modo restrito. Suponha, por exemplo, que alguém queira executar vários aplicativos do MS-DOS simultaneamente, cada um com sua própria tela. Se os aplicativos pudessem gravar diretamente no controlador de vídeo em 0xB800: 0, não haveria maneira de impedir que um aplicativo substituísse a tela de outro aplicativo. Por outro lado, um sistema operacional pode executar o aplicativo no modo restrito e interceptar todos os acessos à memória do monitor; se descobrisse que um aplicativo que deveria estar em segundo plano tentando gravar 0xB800: 160, poderia armazenar os dados em alguma memória que havia reservado como um buffer de tela de aplicativo em segundo plano. Se esse aplicativo for posteriormente alternado para o primeiro plano, o buffer poderá ser copiado para a tela real. um sistema operacional pode executar o aplicativo no modo restrito e interceptar todos os acessos à memória do monitor; se descobrisse que um aplicativo que deveria estar em segundo plano tentando gravar 0xB800: 160, poderia armazenar os dados em alguma memória que havia reservado como um buffer de tela de aplicativo em segundo plano. Se esse aplicativo for posteriormente alternado para o primeiro plano, o buffer poderá ser copiado para a tela real. um sistema operacional pode executar o aplicativo no modo restrito e interceptar todos os acessos à memória do monitor; se descobrisse que um aplicativo que deveria estar em segundo plano tentando gravar 0xB800: 160, poderia armazenar os dados em alguma memória que havia reservado como um buffer de tela de aplicativo em segundo plano. Se esse aplicativo for posteriormente alternado para o primeiro plano, o buffer poderá ser copiado para a tela real.

As principais coisas a serem observadas são (1) embora muitas vezes seja útil ter um conjunto padrão de rotinas para executar vários serviços padrão, como exibir texto, eles não fazem nada que um aplicativo que estava sendo executado no "modo privilegiado" não poderia fazer se foi programado corretamente para lidar com o hardware que foi instalado; (2) embora a maioria dos aplicativos em execução hoje seja impedida pelo sistema operacional de executar essas E / S diretamente, um programa que é iniciado no modo privilegiado pode fazer o que quiser e pode configurar as regras que deseja para o modo restrito programas.


2

Como Stephen C. disse, não se trata apenas de iniciar o sistema operacional, mas também de como ele roda, interage com o hardware e com o software em cima dele.

Vou acrescentar à sua resposta que você pode dar uma olhada em "Os elementos dos sistemas de computação" . É um livro e algumas ferramentas que explicam como um computador, sistema operacional e compiladores interagem. O único aspecto disso é que ele fornece as ferramentas para desenvolver rapidamente seu próprio sistema operacional em um ambiente simulado, ignorando os muitos detalhes necessários para um real, para que você possa entender os conceitos . Ele faz um ótimo trabalho em deixar você ver a floresta em vez das árvores.

Se você quiser entrar em mais detalhes sobre como o sistema operacional interage com o hardware, consulte o Minix .


1

Você escreve um sistema operacional. Tem que ser executado de alguma forma, e dessa forma está dentro de outro sistema operacional?

Seu aplicativo está sendo executado em um sistema operacional. Este sistema operacional fornece serviços ao seu aplicativo, como abrir um arquivo e escrever bytes nele. Esses serviços geralmente são fornecidos por meio de chamadas do sistema.

O sistema operacional está sendo executado no hardware. O hardware fornece serviços ao sistema operacional, como definir a taxa de transmissão de uma porta serial e gravar bytes nela. Esses serviços geralmente são fornecidos por meio de registradores mapeados na memória ou portas de E / S.


Para dar um exemplo muito simplificado de como isso funciona:

Seu aplicativo diz ao sistema operacional para gravar algo em um arquivo. Para o seu aplicativo, o sistema operacional fornece conceitos como arquivos e diretórios.

No hardware, esses conceitos não existem. O hardware fornece conceitos como discos divididos em blocos fixos de 512 bytes. O sistema operacional decide quais blocos usar para o seu arquivo e alguns outros blocos para os metadados, como o nome, o tamanho e o local do disco. Em seguida, informa ao hardware: grave esses 512 bytes no setor com esse número no disco com esse número; escreva esses outros 512 bytes no setor com esse número diferente no disco com o mesmo número; e assim por diante.

A maneira como o sistema operacional instrui o hardware a fazer isso varia muito. Uma das funções de um sistema operacional é proteger os aplicativos dessas diferenças. Para o exemplo do disco, em um tipo de hardware, o sistema operacional precisaria gravar o número do disco e do setor em uma porta de E / S e, em seguida, gravar os bytes um a um em uma porta de E / S separada. Em outro tipo de hardware, o sistema operacional teria que copiar os 512 bytes inteiros de um setor em uma área de memória, gravar o local dessa área de memória em um local de memória especial e gravar o número do disco e do setor em outro local de memória especial.


O hardware de ponta de hoje é extremamente complicado. Os manuais que fornecem todos os detalhes de programação são batentes de porta com milhares de páginas; por exemplo, o mais recente manual da CPU Intel é de sete volumes, com um total de mais de 4000 páginas - e isso é apenas para a CPU. A maioria dos outros componentes expõe blocos de memória ou portas de E / S, que o sistema operacional pode solicitar à CPU para mapear para endereços dentro de seu espaço de endereço. Vários desses componentes expõem ainda mais coisas atrás de algumas portas de E / S ou endereços de memória; como exemplo, o RTC (Real Time Clock, o componente que mantém o tempo do computador desligado) expõe algumas centenas de bytes de memória atrás de um par de portas de E / S, e esse é um componente muito simples que remonta a o PC / AT original. Coisas como discos rígidos têm processadores separados inteiros, com o qual o sistema operacional se comunica por meio de comandos padronizados. GPUs são ainda mais complicadas.

Várias pessoas nos comentários acima sugeriram o Arduino. Concordo com eles, é muito mais simples de entender - o ATmega328, que faz tudo no Arduino Uno, exceto a exposição do conector USB como porta serial, possui um manual com apenas algumas centenas de páginas. No Arduino, você roda diretamente no hardware, sem sistema operacional no meio; apenas algumas pequenas rotinas de biblioteca, que você não precisará usar se não quiser.


1

Exemplos executáveis

Tecnicamente, um programa que é executado sem um sistema operacional é um sistema operacional. Então, vamos ver como criar e executar alguns SOs minúsculos do Hello World.

O código de todos os exemplos abaixo está presente neste repositório do GitHub .

Setor de inicialização

No x86, a coisa mais simples e de nível mais baixo que você pode fazer é criar um MBR (setor de inicialização mestre) , que é um tipo de setor de inicialização , e depois instalá-lo em um disco.

Aqui, criamos um com uma única printfchamada:

printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img

Resultado:

insira a descrição da imagem aqui

Testado no Ubuntu 18.04, QEMU 2.11.1.

main.img contém o seguinte:

  • \364in octal == 0xf4in hex: a codificação de uma hltinstrução, que diz à CPU para parar de funcionar.

    Portanto, nosso programa não fará nada: apenas inicie e pare.

    Usamos octal porque os \xnúmeros hexadecimais não são especificados pelo POSIX.

    Poderíamos obter essa codificação facilmente com:

    echo hlt > a.asm
    nasm -f bin a.asm
    hd a
    

    mas a 0xf4codificação também está documentada no manual da Intel, é claro.

  • %509sproduzir 509 espaços. Necessário preencher o arquivo até o byte 510.

  • \125\252em octal == 0x55seguido por 0xaa: bytes mágicos requeridos pelo hardware. Eles devem ser bytes 511 e 512.

    Se não estiver presente, o hardware não tratará isso como um disco inicializável.

Observe que, mesmo sem fazer nada, alguns caracteres já estão impressos na tela. Esses são impressos pelo firmware e servem para identificar o sistema.

Executar em hardware real

Os emuladores são divertidos, mas o hardware é o verdadeiro negócio.

Observe, no entanto, que isso é perigoso e você pode limpar seu disco por engano: faça isso apenas em máquinas antigas que não contêm dados críticos! Ou melhor ainda, devboards como o Raspberry Pi, veja o exemplo do ARM abaixo.

Para um laptop típico, você precisa fazer algo como:

  • Grave a imagem em um dispositivo USB (destruirá seus dados!):

    sudo dd if=main.img of=/dev/sdX
    
  • conecte o USB ao computador

  • ligue

  • diga para inicializar a partir do USB.

    Isso significa fazer com que o firmware escolha USB antes do disco rígido.

    Se esse não é o comportamento padrão da sua máquina, continue pressionando Enter, F12, ESC ou outras teclas estranhas após a inicialização, até que você obtenha um menu de inicialização onde você pode selecionar para inicializar a partir do USB.

    Geralmente, é possível configurar a ordem de pesquisa nesses menus.

Por exemplo, no meu antigo Lenovo Thinkpad T430, UEFI BIOS 1.16, posso ver:

Olá Mundo

Agora que criamos um programa mínimo, vamos para o mundo olá.

A pergunta óbvia é: como fazer IO? Algumas opções:

  • peça ao firmware, por exemplo, BIOS ou UEFI, que faça por nós
  • VGA: região de memória especial que é impressa na tela se gravada. Pode ser usado no modo protegido.
  • escreva um driver e fale diretamente com o hardware da tela. Esta é a maneira "correta" de fazê-lo: mais poderoso, mas mais complexo.
  • porta serial . Este é um protocolo padronizado muito simples que envia e recupera caracteres de um terminal host.

    Fonte .

    Infelizmente, ele não está exposto na maioria dos laptops modernos, mas é o caminho mais comum para as placas de desenvolvimento, veja os exemplos de ARM abaixo.

    Isso é realmente uma pena, pois essas interfaces são realmente úteis para depurar o kernel do Linux, por exemplo .

  • use recursos de depuração de chips. O ARM chama de semi - hospedagem deles, por exemplo. Em hardware real, requer algum suporte extra de hardware e software, mas em emuladores pode ser uma opção conveniente e gratuita. Exemplo .

Aqui vamos fazer um exemplo de BIOS, pois é mais simples no x86. Mas observe que este não é o método mais robusto.

main.S

.code16
    mov $msg, %si
    mov $0x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int $0x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"

link.ld

SECTIONS
{
    . = 0x7c00;
    .text :
    {
        __start = .;
        *(.text)
        . = 0x1FE;
        SHORT(0xAA55)
    }
}

Monte e vincule com:

gcc -c -g -o main.o main.S
ld --oformat binary -o main.img -T linker.ld main.o

Resultado:

insira a descrição da imagem aqui

Testado em: Lenovo Thinkpad T430, UEFI BIOS 1.16. Disco gerado em um host Ubuntu 18.04.

Além das instruções de montagem padrão da userland, temos:

  • .code16: diz ao GAS para gerar código de 16 bits

  • cli: desativar interrupções de software. Isso pode fazer com que o processador volte a funcionar após ohlt

  • int $0x10: faz uma chamada do BIOS. É isso que imprime os caracteres um por um.

Os sinalizadores de link importantes são:

  • --oformat binary: produz código de montagem binário bruto, não o distorce dentro de um arquivo ELF, como é o caso de executáveis ​​regulares da terra do usuário.

Use C em vez de montagem

Como o C é compilado no assembly, o uso do C sem a biblioteca padrão é bastante simples, você basicamente precisa:

  • um script vinculador para colocar as coisas na memória no lugar certo
  • sinalizadores que informam ao GCC para não usar a biblioteca padrão
  • um pequeno ponto de entrada de montagem que define o estado C necessário para main, principalmente:
    • a pilha
    • zerar o BSS

TODO: link para algum exemplo x86 no GitHub. Aqui está um ARM que eu criei .

No entanto, as coisas ficam mais divertidas se você quiser usar a biblioteca padrão, já que não temos o kernel Linux, o que implementa grande parte da funcionalidade da biblioteca padrão C através do POSIX .

Algumas possibilidades, sem acessar um sistema operacional completo como o Linux, incluem:

BRAÇO

No ARM, as idéias gerais são as mesmas. Eu enviei:

Para o Raspberry Pi, https://github.com/dwelch67/raspberrypi parece o tutorial mais popular disponível hoje.

Algumas diferenças do x86 incluem:

  • IO é feito escrevendo diretamente para endereços de mágica, não há ine outinstruções.

    Isso é chamado de E / S mapeada na memória .

  • para hardware real, como o Raspberry Pi, você pode adicionar o firmware (BIOS) à imagem do disco.

    Isso é bom, pois torna a atualização desse firmware mais transparente.

Firmware

Na verdade, seu setor de inicialização não é o primeiro software executado na CPU do sistema.

O que realmente roda primeiro é o chamado firmware , que é um software:

  • feita pelos fabricantes de hardware
  • fonte tipicamente fechada, mas provavelmente baseada em C
  • armazenado na memória somente leitura e, portanto, mais difícil / impossível de modificar sem o consentimento do fornecedor.

Firmwares conhecidos incluem:

  • BIOS : firmware x86 antigo completo. SeaBIOS é a implementação de código aberto padrão usada pelo QEMU.
  • UEFI : sucessor do BIOS, melhor padronizado, mas mais capaz e incrivelmente inchado.
  • Coreboot : a nobre tentativa de código aberto de arco cruzado

O firmware faz coisas como:

  • faça um loop sobre cada disco rígido, USB, rede etc. até encontrar algo inicializável.

    Quando executamos o QEMU, -hdadiz que main.imgé um disco rígido conectado ao hardware e

    hda é o primeiro a ser experimentado e é usado.

  • carregue os primeiros 512 bytes no endereço de memória RAM 0x7c00, coloque o RIP da CPU lá e deixe executar

  • mostrar coisas como o menu de inicialização ou as chamadas de impressão do BIOS no visor

O firmware oferece funcionalidade semelhante ao sistema operacional da qual a maioria dos sistemas operacionais depende. Por exemplo, um subconjunto Python foi portado para execução no BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM

Pode-se argumentar que os firmwares são indistinguíveis dos sistemas operacionais, e que o firmware é a única programação bare metal "verdadeira" que se pode fazer.

Como este desenvolvedor do CoreOS coloca :

A parte difícil

Quando você liga um PC, os chips que compõem o chipset (northbridge, southbridge e SuperIO) ainda não foram inicializados corretamente. Mesmo que a ROM do BIOS esteja o mais longe possível da CPU, ela pode ser acessada pela CPU, porque precisa ser, caso contrário, a CPU não teria instruções para executar. Isso não significa que a ROM do BIOS esteja completamente mapeada, geralmente não. Mas apenas o suficiente é mapeado para iniciar o processo de inicialização. Quaisquer outros dispositivos, apenas esqueça.

Ao executar o Coreboot no QEMU, você pode experimentar as camadas superiores do Coreboot e as cargas úteis, mas o QEMU oferece poucas oportunidades para experimentar o código de inicialização de baixo nível. Por um lado, a RAM funciona desde o início.

Estado inicial do BIOS pós

Como muitas coisas no hardware, a padronização é fraca, e uma das coisas em que você não deve confiar é o estado inicial dos registros quando o código começa a ser executado após o BIOS.

Então faça um favor a si mesmo e use algum código de inicialização como o seguinte: https://stackoverflow.com/a/32509555/895245

Os registros gostam %dse %estêm efeitos colaterais importantes, portanto, você deve zerá-los, mesmo que não os esteja usando explicitamente.

Observe que alguns emuladores são mais agradáveis ​​que o hardware real e fornecem um bom estado inicial. Então, quando você roda em hardware real, tudo quebra.

Inicialização múltipla GNU GRUB

Os setores de inicialização são simples, mas não são muito convenientes:

  • você só pode ter um sistema operacional por disco
  • o código de carregamento deve ser muito pequeno e caber em 512 bytes. Isso pode ser resolvido com a chamada int 0x13 do BIOS .
  • você tem que fazer muita inicialização, como mudar para o modo protegido

É por esses motivos que o GNU GRUB criou um formato de arquivo mais conveniente chamado de inicialização múltipla.

Exemplo de trabalho mínimo: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

Também o uso no repositório de exemplos do GitHub para poder executar facilmente todos os exemplos em hardware real sem queimar o USB um milhão de vezes. No QEMU, fica assim:

insira a descrição da imagem aqui

Se você preparar seu sistema operacional como um arquivo de inicialização múltipla, o GRUB poderá encontrá-lo dentro de um sistema de arquivos comum.

É isso que a maioria das distros faz, colocando as imagens do SO em baixo /boot.

Os arquivos de inicialização múltipla são basicamente um arquivo ELF com um cabeçalho especial. Eles são especificados pelo GRUB em: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html

Você pode transformar um arquivo de inicialização múltipla em um disco inicializável com grub-mkrescue.

El Torito

Formato que pode ser gravado em CDs: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29

Também é possível produzir uma imagem híbrida que funcione em ISO ou USB. Isso pode ser feito com grub-mkrescue( exemplo ) e também é feito pelo kernel do Linux no make isoimageuso isohybrid.

Recursos

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.