Um executável precisaria de um kernel do SO para rodar?

52

Eu sei que quando o código-fonte, digamos em C ++, é compilado, a saída do compilador é o código de máquina (executável) que eu pensei que fossem instruções diretamente para a CPU. Recentemente eu estava lendo nos kernels e descobri que os programas não podem acessar o hardware diretamente, mas precisam passar pelo kernel.

Então, quando compilamos algum código-fonte simples, digamos com apenas uma função printf() , e a compilação produz o código de máquina executável, cada instrução neste código de máquina será executada diretamente da memória (uma vez que o código é carregado na memória pelo SO) ou cada comando no código de máquina ainda precisará passar pelo SO (kernel) para ser executado?

Eu li uma pergunta semelhante . Ele não explicou se o código de máquina que é gerado após a compilação é uma instrução para a CPU diretamente ou se precisará novamente passar pelo kernel para criar a instrução correta para a CPU. Ou seja, o que acontece depois que o código da máquina é carregado na memória? Será que vai através do kernel ou falar diretamente com o processador?

    
por GRANZER 27.07.2018 / 17:41

11 respostas

85

Como alguém que escreveu programas que são executados sem um SO, ofereço uma resposta definitiva.

Would an executable need an OS kernel to run?

Isso depende de como esse programa foi escrito e criado.
Você poderia escrever um programa (supondo que você tem o conhecimento) que não requer um sistema operacional.
Esse programa é descrito como standalone .
Carregadores de inicialização e programas de diagnóstico são usos típicos para programas autônomos.

No entanto, o programa típico escrito e construído em algum ambiente do sistema operacional host teria como padrão a execução nesse mesmo ambiente do sistema operacional host.
Decisões e ações muito explícitas são necessárias para escrever e construir um programa independente.

... the output from the compiler is the machine code (executable) which I thought were instructions to the CPU directly.

Correto.

Recently I was reading up on kernels and I found out that programs cannot access the hardware directly but have to go through the kernel.

Essa é uma restrição imposta por um modo de CPU que o sistema operacional usa para executar programas e facilitado por certas ferramentas de compilação, como compiladores e bibliotecas. Não é uma limitação intrínseca em todos os programas já escritos.

So when we compile some simple source code, say with just a printf() function, and the compilation produces the executable machine code, will each instruction in this machine code be directly executed from memory (once the code is loaded into memory by the OS) or will each command in the machine code still need to go through the OS (kernel) to be executed?

Toda instrução é executada pela CPU.
Uma instrução não suportada ou ilegal (por exemplo, processo tem privilégio insuficiente) causará uma exceção imediata, e a CPU executará uma rotina para lidar com essa condição incomum.

Uma função printf () não deve ser usada como um exemplo de "código fonte simples" .
A tradução de uma linguagem de programação de alto nível orientada a objeto para um código de máquina pode não ser tão trivial quanto você sugere. E então você escolhe uma das funções mais complexas de uma biblioteca de tempo de execução que realiza conversões de dados e I / O.

Observe que sua pergunta estipula um ambiente com um sistema operacional (e uma biblioteca de tempo de execução).
Uma vez que o sistema é inicializado e o sistema operacional recebe o controle do computador, restrições são impostas sobre o que um programa pode fazer (por exemplo, a E / S deve ser executada pelo sistema operacional). Se você espera executar um programa independente (ou seja, sem um sistema operacional), não é necessário inicializar o computador para executar o SO.

... what happens after the machine code is loaded into memory?

Isso depende do ambiente.

Para um programa autônomo, ele pode ser executado, ou seja, o controle é entregue ao pular para o endereço inicial do programa.

Para um programa carregado pelo sistema operacional, o programa deve ser vinculado dinamicamente a bibliotecas compartilhadas das quais depende. O sistema operacional deve criar um espaço de execução para o processo que executará o programa.

Will it go through the kernel or directly talk to the processor?

O código da máquina é executado pela CPU.
Eles não "passam pelo kernel" , mas nem "conversam com o processador" .
O código da máquina (composto por código operacional e operandos) é uma instrução para a CPU que é decodificada e a operação é executada.

Talvez o próximo tópico que você deva investigar seja modos de CPU .

    
por 27.07.2018 / 22:32
38

O kernel é "apenas" mais código. É só que esse código é uma camada que vive entre as partes mais baixas do seu sistema e o hardware real.

Tudo isso roda diretamente na CPU, você faz a transição através de camadas para fazer qualquer coisa.

Seu programa "precisa" do kernel da mesma forma que ele precisa das bibliotecas C padrão para usar o comando printf em primeiro lugar.

O código atual do seu programa roda na CPU, mas os ramos que o código faz para imprimir algo na tela passam pelo código da função C printf , através de vários outros sistemas e intérpretes, cada um deles fazendo o seu próprio processamento para trabalhar apenas como hello world! é realmente impresso em sua tela.

Digamos que você tenha um programa de terminal sendo executado em um gerenciador de janelas da área de trabalho, rodando em seu kernel que, por sua vez, está sendo executado em seu hardware.

Há muito mais coisas acontecendo, mas vamos simplificar ...

  1. No seu programa de terminal, você executa seu programa para imprimir hello world!
  2. O terminal vê que o programa gravou (por meio das rotinas de saída C) hello world! no console
  3. O programa terminal vai até o gerenciador de janelas da área de trabalho dizendo "Eu tenho hello world! escrito em mim, você pode colocá-lo na posição x , y por favor?"
  4. O gerenciador de janelas da área de trabalho vai até o kernel com "um dos meus programas quer que seu dispositivo gráfico coloque algum texto nessa posição, vá até ele cara!"
  5. O kernel passa a solicitação para o driver de dispositivo gráfico, que o formata de forma que a placa gráfica possa entender
  6. Dependendo de como a placa de vídeo está conectada, outros drivers de dispositivo de kernel precisam ser chamados para enviar os dados para fora em barramentos de dispositivos físicos, como PCIe, manipulando coisas como verificar se o dispositivo correto está selecionado e que os dados podem passar ponte ou conversores relevantes
  7. O hardware exibe coisas.

Esta é uma simplificação enorme para descrição apenas. Aqui estão dragões.

Efetivamente, tudo o que você faz precisa de acesso a hardware, exibição, blocos de memória, pedaços de arquivos ou qualquer coisa assim para passar por algum driver de dispositivo no kernel para trabalhar exatamente como falar com o dispositivo relevante. Seja um driver de sistema de arquivos em cima de um driver de controlador de disco rígido SATA que está em cima de um dispositivo de ponte PCIe.

O kernel sabe como unir todos esses dispositivos e apresenta uma interface relativamente simples para que os programas façam coisas sem precisar saber como fazer tudo isso sozinhos.

Os gerenciadores de janela da área de trabalho fornecem uma camada que significa que os programas não precisam saber como desenhar janelas e jogar bem com outros programas que tentam exibir as coisas ao mesmo tempo.

Finalmente, o programa terminal significa que seu programa não precisa saber como desenhar uma janela, nem como falar com o driver da placa de vídeo do kernel, nem toda a complexidade de lidar com buffers de tela e tempo de exibição e na verdade, mexendo as linhas de dados para o display.

Tudo é tratado por camadas e mais camadas de código.

    
por 27.07.2018 / 18:22
21

Depende do ambiente. Em muitos computadores antigos (e mais simples!), Como o IBM 1401, a resposta seria "não". Seu compilador e linker emitiram um "binário" autônomo que foi executado sem nenhum sistema operacional. Quando seu programa parou de funcionar, você carregou um diferente, que também rodou sem sistema operacional.

Um sistema operacional é necessário em ambientes modernos, porque você não está executando apenas um programa por vez. Compartilhar o (s) núcleo (s) da CPU, a RAM, o dispositivo de armazenamento em massa, o teclado, o mouse e a tela, entre vários programas ao mesmo tempo, requer coordenação. O sistema operacional fornece isso. Assim, em um ambiente moderno, seu programa não pode apenas ler e gravar o disco ou o SSD, ele tem que pedir ao sistema operacional para fazer isso em seu nome. O sistema operacional obtém tais solicitações de todos os programas que desejam acessar o dispositivo de armazenamento, implementa coisas como controles de acesso (não permite que usuários comuns gravem nos arquivos do sistema operacional), enfileira-os no dispositivo e classifica as informações retornadas aos programas corretos (processos).

Além disso, os computadores modernos (diferentemente do 1401, por exemplo) suportam a conexão de uma ampla variedade de dispositivos de E / S, não apenas os que a IBM venderia nos velhos tempos. Seu compilador e linker não podem saber sobre todas as possibilidades. Por exemplo, o seu teclado pode estar conectado via PS / 2 ou USB. O sistema operacional permite que você instale "drivers de dispositivo" específicos de dispositivo que sabem como falar com esses dispositivos, mas apresentam uma interface comum para a classe de dispositivo no sistema operacional. Portanto, o seu programa, e até mesmo o SO, não precisa fazer nada diferente para obter pressionamentos de teclas de um teclado USB versus PS / 2, ou para acessar um disco SATA local versus um dispositivo de armazenamento USB versus armazenamento que está em algum lugar fora em um NAS ou SAN. Esses detalhes são manipulados por drivers de dispositivo para os vários controladores de dispositivo.

Para dispositivos de armazenamento em massa, o sistema operacional fornece, sobre todos eles, um driver de sistema de arquivos que apresenta a mesma interface para diretórios e arquivos, independentemente de onde e como o armazenamento é implementado. E novamente, o sistema operacional preocupa-se com controles de acesso e serialização. Em geral, por exemplo, o mesmo arquivo não deve ser aberto para escrita por mais de um programa de cada vez sem passar por alguns arcos (mas as leituras simultâneas são geralmente ok).

Então, em um ambiente moderno de propósito geral, sim - você realmente precisa de um sistema operacional. Mas até hoje existem computadores, como controladores em tempo real, que não são complicados o suficiente para precisar de um.

No ambiente do Arduino, por exemplo, não há realmente um sistema operacional. Claro, há um monte de código de biblioteca que o ambiente de compilação incorpora em cada "binário" que ele constrói. Mas como não há persistência desse código de um programa para outro, não é um sistema operacional.

    
por 27.07.2018 / 20:37
10

Acho que muitas respostas não entendem a pergunta, o que se resume a isso:

A compiler outputs machine code. Is this machine code executed directly by a CPU, or is it "interpreted" by the kernel?

Basicamente, a CPU executa diretamente o código da máquina . Seria significativamente mais lento ter o kernel executando todos os aplicativos. No entanto, existem algumas ressalvas.

  1. Quando um sistema operacional está presente, os programas aplicativos geralmente são impedidos de executar determinadas instruções ou acessar certos recursos. Por exemplo, se um aplicativo executa uma instrução que modifica a tabela de interrupções do sistema, a CPU, em vez disso, passa para um manipulador de exceções do sistema operacional para que o aplicativo incorreto seja encerrado. Além disso, os aplicativos geralmente não podem ler / gravar na memória do dispositivo. (Por exemplo, "conversando com o hardware".) O acesso a essas regiões de memória especiais é como o sistema operacional se comunica com dispositivos como a placa gráfica, a interface de rede, o relógio do sistema, etc.

  2. As restrições que um SO coloca em aplicativos são alcançadas por recursos especiais da CPU, como modos de privilégio, proteção de memória e interrupções. Embora qualquer CPU que você encontrasse em um smartphone ou PC tenha esses recursos, algumas CPUs não. Esses processadores realmente precisam de kernels especiais que "interpretem" o código do aplicativo para obter os recursos desejados. Um exemplo muito interessante é o Gigatron , que é um computador de 8 instruções que você pode construir com chips que emulam um computador com 34 instruções.

  3. Algumas linguagens como Java "compilam" para algo chamado Bytecode, que não é realmente código de máquina. Embora no passado eles fossem interpretados para rodar os programas, nos dias de hoje algo chamado de compilação Just-in-Time é geralmente usado para que eles acabem sendo executados diretamente na CPU como código de máquina.

  4. A execução de software em uma máquina virtual costumava exigir que seu código de máquina fosse "interpretado" por um programa chamado Hypervisor . Devido à enorme demanda do setor por VMs, os fabricantes de CPU adicionaram recursos como o VTx em suas CPUs para permitir que a maioria das instruções de um sistema convidado seja executada diretamente pela CPU. No entanto, ao executar um software projetado para uma CPU incompatível em uma Máquina Virtual (por exemplo, emulando um NES), o código da máquina precisará ser interpretado.

por 28.07.2018 / 10:26
5

Quando você compila seu código, você cria o chamado código "object" que (na maioria dos casos) depende de bibliotecas do sistema ( printf por exemplo), então seu código é empacotado pelo linker que adicionará o tipo de carregador de programa que seu sistema operacional em particular pode reconhecer (é por isso que você não pode executar um programa compilado para Windows no Linux, por exemplo) e saber como desembrulhar seu código e executá-lo. Então, seu programa é como uma carne dentro de um sanduíche e pode ser comido apenas como um pacote, no todo.

Recently I was reading up on Kernels and I found out that programs cannot access the hardware directly but have to go through the kernel.

Bem, é meio verdade; Se o seu programa for um driver do modo kernel, você poderá acessar diretamente o hardware se souber "falar" com o hardware, mas normalmente (especialmente para hardware não documentado ou complicado) as pessoas usam drivers que são bibliotecas do kernel. Desta forma, você pode encontrar funções de API que sabem como falar com hardware de maneira quase humana, sem a necessidade de conhecer endereços, registros, sincronismo e outras coisas.

will each instruction in this machine code be directly be executed from the memory (once the code is loaded into the memory by OS) or will each each command in the machine code still need to go through the OS(kernel) to be executed

Bem, o kernel é como garçonete, cuja responsabilidade é levá-lo a uma mesa e atendê-lo. A única coisa que não pode fazer - é comer para você, você deve fazer isso sozinho. O mesmo com o seu código, o kernel irá descompactar o seu programa para uma memória e irá iniciar o seu código, que é um código de máquina executado diretamente pela CPU. Um kernel só precisa supervisionar você - o que você é permitido e o que você não pode fazer.

it not explain if the machine code that is generated after compilation is an instruction to the CPU directly or will it need to again go through the kernel to create the correct instruction for the CPU?

O código da máquina gerado após a compilação é uma instrução para a CPU diretamente. Nenhuma dúvida sobre isso. A única coisa que você precisa ter em mente, nem todo código no arquivo compilado é o código real da máquina / CPU. O Linker envolveu seu programa com alguns metadados que somente o kernel pode interpretar, como uma pista - o que fazer com seu programa.

What happens after the machine code loaded on to the memory? Will it go through the kernel or directly talk to the processor.

Se o seu código é simplesmente simples, como adição de dois registradores, ele será executado diretamente pela CPU sem ajuda do kernel, mas se seu código usar funções de bibliotecas, essas chamadas serão assistidas pelo kernel, como no exemplo com waitress, se você quiser comer em um restaurante, eles lhe dariam uma ferramenta - garfo, colher (e ainda assim, seus bens), mas o que você faria com ele - o seu "código".

Bem, apenas para evitar ataques nos comentários - é um modelo muito simplista que espero que ajude a OP a entender as coisas básicas, mas boas sugestões para melhorar essa resposta são bem-vindas.

    
por 27.07.2018 / 20:16
3

So when we compile a simple source code, say with just a printf() function, and the compilation produces the executable machine code, will each instruction in this machine code be directly be executed from the memory (once the code is loaded into the memory by OS) or will each each command in the machine code still need to go through the OS(kernel) to be executed?

Essencialmente, apenas as chamadas do sistema vão para o kernel. Qualquer coisa relacionada a E / S ou alocação / desalocação de memória normalmente resulta em uma chamada do sistema. Algumas instructiosn só podem ser executadas no modo kernel e farão com que a CPU dispare uma exceção. Exceções causam uma mudança para o modo kernel e um salto para o código do kernel.

O kernel não processa todas as instruções em um programa. Ele apenas faz as chamadas do sistema e alterna entre os programas em execução para compartilhar a CPU.

Fazer a alocação de memória no modo de usuário (sem o kernel) não é possível, se você acessar a memória que não tem permissão de acesso, a MMU, previamente programada pelo kernel, notará e causará uma "segmentação" no nível da CPU. fault "exception, que aciona o kernel, e o kernel mata o programa.

Fazer E / S no modo de usuário (sem o kernel) não é possível, se você acessar portas de E / S ou registros de dispositivos ou endereços conectados a dispositivos (um ou ambos necessários para executar qualquer E / S), isso aciona uma exceção da mesma maneira.

Would a executable need a OS kernel to run?

Depende do tipo de executável.

Kernels, além de mediar o acesso compartilhado à RAM e ao hardware, também executam uma função loader.

Muitos "formatos executáveis", como o ELF ou o PE, têm metadados no arquivo executável além do código e são o trabalho do carregador para processá-lo. Leia os detalhes sobre o formato PE da Microsoft para obter mais informações.

Esses executáveis também fazem referência a bibliotecas (Windows .dll ou objeto compartilhado .so do Linux) - seu código deve ser incluído.

Se o seu compilador produz um arquivo que deve ser processado por um carregador do sistema operacional e que o carregador não está lá, não funcionará.

  • Você pode incluir código que faz o trabalho do carregador?

Claro. Você precisa convencer o SO a, de alguma forma, executar seu código bruto sem processar nenhum metadado. Se o seu código chamar APIs do kernel, ainda assim não funcionará.

  • E se ele não chamar APIs de kernel?

Se você carregar esse executável de alguma forma de algum sistema operacional (isto é, se ele permitir que o código bruto seja carregado e executado), ele ainda estará no modo de usuário. Se seu código acessa coisas que são proibidas no modo de usuário, ao contrário do modo kernel, como memória não alocada ou endereços / registros de dispositivos de E / S, ele falhará com privilégios ou violações de segmento (mais uma vez, as exceções vão para o modo kernel e são manipuladas lá) e ainda não vai funcionar.

  • E se você executá-lo no modo kernel.

Então vai funcionar.

por 27.07.2018 / 20:51
3

TL; no. DR

O desenvolvimento do Arduino vem à mente como um ambiente atual onde não há sistema operacional. Confie em mim, em um dos esses bebês você não tem espaço para um sistema operacional.

Da mesma forma, os jogos para o Sega Genesis não tinham um sistema operacional fornecido pela Sega para entrar em contato. Você acabou de criar seu jogo em 68K assembler, escrevendo diretamente para o metal.

Ou onde eu cortei meus dentes, fazendo trabalho incorporado no Intel 8051. Novamente, quando tudo que você tem é um 2716 eprom com um tamanho 2k * 8, você não tem espaço para um sistema operacional.

Naturalmente, isso pressupõe um uso muito amplo do aplicativo Word. Como pergunta retórica, vale a pena se perguntar se um esboço do Arduino é realmente um aplicativo.

    
por 27.07.2018 / 22:30
3

Embora eu não queira sugerir que as outras respostas não estão certas por si só, elas fornecem muitos detalhes que, infelizmente, ainda são muito obscuros para você.

A resposta básica é que o código será executado diretamente no processador. E não, o código da máquina não "fala" com ninguém, é o contrário. O processador é o componente ativo e tudo o que você faz no seu computador será feito por esse processador (estou simplificando um pouco as coisas aqui, mas tudo bem por agora). O processador irá ler o código e executá-lo e cuspir os resultados, o código da máquina é apenas alimento para o processador.

Sua confusão decorre do uso da palavra hardware. Embora a divisão não seja tão clara como costumava ser, é melhor pensar em termos de periféricos do que simplesmente chamar hardware. Então, se existe um sistema operacional ou similar em sua máquina, seu programa tem que usar seus serviços para acessar os periféricos, mas o processador em si não é um periférico, é a principal unidade de processamento que seu programa roda diretamente.

Kernels, sistemas operacionais e camadas intermediárias similares são tipicamente usadas apenas em sistemas maiores, onde há uma expectativa de que vários programas serão executados e há uma necessidade de o sistema gerenciar como esses programas podem usar os periféricos do computador. muitas vezes ao mesmo tempo). Nesses casos, os programas em execução só podem acessar esses periféricos usando o sistema que decidirá como compartilhá-los e se certificará de que não haja conflitos. Sistemas pequenos, onde não há necessidade de gerenciamento entre programas concorrentes, porque não existem, geralmente não têm nenhum sistema subjacente e o único programa normalmente executado nesses sistemas é mais ou menos livre para fazer o que quiser com os periféricos.

    
por 28.07.2018 / 01:46
2

O BIOS que é executado em seu computador na inicialização é um código executável armazenado na ROM. Consiste em instruções de máquina mais dados. Existe um compilador (ou assembler) que monta este BIOS a partir do código fonte. Este é um caso especial.

Outros casos especiais incluem o programa de bootstrap que carrega o kernel e o próprio kernel. Esses casos especiais geralmente são codificados em um idioma diferente do C ++.

No caso geral, é muito mais prático que o compilador produza algumas instruções que invocam serviços do sistema fornecidos por um kernel ou por rotinas de bibliotecas. Isso torna o compilador muito mais leve. Também torna o código compilado mais leve.

No outro extremo do espectro está o Java. Em Java, o compilador não traduz o código-fonte em instruções de máquina, pois esse termo é normalmente entendido. Em vez disso, o código-fonte é traduzido em "instruções de máquina" para uma máquina imaginária, chamada Java Virtual Machine. Antes que um programa Java possa ser executado, ele deve ser combinado com o Java runtime, que inclui um interpretador para a Java Virtual Machine.

    
por 28.07.2018 / 14:03
2

Nos bons e velhos tempos, o seu programa era responsável por fazer tudo o que precisava ser feito durante a execução do seu programa, seja fazendo você mesmo ou adicionando o código da biblioteca que outras pessoas escreveram para o seu programa. A única coisa que funcionava ao lado disso no computador era o código para ler em seu programa compilado - se você tivesse sorte. Alguns computadores precisavam ter código inserido por meio de comutadores antes de poderem fazer mais (o processo original de "inicialização") ou até mesmo todo o seu programa inserido dessa maneira.

Foi rapidamente descoberto que era legal ter o código rodando capaz de carregar e executar programas. Mais tarde, descobriu-se que os computadores eram poderosos o suficiente para suportar a execução de vários programas ao mesmo tempo, fazendo com que a CPU alternasse entre eles, especialmente se o hardware pudesse ajudar, mas com a complexidade adicional dos programas. , como lidar com vários programas tentando enviar dados para a impressora de uma vez?).

Tudo isso resultou em uma grande quantidade de código auxiliar sendo movido dos programas individuais para o "sistema operacional", com uma maneira padronizada de invocar o código auxiliar a partir dos programas do usuário.

E é aí que estamos hoje. Seus programas rodam a todo vapor, mas sempre que precisam de algo gerenciado pelo sistema operacional, eles chamam rotinas auxiliares fornecidas pelo sistema operacional, e esse código não é necessário e não está presente nos próprios programas do usuário. Isso incluiu escrever no visor, salvar arquivos, acessar a rede, etc.

Microkernels foram escritos para fornecer o que é necessário para um determinado programa rodar sem um sistema operacional completo. Isso tem algumas vantagens para os usuários experientes, enquanto distribui a maioria dos outros. Você pode querer ler a página da Wikipedia sobre isso - link - se você quiser saber mais.

Eu experimentei um Microkernel capaz de executar uma Java Virtual Machine, mas descobri depois que o ponto ideal para isso é o Docker.

    
por 28.07.2018 / 16:37
1

Em sistemas operacionais de desktop típicos, o próprio kernel é um executável. (O Windows tem ntoskrnl.exe ; o Linux tem vmlinux , etc.) Se você precisasse de um kernel para executar um executável, então esses sistemas operacionais não poderiam existir.

O que você precisa de um kernel é fazer as coisas que um kernel faz. Permitir que múltiplos executáveis sejam executados ao mesmo tempo, arbitrar entre eles, abstrair o hardware, etc. A maioria dos programas não é capaz de fazer essas coisas por conta própria, e você não os desejaria, mesmo que eles pudessem. Nos dias do DOS - que mal podia ser chamado de sistema operacional em si - os jogos geralmente usavam o sistema operacional como pouco mais que um carregador, e acessavam diretamente o hardware da mesma forma que um kernel faria. Mas muitas vezes você precisava saber quais marcas e modelos de hardware estavam em sua máquina antes de comprar um jogo. Muitos jogos suportavam apenas certas famílias de placas de vídeo e de som, e funcionavam muito mal em marcas concorrentes se eles funcionassem. Esse é o tipo de coisa que você obtém quando o programa controla o hardware diretamente e não através da abstração normalmente fornecida pelo kernel.

    
por 31.07.2018 / 18:07