Eu sei que quando o código-fonte, digamos C ++, é compilado, a saída do compilador é o código da máquina (executável) que eu pensei que fosse instruções diretamente para a CPU. Recentemente, eu estava lendo sobre kernels e descobri que os programas não podem acessar o hardware diretamente, mas precisam passar pelo kernel.
Portanto, quando compilarmos um código-fonte simples, digamos com apenas uma printf()
função, e a compilação produzir o código de máquina executável, cada instrução nesse código de máquina será executada diretamente da memória (depois que o código for carregado na memória pelo sistema operacional) ou será cada comando no código da máquina ainda precisa passar pelo SO (kernel) a ser executado?
Eu li uma pergunta semelhante . Ele não explicou se o código da máquina gerado após a compilação é uma instrução diretamente para a CPU ou se será necessário passar novamente 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? Ele passará pelo kernel ou conversará diretamente com o processador?
fonte
printf
não é um ótimo exemplo. É explicitamente definido pela especificação C como uma função disponível apenas em implementações "hospedadas" (ou seja, executando em um kernel, em oposição a "autônomo", que pode não exigir uma). E na maioria das plataformas,printf
é apenas uma função fornecida por vocêlibc
que faz um monte de coisas em seu nome (que eventualmente inclui um syscall para imprimir no stdout). Realmente não é diferente de chamarlibvlc_media_list_add_media
ouPyObject_GetAttr
, exceto que algumaprintf
implementação é garantida como vinculável sem a adição de-l
s não-padrão extras .Respostas:
Como alguém que escreveu programas executados sem um sistema operacional, ofereço uma resposta definitiva.
Isso depende de como esse programa foi escrito e construído.
Você pode escrever um programa (supondo que tenha conhecimento) que não exija um sistema operacional.
Esse programa é descrito como autônomo .
Carregadores de inicialização e programas de diagnóstico são usos típicos para programas autônomos.
No entanto, o programa típico gravado e construído em algum ambiente do sistema operacional host será padronizado para execução no mesmo ambiente do sistema operacional host.
São necessárias decisões e ações muito explícitas para escrever e criar um programa independente.
Corrigir.
Essa é uma restrição imposta por um modo de CPU que o sistema operacional usa para executar programas e facilitada por certas ferramentas de compilação, como compiladores e bibliotecas.
Não é uma limitação intrínseca a todos os programas já escritos.
Toda instrução é executada pela CPU.
Uma instrução que não é suportada ou é ilegal (por exemplo, o processo tem privilégios insuficientes) 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 objetos para 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 E / S.
Observe que sua pergunta estipula um ambiente com um sistema operacional (e uma biblioteca de tempo de execução).
Depois que o sistema é inicializado e o sistema operacional recebe o controle do computador, são impostas restrições 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 deve inicializar o computador para executar o sistema operacional.
Isso depende do meio ambiente.
Para um programa independente, ele pode ser executado, ou seja, o controle é entregue pulando para o endereço inicial do programa.
Para um programa carregado pelo sistema operacional, o programa precisa ser vinculado dinamicamente às bibliotecas compartilhadas das quais depende. O sistema operacional precisa criar um espaço de execução para o processo que executará o programa.
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 (que consiste em 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 o modo CPU .
fonte
gcc -O2 -ffreestanding my_kernel.c special_sauce.S
para criar um executável que não presuma que nenhuma das bibliotecas normais ou material do SO esteja lá. (Claro que você normalmente precisa de um script de vinculador para fazê-lo utilmente link em um formato de arquivo que um bootloader vai querer carga!)O kernel é "apenas" mais código. Só que esse código é uma camada que fica entre as partes mais baixas do seu sistema e o hardware real.
Tudo é executado diretamente na CPU, você apenas passa por camadas dela para fazer qualquer coisa.
Seu programa "precisa" do kernel da mesma maneira que precisa das bibliotecas C padrão para usar o
printf
comando em primeiro lugar.O código real do seu programa é executado na CPU, mas as ramificações que o código faz para imprimir algo na tela passam pelo código da
printf
função C , por vários outros sistemas e intérpretes, cada um dos quais faz seu próprio processamento para descobrir exatamente comohello world!
realmente é impresso na tela.Digamos que você tenha um programa de terminal executando em um gerenciador de janelas da área de trabalho, executando no seu kernel que, por sua vez, está sendo executado no seu hardware.
Há muito mais coisas acontecendo, mas vamos simplificar ...
hello world!
hello world!
no consolehello world!
escrito para mim, você pode colocá-lo em posiçãox
,y
por favor?"Esta é uma simplificação excessiva maciça apenas para descrição. Aqui estejam dragões.
Efetivamente, tudo o que você faz que precisa de acesso ao hardware, seja exibição, blocos de memória, bits de arquivos ou qualquer coisa assim, precisa passar por algum driver de dispositivo no kernel para descobrir exatamente como conversar 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 os programas fazerem as coisas sem precisar saber como fazer elas mesmas.
Os gerenciadores de janelas da área de trabalho fornecem uma camada que significa que os programas não precisam saber desenhar janelas e jogar bem com outros programas que tentam exibir as coisas ao mesmo tempo.
Finalmente, o programa do terminal significa que seu programa não precisa saber como desenhar uma janela, nem conversar com o driver da placa gráfica do kernel, nem toda a complexidade relacionada ao lidar com buffers de tela e tempo de exibição e realmente mexer no linhas de dados para a tela.
Tudo é tratado por camadas e camadas de código.
fonte
Depende do ambiente. Em muitos computadores mais antigos (e mais simples!), Como o IBM 1401, a resposta seria "não". Seu compilador e vinculador emitiram um "binário" independente que era executado sem nenhum sistema operacional. Quando seu programa parou de ser executado, você carregou um programa diferente, que também era executado sem SO.
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. Portanto, em um ambiente moderno, seu programa não pode apenas ler e gravar o disco ou o SSD, ele precisa solicitar ao sistema operacional que faça isso em seu nome. O sistema operacional recebe essas solicitações de todos os programas que desejam acessar o dispositivo de armazenamento, implementa coisas como controles de acesso (não pode permitir que usuários comuns gravem nos arquivos do sistema operacional), enfileira-as no dispositivo e classifica as informações retornadas para os programas corretos (processos).
Além disso, os computadores modernos (ao contrário do, digamos, o 1401) suportam a conexão de uma variedade muito ampla de dispositivos de E / S, não apenas os que a IBM venderia para você nos velhos tempos. Seu compilador e vinculador não podem conhecer todas as possibilidades. Por exemplo, seu teclado pode estar conectado via PS / 2 ou USB. O sistema operacional permite instalar "drivers de dispositivo" específicos do dispositivo que sabem como conversar com esses dispositivos, mas apresentam uma interface comum para a classe de dispositivo no sistema operacional. Portanto, seu programa e até o sistema operacional não precisam fazer nada diferente para obter pressionamentos de teclas de um teclado USB versus PS / 2 ou para acessar, digamos, um disco SATA local ou um dispositivo de armazenamento USB ou um armazenamento que esteja em algum lugar fora em um NAS ou SAN. Esses detalhes são tratados pelos drivers de dispositivos dos vários controladores de dispositivos.
Para dispositivos de armazenamento em massa, o sistema operacional fornece sobre todos esses 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 se preocupa com controles de acesso e serialização. Em geral, por exemplo, o mesmo arquivo não deve ser aberto para gravação por mais de um programa por vez, sem passar por alguns obstáculos (mas leituras simultâneas geralmente são boas).
Portanto, em um ambiente moderno de uso geral, sim - você realmente precisa de um sistema operacional. Mas ainda 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 existe realmente um sistema operacional. Claro, há um monte de código de biblioteca que o ambiente de construção incorpora em todos os "binários" que cria. Mas como não há persistência desse código de um programa para o outro, não é um sistema operacional.
fonte
Eu acho que muitas respostas não entendem a pergunta, que se resume a isso:
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.
Quando um sistema operacional está presente, os programas de aplicativos geralmente são impedidos de executar determinadas instruções ou acessar determinados recursos. Por exemplo, se um aplicativo executar uma instrução que modifica a tabela de interrupção do sistema, a CPU irá pular para um manipulador de exceções do SO para que o aplicativo incorreto seja encerrado. Além disso, os aplicativos geralmente não têm permissão para ler / gravar na memória do dispositivo. (Ou seja, "falando com o hardware".) Acessar essas regiões especiais de memória é como o sistema operacional se comunica com dispositivos como placa gráfica, interface de rede, relógio do sistema etc.
As restrições que um SO impõe aos 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ê encontraria em um smartphone ou PC possua esses recursos, algumas CPUs não. Essas CPUs realmente precisam de kernels especiais que "interpretam" o código do aplicativo para alcançar os recursos desejados. Um exemplo muito interessante é o Gigatron , que é um computador com 8 instruções que você pode construir com chips que emula um computador com 34 instruções.
Algumas linguagens como Java "compilam" algo chamado Bytecode, que não é realmente um código de máquina. Embora no passado eles fossem interpretados para executar os programas, hoje em dia algo chamado compilação Just-in-Time é normalmente usado, para que eles acabem sendo executados diretamente na CPU como código de máquina.
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 às suas CPUs para permitir que a maioria das instruções de um sistema convidado seja executada diretamente pela CPU. No entanto, ao executar o 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.
fonte
Ao compilar seu código, você cria o chamado código "objeto" que (na maioria dos casos) depende das bibliotecas do sistema (
printf
por exemplo), então o código é envolvido por um vinculador que adiciona o tipo de carregador de programa que seu sistema operacional específico pode reconheça (é por isso que você não pode executar o programa compilado para Windows no Linux, por exemplo) e saiba como desembrulhar seu código e executar. Portanto, seu programa é como uma carne dentro de um sanduíche e só pode ser consumido como um pacote.Bem, isso é meio caminho andado; se o seu programa for um driver no modo kernel, na verdade você poderá acessar diretamente o hardware se souber "conversar" com o hardware, mas geralmente (especialmente para hardware não documentado ou complicado) as pessoas usam drivers que são bibliotecas do kernel. Dessa forma, você pode encontrar funções da API que sabem como conversar com o hardware de maneira quase legível por humanos, sem a necessidade de saber endereços, registros, horários e várias outras coisas.
Bem, o kernel é como garçonete, cuja responsabilidade é levá-lo a uma mesa e servi-lo. A única coisa que não pode fazer - é comer para você, você deve fazer isso sozinho. O mesmo acontece com o seu código, o kernel descompactará seu programa em uma memória e iniciará seu código, que é o código da máquina executado diretamente pela CPU. Um kernel só precisa supervisionar você - o que você tem permissão e o que não tem permissão para fazer.
O código de máquina gerado após a compilação é uma instrução diretamente para a CPU. Não há dúvida disso. A única coisa que você precisa ter em mente é que nem todo código no arquivo compilado é o código da máquina / CPU real. O Linker envolveu seu programa com alguns metadados que apenas o kernel pode interpretar, como uma pista - o que fazer com o seu programa.
Se o seu código for apenas opcodes simples, como a adição de dois registradores, ele será executado diretamente pela CPU sem assistência do kernel, mas se o seu código usar funções de bibliotecas, essas chamadas serão assistidas pelo kernel, como no exemplo da garçonete, se você desejar para comer em um restaurante, eles dariam a você uma ferramenta - garfo, colher (e ainda são seus ativos), mas o que você fará com ela -, até o seu "código".
Bem, apenas para evitar chamas nos comentários - é realmente um modelo simplificado demais que espero ajudar o OP a entender as coisas básicas, mas boas sugestões para melhorar essa resposta são bem-vindas.
fonte
Essencialmente, apenas as chamadas do sistema vão para o kernel. Qualquer coisa a ver com E / S ou alocação / desalocação de memória geralmente resulta em uma chamada do sistema. Algumas instruções podem ser executadas apenas no modo kernel e farão com que a CPU desencadeie 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 chamadas e alterna entre os programas em execução para compartilhar a CPU.
Não é possível fazer alocação de memória no modo de usuário (sem o kernel); se você acessar a memória, não terá permissão para acessar, a MMU, previamente programada pelo kernel, notará e causaria uma exceção de "falha de segmentação" no nível da CPU. , que aciona o kernel, e o kernel mata o programa.
Não é possível executar E / S no modo de usuário (sem o kernel), se você acessar portas ou registros de E / S para dispositivos ou endereços conectados a dispositivos (um ou ambos necessários para executar qualquer E / S), eles acionam um exceção da mesma maneira.
Depende do tipo de executável.
Os kernels, além de mediar o acesso compartilhado à RAM e ao hardware, também desempenham uma função de carregador.
Muitos "formatos executáveis", como ELF ou PE, possuem metadados no arquivo executável, além do código, e o trabalho do carregador é processá-lo. Leia os detalhes sangrentos sobre o formato PE da Microsoft para obter mais informações.
Esses executáveis também fazem referência a bibliotecas ( arquivos de
.dll
objetos compartilhados no Windows ou Linux.so
) - seu código deve ser incluído.Se o seu compilador produzir um arquivo que deve ser processado por um carregador do sistema operacional, e esse carregador não estiver lá, ele não funcionará.
Certo. Você precisa convencer o sistema operacional a executar, de alguma forma, seu código bruto sem processar nenhum metadado. Se o seu código chamar APIs do kernel, ele ainda não funcionará.
Se você carregar este executável de alguma forma a partir de um sistema operacional (por exemplo, se ele permitir que o código bruto seja carregado e executado), ele ainda estará no modo de usuário. Se o seu código acessar coisas que são proibidas no modo de usuário, ao contrário do modo do kernel, como memória não alocada ou endereços / registros de dispositivos de E / S, ele trará falhas com privilégios ou violações de segmento (novamente, as exceções são para o modo kernel e são tratadas lá) e ainda não funcionará.
Então vai funcionar.
fonte
TL; DR No.
O desenvolvimento do Arduino vem à mente como um ambiente atual em que não há sistema operacional. Confie em mim, em um desses 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 serem acessados. Você acabou de criar seu jogo em montador de 68K, escrevendo diretamente para o bare metal.
Ou onde eu cortei os dentes, fazendo um trabalho incorporado no Intel 8051. Novamente, quando tudo o que você tem é uma 2716 eprom com uma área de 2k * 8, você não tem espaço para um sistema operacional.
Obviamente, isso pressupõe um uso muito amplo da palavra aplicativo. Como uma pergunta retórica, vale a pena se perguntar se um esboço do Arduino é realmente um aplicativo.
fonte
Embora eu não queira sugerir que as outras respostas não estejam corretas sozinhas, elas fornecem muitos detalhes que, receio, 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 "conversa" 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 enquanto). O processador irá ler o código e executá-lo e cuspir os resultados, o código da máquina é apenas um 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 se você pensar em periféricos, em vez de simplesmente chamar tudo de hardware. Portanto, se houver um sistema operacional ou semelhante em sua máquina, seu programa precisará usar seus serviços para acessar os periféricos, mas o próprio processador não é periférico, é a principal unidade de processamento na qual seu programa é executado diretamente.
Kernels, sistemas operacionais e camadas intermediárias semelhantes geralmente são usados apenas em sistemas maiores, onde há a expectativa de que vários programas sejam executados e é necessário que o sistema gerencie como esses programas podem usar os periféricos do computador (geralmente no mesmo tempo). Nesses casos, os programas em execução só podem acessar esses periféricos usando o sistema que decidirá como compartilhá-los e garantirá que não haja conflitos. Sistemas pequenos nos quais não há necessidade de gerenciamento entre os programas concorrentes, porque não existem, geralmente não possuem um sistema subjacente e o único programa normalmente em execução nesses sistemas é mais ou menos livre para fazer o que quiser com os periféricos.
fonte
O BIOS que é executado no seu computador na inicialização é um código executável armazenado na ROM. Consiste em instruções da máquina e dados. Existe um compilador (ou montador) que monta este BIOS a partir do código fonte. Este é um caso especial.
Outros casos especiais incluem o programa de autoinicialização que carrega o kernel e o próprio kernel. Esses casos especiais geralmente são codificados em um idioma diferente de C ++.
No caso geral, é muito mais prático fazer com que o compilador produza algumas instruções que invocam os serviços do sistema fornecidos por um kernel ou por rotinas de biblioteca. 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 converte o código fonte em instruções de máquina, pois esse termo geralmente é entendido. Em vez disso, o código-fonte é traduzido em "instruções da 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 tempo de execução do Java, que inclui um intérprete para a Java Virtual Machine.
fonte
Nos velhos tempos, seu programa era responsável por fazer tudo o que precisava ser feito durante a execução do seu programa, seja por você mesmo ou adicionando o código da biblioteca que outras pessoas escreviam em seu programa. A única coisa que funcionava ao lado disso no computador era o código para ler no seu programa compilado - se você tivesse sorte. Alguns computadores precisavam inserir código por meio de comutadores antes de poder fazer mais (o processo original de "inicialização"), ou mesmo todo o programa desse modo.
Foi rapidamente descoberto que era bom ter um código em execução capaz de carregar e executar o programa. Mais tarde, verificou-se que os computadores eram poderosos o suficiente para suportar a execução de vários programas ao mesmo tempo, alternando a CPU entre eles, especialmente se o hardware pudesse ajudar, mas com a complexidade adicional dos programas, não pisando nos dedos uns dos outros (por exemplo, , como lidar com vários programas que tentam enviar dados para a impressora de uma só vez?).
Tudo isso resultou em uma grande quantidade de código auxiliar sendo movida dos programas individuais para o "sistema operacional", com uma maneira padronizada de chamar o código auxiliar a partir de programas do usuário.
E é aí que estamos hoje. Seus programas executam a toda velocidade, mas sempre que precisam de algo gerenciado pelo sistema operacional, eles chamam de 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 incluía escrever no visor, salvar arquivos, acessar a rede etc.
Foram criados microkernels que fornecem exatamente o necessário para que um determinado programa seja executado sem um sistema operacional completo. Isso tem algumas vantagens para os usuários experientes, além de oferecer a maioria dos outros. Você pode ler a página da Wikipedia sobre o assunto - https://en.wikipedia.org/wiki/Microkernel - se quiser saber mais.
Eu experimentei um microkernel capaz de executar uma Java Virtual Machine, mas descobri mais tarde que o ponto ideal para isso é o Docker.
fonte
Nos sistemas operacionais típicos da área de trabalho, o próprio kernel é um executável. (Windows possui
ntoskrnl.exe
; Linux possuivmlinux
etc.) Se você precisasse de um kernel para que um executável fosse executado, esses sistemas operacionais não existiriam.O que você precisa para um kernel é fazer as coisas que um kernel faz. Permita que vários executáveis sejam executados ao mesmo tempo, apareça entre eles, abstraia o hardware, etc. A maioria dos programas não é capaz de fazer essas coisas por si só com competência, e você não gostaria que eles o fizessem. Nos dias do DOS - que mal podia ser chamado de sistema operacional - os jogos costumavam usar o sistema operacional como pouco mais que um carregador e acessavam diretamente o hardware como 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 suportam apenas certas famílias de placas de vídeo e som e funcionam muito mal em marcas concorrentes, se funcionarem. Naquela'
fonte