Como um programa é executado no nível da CPU?


14

Eu sei que esta é uma pergunta muito comum. Mas tenho um ângulo diferente em minha mente. Vou apenas tentar articulá-lo aqui.

Pelo que sei, todas as instruções que uma CPU executa, estão na linguagem da máquina e tudo que a CPU pode fazer é executar algumas operações aritméticas graças à ALU e a seus transistores (se formos ao nível do hardware).

No entanto, é mais fácil digitar do que compreendê-lo. Portanto, se toda a CPU faz adição, subtração, etc., como é um programa, digamos um programa JAVA dizendo print Hello World, executado com essas operações aritméticas?

Quero dizer, como esse programa é convertido em algo que é apenas uma adição à CPU?

PS Se esta pergunta não for aplicável a este site, peço desculpas.

-----Parte dois-----

OK. Obrigado a todos por responderem rápido e com entusiasmo. Eu pensei que era melhor modificar um pouco a minha pergunta do que ir e comentar todas as respostas e perguntar novamente.

Então aqui está.

Primeiro, todos responderam especificamente ao exemplo errado do Hello World. Isto é minha culpa. Eu deveria ter mantido esse genérico. O exemplo Hello world traz em questão os dispositivos de saída e como o processamento não se limita apenas à CPU, que é justamente apresentada em suas respostas.

Muitos de vocês também notaram que a CPU faz mais do que apenas adicionar. Eu concordo com isso. Eu simplesmente não escrevi isso e assumi tudo. Pelo que entendi, este é o processo:

  1. leia as instruções da memória (usando barramentos de dados e endereço e itens do contador de programas)

    1. armazenar dados no registro dentro da CPU
    2. Agora, a ALU realiza operações aritméticas, é claro, após decodificar a instrução, ou dá um pulo se for uma instrução if like
    3. E depois se comunicar com outros recursos, se necessário, como com o dispositivo de saída e assim por diante. Processos além disso são triviais por enquanto.

Portanto, na etapa 3, em que a CPU decodifica uma instrução e decide executar uma operação aritmética (aqui estamos assumindo que não há outra operação a ser feita, como pular a instrução atual .. como as operações aritméticas são feitas principalmente .. então vamos nos ater a isso) ) É aqui que minha visualização termina. Como uma instrução do meu programa é apenas uma operação aritmética para a CPU. Faz essa operação aritmética e essa instrução serve a seu propósito.

Espero ter me esclarecido desta vez.

PS Estou assumindo aqui que a ALU não se restringe apenas à operação aritmética real que fazemos em nossos programas, mas executa todas as instruções, que agora estão em formato binário, adicionando ou subtraindo-as, etc., para produzir o resultado que elas significam ceder. Se eu estiver errado aqui, as respostas abaixo respondem corretamente à minha pergunta.


Entendo que o compilador converte o programa em linguagem de máquina. Eu simplesmente não consigo visualizar um programa como uma operação aritmética. Embora se o programa em si é sobre a adição de dois números, então é compreensível, mas caso contrário não ... ainda !! :)
user2827893

1
Talvez você deva começar a olhar para o conjunto de instruções real das CPUs, por exemplo, muito simples como MC6502, Z80 ... e então ver que há instruções de acesso à memória, instruções de processamento de dados, ramificações ... Você poderia adivinhar como elas podem ser combinado para implementar qualquer algoritmo.
TEMLIB

3
Uma CPU definitivamente pode fazer mais do que acrescentar. É crucial mencionar que uma CPU pode fazer comparações e saltos.
Theodoros Chatzigiannakis

1
Você (parece) ainda se recusando firmemente a ver IF (tomar uma decisão) e MOVE (ler e armazenar dados), a programação é 99% IF e MOVE. A aritmética é negligenciável. Seu primeiro exemplo (Olá, mundo) não tem aritmética.
Edc65 22/09/2015

1
1. Acho que você provavelmente obterá boas respostas se fizer uma nova pergunta com sua nova confusão, em vez de editá-la para alterar o que está fazendo. Você obteve boas respostas para sua pergunta original e parece que ela pode se sustentar por si mesma. Por que não remover a edição e fazer uma nova pergunta? 2. Dito isto, não consigo entender a nova parte. Qual é exatamente a sua pergunta sobre a nova peça? Quando você diz "este é o lugar onde meus fins de visualização", que quer dizer que você não compreender o passo 3, ou você não entender o passo 3? Se você faz, o que você não entende?
DW

Respostas:


7

Você pode tentar pegar um programa simples e compilá-lo no código de máquina nativo. (O Java normalmente compila com o código da JVM, mas Andrew Tennenbaum tem um livro onde descreve como projetar uma CPU que é executada de forma nativa, assim será.) No GCC, por exemplo, você dá ao -Scomutador o compilador .

Isso indicará que qualquer coisa complicada, como E / S, é implementada chamando o sistema operacional. Embora você possa fazer o download da fonte para o kernel Linux e fazer o mesmo, o que está acontecendo é o seguinte: tudo está manipulando o estado da memória do computador, por exemplo, a lista de processos em execução ou conversando com o hardware usando endereços de memória especiais que controlam ou usam instruções especiais da CPU como ine outno x86. Geralmente, porém, apenas programas especiais chamados drivers de dispositivo conversam com um hardware específico, e o sistema operacional envia solicitações para usar o hardware no driver correto.

Especificamente, se você imprimir "Olá, mundo!" seu compilador transformará isso em um conjunto de instruções que carrega a string em um local específico (por exemplo, carregando o endereço da string na memória no %rdiregistro) e chamando uma função de biblioteca com a callinstrução Essa função de biblioteca pode encontrar o comprimento da string com um loop e chamar a chamada de sistemawrite()para escrever esse número de bytes da sequência no descritor de arquivo número 1, que é a saída padrão. Nesse ponto, o sistema operacional verifica qual é o arquivo número 1 desse processo e decide o que significa escrever nele. Se as gravações na saída padrão forem impressas na tela, haverá algum processo, como copiar os bytes para um buffer, que será lido pelo programa do terminal, que informará ao sistema de janelas quais letras colocar onde em que fonte. O sistema de janelas decide exatamente como isso deve ser, e diz ao driver de dispositivo para colocar os pixels na tela, o que é feito alterando a memória de vídeo.


Obrigado @Lorehead. Esta explicação parece ser um bom exemplo do Hello World.
user2827893

5

Sua CPU em si é burra, como você descobriu. Mas há um microcosmo de chips de hardware em torno dele. Você tem uma instrução que permite definir uma linha da CPU para alto nível, conectada a outro chip. Esse chip de hardware monitora a linha e diz: "Ei, se essa linha é alta, eu faço algo com outras linhas".

Para facilitar, essas linhas são agrupadas. Alguns são usados ​​para endereçar dispositivos, outros são usados ​​para transferir dados para esses endereços e outros são apenas "Cara, há algo importante acontecendo no meu chip".

No final, sua CPU apenas diz a algum outro chip que modifique o sinal para o monitor para que pareça "Hello World".

Google o desenho de um display de 7 segmentos. Possui fios, o que acenderá um segmento se você aplicar tensão a ele. Se você conectar agora uma linha de saída da sua CPU a uma linha da tela de 7 segmentos, a tela acenderá. Não é a CPU que faz com que o LED acenda, apenas aplica tensão às linhas, mas algumas outras coisas de hardware podem fazer coisas bacanas devido a isso.

Se sua CPU agora definir todas as linhas para H alto, o segmento 7 exibirá H, embora H não seja um número que uma CPU adicionaria ou subtrairia.

Agora, se todas as camadas concordarem com o que é necessário para tornar o display de 7 segmentos H (defina 5 linhas específicas como altas), o compilador Java poderá criar código para exibir H. Isso é obviamente bastante inconveniente - portanto, as camadas começam abstrair. A camada mais baixa começará: "Ei, existem 26 letras, vamos atribuir números a cada letra - que tal darmos à letra 'H' o número '72'? Então você pode apenas me dizer" Exibir letra 72 ", em vez de "Definir linha 309 alta, definir linha 310 alta, definir linha 498 alta, definir linha 549 alta, definir linha 3 alta". E assim cada camada começa a abstrair informações, como obter certos resultados, para que você não precise para se preocupar com eles.

Então, sim, resume-se a um mapeamento huuuuge de números ou bits, que a CPU pode realmente processar, com significados que todos na cadeia concordaram.


3

Na faculdade, como parte de um programa de graduação em CS, estudei um exemplo extenso da linguagem de transferência de registros que define uma CPU. Fui inspirado a adotar uma abordagem diferente e escrever um simulador que aceita essa notação como definição, e publiquei isso na Embedded Systems Programming (edição de março de 1989) como uma maneira de responder ao mesmo tipo de pergunta que você fez, permitindo que as pessoas construir seus entendimentos intuitivos de tais coisas.

Em sala de aula, passamos a destilar essa notação de transferência de resistência em portas lógicas reais nos registros! Ele se escreve: veja tudo o que tem o registro 'A' como destino e o código A = (caso1) ou (caso2) ... e que é expresso como forma normalizada de soma de produtos ou produto de soma.

Somente no final do curso soube que era uma CPU real: o PDP-8, se bem me lembro.

Hoje você pode alimentar o diagrama do portão em um chip de matriz lógica programável.

Essa é a essência: um registro é definido com o resultado das portas AND e OR levando de volta a outros registros. Um dos valores a serem incluídos é o valor do opcode.

Imagine então: A: = (opcode == 17 & X + Y) | (opcode == 18 & X + Z) | ...

Os cpus modernos são mais complicados, com pipelines e barramentos, mas subunidades individuais, como uma única ALU, funcionam dessa maneira.


2

Você está considerando a CPU aqui, mas há outro componente envolvido ao executar o 'Hello World': a tela!

Para a CPU, um valor na memória é apenas um número representado como um número determinado de bits (0 e 1).

Como se transforma em letras na tela é outra história: a tela também tem memória. Essa memória (memória gráfica) é mapeada para 'pixels' na tela. Cada pixel é codificado com um valor: se for uma exibição monocromática muito básica, o valor é apenas intensidade, para as exibições em cores o valor é uma combinação de verde vermelho e azul (RGB), que pode ser codificado de várias maneiras diferentes.

Portanto, quando a CPU 'grava' um determinado valor na memória de exibição, os pixels acendem. Para escrever cartas, é necessário iluminar muitos pixels. Normalmente, um computador terá um conjunto de caracteres (na verdade vários) definido em seu sistema operacional. (fazendo abstração das próprias fontes, que mapeia para uma definição da aparência de cada letra na tela)

Assim, como o código é compilado, ele inclui todos os tipos de coisas provenientes das bibliotecas do sistema operacional, incluindo esses conjuntos de fontes / caracteres, etc., que permitem à CPU saber o que escrever na memória gráfica. (É bastante complexo, mas essa é a ideia geral: o compilador inclui muito mais código do que o que está no código do 'hello world' sozinho, por meio de bibliotecas importadas)

No final, muitas coisas aconteceram como você suspeita, mas você não precisava escrever todo esse código.


1

Aqui está uma abordagem formal para sua pergunta no campo da ciência da computação teórica.

Basicamente, podemos definir um mapeamento entre o modelo de computação de uma CPU e uma máquina de turing. Existem provas teóricas de que o conjunto de todos os programas imagináveis ​​de máquinas de turing (e, portanto, todos os programas imagináveis ​​executáveis ​​em uma CPU) é contável infinito. Isso significa que podemos identificar todos os programas com um número natural único, incluindo o programa que estenderia os números naturais às máquinas de turing .

Como você já sabe que quase tudo o que as CPUs fazem é computar números naturais na representação binária, você pode raciocinar que as CPUs podem executar todos os programas imagináveis.

Nota: Isso é muito simplificado, mas, na minha opinião, fornece uma boa intuição.


1

O que pode ajudar é desviar seu pensamento de "fazer aritmética". Se você está realmente tentando entender o que os computadores estão fazendo sob o capô para imprimir "Hello World", é melhor pensar um nível mais baixo. O "estado" do computador pode ser descrito como um conjunto de bits, armazenados por chaves de transistor que estão ativadas ou desativadas (ou capacitores que são carregados ou descarregados). O computador manipula esses bits de acordo com as regras. As maneiras pelas quais o computador pode manipular esses bits são gravadas na CPU na forma de transistores que realizam o trabalho de alterar os bits de 0 para 1 ou 1 para 0.

Quando uma ULA "aritmética", o que isso realmente significa é que ela mudou o estado do computador de maneira consistente com nossas regras aritméticas. Tudo o que fez foi mudar alguns bits. É o significado por trás do software que explica por que devemos pensar nele como adição ou subtração. A CPU não "sabe" o que está fazendo. Apenas muda de estado para estado, e é tudo (pelo menos até a Skynet assumir).

Quando você pensa dessa maneira, instruções mais complicadas, como uma instrução de "salto", não são diferentes. Tudo o que faz é mudar alguns bits. Nesse caso, altera os bits que sabemos que significam o local da próxima instrução a ser executada. A CPU não "sabe" isso, mas nós sabemos. Então, usamos a instrução que altera esses bits para "pular" de um lugar para outro em nosso código.

O IO também não é diferente, está apenas mudando os bits. A única diferença menor é que esses bits estão conectados aos transistores, o que eventualmente leva à iluminação dos caracteres na tela. Se eu puder relembrar algumas décadas até quando "Hello World" era realmente simples, havia um espaço de memória em que, se você escrevesse bits correspondentes aos caracteres ASCII de "Hello World", esses caracteres seriam renderizados diretamente no tela. Hoje em dia é um pouco mais complicado, porque temos placas gráficas e sistemas operacionais que mexem com ela, mas a idéia básica é a mesma. Você tem um conjunto de transistores que estão ligados ou desligados, que estão ligados ao circuito para exibir um pixel na tela. Definimos os corretos e parece que "Hello World" aparece na tela.

A confusão é simplesmente uma questão de sintaxe versus semântica. O comportamento de uma "meia-adição" ou "uma adição completa" em uma ALU é uma sintaxe. Ele define quais bits serão exibidos quando você os inserir. A semântica é o conceito da capacidade de fazer adição. Você e eu estamos cientes de que a ALU pode "fazer acréscimos", mas para realmente entender o que acontece por baixo, é preciso lembrar que uma ALU manipula apenas os bits e bytes da sintaxe.


0

As CPUs funcionam assim:

  • buscar instruções atuais, incrementar o ponteiro "instruções atuais".

  • decodificá-lo (por exemplo, descubra o que esta instrução está dizendo à CPU)

  • execute-o (faça o que a instrução diz) - o ponteiro de instrução atual pode ser modificado se a instrução for algo como um "salto".

  • Repita para sempre

As CPUs modernas são mais complexas e tentam se sobrepor e até prever partes desse processo (por exemplo, comece a executar enquanto outras 10 instruções estão decodificando enquanto a CPU está muito à frente do ponteiro "instrução atual" para manter os "pipelines" cheios), mas o processo essencial é realmente o mesmo.

Existem muitos tipos de instruções, um exemplo da maioria deles é:

  • Instruções "Mover". Eles podem copiar X para outro X, onde X é memória (RAM), um registro ou um endereço no espaço de E / S, se a CPU suportar essa noção.

  • Instruções de manipulação de pilha, incluindo pop no registro, push push na pilha, etc. Este é um caso especial de instruções de "movimentação" que usam e atualizam um registro "ponteiro da pilha".

  • Instruções que executam operações matemáticas, entre dois registros ou memória e um registro. Essas instruções afetam automaticamente um registro de sinalizadores. Um desses sinalizadores é o sinalizador "zero" que é definido se o resultado for zero, outro é o sinalizador "negativo" que é definido se o bit mais significativo do resultado for definido. Pode haver outros, dependendo da CPU.

  • Um caso especial de operações matemáticas são as instruções de comparação, que é o mesmo que uma subtração, mas o resultado não é mantido. Os sinalizadores ainda são afetados.

  • Existem instruções de ramificação que saltam para um endereço de memória se sinalizadores específicos estiverem definidos. Lembre-se do sinalizador "zero" mencionado acima? Também funciona como o sinalizador "se igual", para que você veja instruções como BEQem muitas CPUs, que na verdade ramificam se o sinalizador "zero" estiver definido.

  • Instruções que executam operações lógicas (AND, OR, NOT), bits de deslocamento e bits de teste. Eles podem afetar sinalizadores como instruções matemáticas, dependendo da CPU.

  • Instruções que saltam incondicionalmente.

  • Instruções que saltam e salvam o endereço de retorno na pilha (uma "chamada") e outras instruções que retiram um endereço da pilha (uma "devolução").

  • Instruções especiais como aquelas que interrompem a CPU, identificam a CPU ou chamam manipuladores de interrupção.

  • "No Operation" - quase todas as CPUs possuem uma instrução "no-op" que consome ciclos e segue em frente.

Este é realmente apenas um exemplo, existem CPUs com menos tipos de instruções e CPUs com mais.

O objetivo é ilustrar que existem muitos tipos de instruções além das instruções matemáticas em uma CPU. Tudo em um idioma de nível superior é dividido nos tipos de operações acima e apenas alguns deles são instruções matemáticas ou do tipo ALU.

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.