Jak program jest wykonywany na poziomie procesora?

14

Wiem, że to bardzo częste pytanie. Ale mam inny punkt widzenia. Spróbuję to tutaj wyrazić.

Z tego, co wiem, każda instrukcja wykonywana przez procesor jest w języku maszynowym i wszystko, co może zrobić, to wykonywanie operacji arytmetycznych dzięki ALU i jego tranzystorom (jeśli idziemy na poziomie sprzętowym).

Jest to jednak łatwiejsze do wpisania niż do zrozumienia. Więc jeśli wszystko, co robi procesor, to dodawanie, odejmowanie itp., To w jaki sposób program, powiedzmy program JAVA z komunikatem print Hello World, wykonany za pomocą tych operacji arytmetycznych?

Mam na myśli, jak ten program jest konwertowany na coś, co jest tylko dodatkiem do procesora?

PS Jeśli to pytanie nie dotyczy tej witryny, przepraszam.

-----Część druga-----

W porządku. Dziękujemy wszystkim za szybką odpowiedź i entuzjazm. Pomyślałem, że lepiej trochę zmodyfikować moje pytanie, niż komentować wszystkie odpowiedzi i zadawać je ponownie.

Więc oto jest.

Po pierwsze, wszyscy odpowiedzieli konkretnie na przykład Hello World. To moja wina. Powinienem był zachować to ogólne. Przykład Hello world stawia pytanie o urządzenia wyjściowe i o to, jak jego przetwarzanie nie ogranicza się tylko do procesora, co słusznie pojawia się w twoich odpowiedziach.

Również wielu z was zwróciło mi uwagę, że procesor robi coś więcej niż tylko dodawanie. Zgadzam się z tym. Po prostu tego nie napisałem i przyjąłem to do końca. Z tego, co rozumiem, jest to proces:

  1. odczytać instrukcję z pamięci (używając magistrali danych i adresów oraz liczników programów)

    1. przechowywać dane w rejestrze wewnątrz procesora
    2. Teraz ALU wykonuje operacje arytmetyczne, oczywiście po dekodowaniu instrukcji, lub wykonaj skok, jeśli jest to instrukcja podobna
    3. A następnie w razie potrzeby komunikowanie się z innymi zasobami, np. Z urządzeniem wyjściowym i tak dalej. Procesy poza tym są na razie trywialne.

Więc w kroku 3, w którym CPU dekoduje instrukcję i decyduje się wykonać operację arytmetyczną (tutaj zakładamy, że nie ma innej operacji do wykonania, jak przeskoczenie bieżącej instrukcji .. ponieważ operacje arytmetyczne są w większości wykonywane .. więc będziemy trzymać się tego ) Tutaj kończy się moja wizualizacja. Jak instrukcja z mojego programu jest po prostu operacją arytmetyczną dla CPU. Wykonuje tę operację arytmetyczną i ta instrukcja służy swojemu celowi.

Mam nadzieję, że tym razem wyraziłem się jasno.

PS Przyjmuję tutaj duże założenie, że ALU nie ogranicza się tylko do faktycznej operacji arytmetycznej, którą wykonujemy w naszych programach, a raczej wykonuje wszystkie instrukcje, które są teraz w formie binarnej, dodając lub odejmując je itp., Aby uzyskać wynik, że są przeznaczone poddać się. Jeśli się tutaj mylę, poniższe odpowiedzi poprawnie odpowiedzą na moje pytanie.

użytkownik 2827893
źródło
Rozumiem, że kompilator konwertuje program na język maszynowy. Nie mogę wizualizować programu jako operacji arytmetycznej. Chociaż jeśli sam program polega na dodawaniu dwóch liczb, jest to zrozumiałe, ale poza tym nie… tak! :)
user2827893,
1
Może powinieneś zacząć przyglądać się rzeczywistemu zestawowi instrukcji procesorów, na przykład bardzo prostym, takim jak MC6502, Z80 ... a potem zobaczyć, że są instrukcje dostępu do pamięci, instrukcje przetwarzania danych, gałęzie ... Możesz wtedy zgadnąć, jak mogą być połączone w celu wdrożenia dowolnego algorytmu.
TEMLIB
3
Procesor zdecydowanie może zrobić więcej niż dodawanie. Należy wspomnieć, że procesor może wykonywać porównania i skakać.
Theodoros Chatzigiannakis
1
(Wydajesz się) wciąż zdecydowanie odmawiasz zobaczenia JEŻELI (podejmowanie decyzji) i MOVE (odczytywanie i przechowywanie danych), programowanie to 99% JEŻELI i MOVE. Arytmetyka jest pomijalna. Twój pierwszy przykład (Hello world) w ogóle nie ma arytmetyki.
edc65
1
1. Myślę, że bardziej prawdopodobne byłoby uzyskanie dobrych odpowiedzi, gdybyś zadał nowe pytanie z nowym zamieszaniem, niż edytować to pytanie, aby zmienić to, o co pytasz. Masz dobre odpowiedzi na swoje pierwotne pytanie, a twoje oryginalne pytanie wydaje się być samodzielne, więc dlaczego nie usunąć edycji i zadać nowe pytanie? 2. To powiedziawszy, nie rozumiem nowej części. Jakie dokładnie jest twoje pytanie dotyczące nowej części? Kiedy mówisz „na tym kończy się moja wizualizacja”, masz na myśli, że rozumiesz krok 3, czy nie rozumiesz kroku 3? Jeśli tak, to czego nie rozumiesz?
DW

Odpowiedzi:

7

Możesz spróbować pobrać prosty program i skompilować go do natywnego kodu maszynowego. (Java zwykle kompiluje się do kodu JVM, ale Andrew Tennenbaum ma książkę, w której opisuje, jak zaprojektować procesor, który działa tak natywnie, więc tak zrobi.) Na przykład w GCC dajesz kompilatorowi -Sprzełącznik.

Dzięki temu dowiesz się, że cokolwiek trudnego, np. We / wy, jest realizowane przez wywołanie systemu operacyjnego. Chociaż możesz pobrać źródło do jądra Linuksa i zrobić to samo, pod maską jest: wszystko manipuluje stanem pamięci komputera, na przykład listą uruchomionych procesów lub rozmową ze sprzętem za pomocą specjalne adresy pamięci, które to kontrolują lub za pomocą specjalnych instrukcji procesora, takich jak ini outna x86. Zasadniczo jednak tylko specjalne programy zwane sterownikami urządzeń będą rozmawiać z określonym sprzętem, a system operacyjny wyśle ​​żądania użycia sprzętu do odpowiedniego sterownika.

W szczególności, jeśli wydrukujesz „cześć, świecie!” Twój kompilator zmieni to w zestaw instrukcji, które ładują ciąg do określonej lokalizacji (na przykład ładując adres ciągu do pamięci do %rdirejestru) i wywołując funkcję biblioteki z callinstrukcją. Ta funkcja biblioteki może znaleźć długość łańcucha za pomocą pętli, a następnie wywołać wywołanie systemowewrite()aby zapisać tę liczbę bajtów z ciągu na deskryptor pliku o numerze 1, który jest standardowym wyjściem. W tym momencie system operacyjny sprawdza, co to jest plik nr 1 tego procesu i decyduje, co znaczy do niego pisać. Jeśli zapisy na standardowe wyjście zostaną wydrukowane na ekranie, nastąpi pewien proces, taki jak kopiowanie bajtów do bufora, który następnie jest odczytywany przez program terminalowy, który informuje system okienkowy, które litery należy umieścić w jakiej czcionce. System okien decyduje dokładnie, jak powinien wyglądać, i mówi sterownikowi urządzenia, aby umieścił piksele na ekranie, co robi, zmieniając pamięć wideo.

Davislor
źródło
Dzięki @Lorehead. To wyjaśnienie wygląda dobrze na przykład Hello world.
user2827893,
5

Twój procesor sam w sobie jest głupi, jak się zorientowałeś. Ale wokół jest mikrokosmos układów sprzętowych. Masz instrukcję, która pozwala ustawić jeden wiersz procesora na wysoki poziom, który jest podłączony do innego układu. Ten układ sprzętowy monitoruje linię i mówi: „Hej, jeśli ta linia jest wysoka, to robię coś z innymi liniami”.

Aby to ułatwić, linie te są zgrupowane razem. Niektóre służą do adresowania urządzeń, niektóre służą do przesyłania danych dla tych adresów, a inne to po prostu „Koleś, w moim chipie dzieje się coś ważnego”.

W końcu twój procesor po prostu każe ładnemu układowi, aby zmodyfikował sygnał tak, aby wyglądał jak „Hello World”.

Google rysunek 7-segmentowego wyświetlacza. Ma przewody, które rozjaśnią segment, jeśli przyłożymy do niego napięcie. Jeśli teraz połączysz jedną linię wyjściową procesora z jedną linią wyświetlacza 7-segmentowego, wyświetlacz zaświeci się. To nie procesor powoduje, że dioda LED świeci, po prostu przykłada napięcie do linii, ale niektóre inne rzeczy sprzętowe mogą z tego powodu robić fajne rzeczy.

Jeśli twój procesor ustawia teraz wszystkie linie dla H na wysoką, 7-segmentowy wyświetli H, chociaż H nie jest liczbą, którą CPU dodawałby lub odejmował.

Teraz, jeśli wszystkie warstwy zgadzają się, co jest konieczne, aby wyświetlić 7-segmentowy wyświetlacz H (ustawić 5 określonych wierszy na wysoki), kompilator Java może ustawić kod tak, aby wyświetlał H. Jest to oczywiście raczej niewygodne - więc warstwy zaczynają się do abstrakcji. Najniższa warstwa rozpocznie się od: „Tak, jest jak 26 liter, przypiszmy cyfry do każdej litery - co powiesz na literę„ H ”, liczbę„ 72 ”? Następnie możesz mi powiedzieć„ Wyświetl literę 72 ”, zamiast "Ustaw linię 309 wysoko, ustaw linię 310 wysoko, ustaw linię 498 wysoko, ustaw linię 549 wysoko, ustaw linię 3 wysoko". I tak każda warstwa zaczyna wyodrębniać informacje, jak osiągnąć określone wyniki, więc nie potrzebujesz dbać o nich.

Tak więc, podsumowuje się do huuuuge mapowania liczb lub bitów, procesor może faktycznie przetwarzać, do znaczeń, które wszyscy w łańcuchu uzgodnili.

John Hammond
źródło
3

Na studiach jako część programu studiów CS studiowałem rozszerzony przykład języka transferu rejestru definiującego procesor. Zainspirowałem się, aby podjąć inne podejście i napisać symulator, który przyjmuje taką notację jako definicję, i opublikowałem ją w Embedded Systems Programming (wydanie z marca 1989 r.) Jako sposób na udzielenie odpowiedzi na to samo pytanie, które zadałeś, pozwalając ludziom budować intuicyjne zrozumienie takich rzeczy.

W klasie kontynuowaliśmy destylację tego zapisu przeniesienia do rzeczywistych bramek logicznych w rejestrach! Pisze się: spójrz na wszystko, co ma „A” jako miejsce docelowe, a kod A = (case1) lub (case2) ... i to wyraża się w postaci znormalizowanej sumy produktów lub iloczynu sumy produktów.

Dopiero pod koniec kursu dowiedziałem się, że to był prawdziwy procesor: PDP-8, jeśli dobrze pamiętam.

Dzisiaj możesz wprowadzić schemat bramki do programowalnego układu matrycy logicznej.

Na tym polega sedno: rejestr jest ustawiany z wynikiem bramek AND i OR prowadzących z powrotem do innych rejestrów. Jedną z wartości, które należy uwzględnić, jest wartość opcode.

Więc wyobraź sobie: A: = (opcode == 17 i X + Y) | (opcode == 18 i X + Z) | ...

Nowoczesne procesory są bardziej skomplikowane, z rurociągami i autobusami, ale poszczególne podjednostki, takie jak pojedyncza jednostka ALU, działają w ten sposób.

JDługosz
źródło
2

Rozważasz tutaj procesor, ale podczas uruchamiania „Hello World” zaangażowany jest inny komponent: wyświetlacz!

Dla procesora wartość w pamięci to tylko liczba reprezentowana jako dana liczba bitów (0 i 1).

Jak zamienia się w litery na ekranie to inna historia: wyświetlacz ma również pamięć. Ta pamięć (pamięć graficzna) jest mapowana na „piksele” na ekranie. Każdy piksel jest kodowany za pomocą wartości: jeśli jest to bardzo prosty monochromatyczny wyświetlacz, wartość jest po prostu intensywnością, w przypadku wyświetlaczy kolorowych wartość jest kombinacją Czerwonego Zielonego i Niebieskiego (RGB), które można zakodować na wiele różnych sposobów.

Kiedy procesor „zapisuje” określoną wartość do pamięci wyświetlacza, piksele zaświecają się. Aby pisać litery, trzeba rozjaśnić wiele pikseli. Zazwyczaj komputer ma zestaw znaków (a właściwie kilka) zdefiniowanych w systemie operacyjnym. (dokonanie abstrakcji samych „czcionek”, które odwzorowuje definicję tego, jak każda litera powinna wyglądać na ekranie)

Tak więc, gdy kod jest kompilowany, zawiera on różne rzeczy, które pochodzą z bibliotek systemu operacyjnego, w tym zestaw czcionek / znaków itp., Które pozwalają procesorowi wiedzieć, co napisać w pamięci graficznej. (Jest to dość skomplikowane, ale taki jest ogólny pomysł: kompilator zawiera o wiele więcej kodu niż sam kod „hello world” za pośrednictwem importowanych bibliotek)

W końcu wydarzyło się wiele rzeczy, jak podejrzewasz, ale nie musiałeś pisać całego tego kodu.

MrE
źródło
1

Oto formalne podejście do twojego pytania z dziedziny informatyki teoretycznej.

Zasadniczo możemy zdefiniować odwzorowanie między modelem obliczeniowym procesora a maszyną Turinga. Istnieją teoretyczne dowody, że zestaw wszystkich możliwych do wyobrażenia programów maszynowych Turinga (a zatem wszystkich możliwych do wyobrażenia programów wykonywalnych na CPU) jest policzalny nieskończony. Oznacza to, że możemy zidentyfikować każdy program za pomocą unikalnej liczby naturalnej, w tym program, który rozszerzyłby liczby naturalne na maszyny Turinga .

Jak już wiesz, że prawie wszystko, co robią procesory, to obliczenia na liczbach naturalnych w reprezentacji binarnej, możesz uzasadnić, że procesory mogą wykonywać każdy możliwy program.

Uwaga: Jest to zbyt uproszczone, ale moim zdaniem daje niezłą intuicję.

Kevin Dreßler
źródło
1

Pomocne może być odwrócenie myślenia od „robienia arytmetyki”. Jeśli naprawdę chcesz dowiedzieć się, co robią komputery pod maską, aby wydrukować „Hello World”, najlepiej pomyśleć o jeden poziom niżej. „Stan” komputera można opisać jako zestaw bitów przechowywanych przez przełączniki tranzystorowe, które są włączone lub wyłączone (lub kondensatory, które są naładowane lub nienaładowane). Komputer manipuluje tymi bitami zgodnie z regułami. Sposoby manipulowania tymi bitami przez komputer są zapisywane na CPU w postaci tranzystorów, które wykonują zmianę bitów od 0 do 1 lub od 1 do 0.

Kiedy ALU „wykonuje arytmetykę”, tak naprawdę oznacza to, że zmieniło stan komputera w sposób zgodny z naszymi zasadami arytmetyki. Wszystko, co zrobiła, to zmieniło kilka bitów. To znaczenie oprogramowania wyjaśnia, dlaczego powinniśmy myśleć o nim jako o dodawaniu lub odejmowaniu. Procesor nie „wie”, co robi. Po prostu zmienia się ze stanu do stanu i to wszystko (przynajmniej dopóki Skynet nie przejmie władzy).

Kiedy myślisz o tym w ten sposób, bardziej skomplikowane instrukcje, takie jak „skok”, nie różnią się. Wszystko, co robi, to zmiana niektórych bitów. W tym przypadku zdarza się, że zmiana bitów, o których wiemy, oznacza lokalizację następnej instrukcji do wykonania. Procesor tego nie „wie”, ale my wiemy. Dlatego używamy instrukcji, która zmienia te bity, aby „przeskakiwać” z miejsca na miejsce w naszym kodzie.

IO tak naprawdę nie jest inaczej, tylko zmienia bity. Jedyną niewielką różnicą jest to, że te bity są podłączone do tranzystorów, co ostatecznie prowadzi do rozjaśniania postaci na ekranie. Gdybym mógł przypomnieć sobie kilka dziesięcioleci, kiedy „Hello World” był w rzeczywistości prosty, istniałaby przestrzeń pamięci, w której, jeśli napisałeś do niego bity odpowiadające znakom ASCII dla „Hello World”, znaki te byłyby renderowane bezpośrednio do ekran. W dzisiejszych czasach jest to trochę bardziej skomplikowane, ponieważ mamy do czynienia z kartami graficznymi i systemami operacyjnymi, które się z tym psują, ale podstawowa idea jest taka sama. Masz zestaw tranzystorów, które są włączone lub wyłączone, które są połączone z obwodami, aby wyświetlać piksel na ekranie. Ustawiamy właściwe i wygląda na to, że na ekranie pojawia się „Hello World”.

Zamieszanie jest po prostu kwestią składni vs. semantyki. Zachowanie „dodanej do połowy” lub „pełnej dodanej” w ALU jest składnią. Definiuje to, jakie bity wyjdą po włożeniu bitów. Ich semantyką jest koncepcja umiejętności dodawania. Ty i ja zdajemy sobie sprawę, że ALU może „robić dodawanie”, ale aby naprawdę zrozumieć, co dzieje się poniżej, musisz pamiętać, że ALU manipuluje tylko bitami i bajtami składni.

Cort Ammon
źródło
0

Procesory działają w następujący sposób:

  • pobierz bieżącą instrukcję, zwiększ wskaźnik „bieżąca instrukcja”.

  • zdekoduj (np. dowiedz się, co ta instrukcja mówi procesorowi)

  • wykonaj go (zrób to, co mówi instrukcja) - aktualny wskaźnik instrukcji może zostać zmodyfikowany, jeśli instrukcja jest czymś w rodzaju „skoku”.

  • Powtarzaj na zawsze

Współczesne procesory są bardziej złożone i starają się w dużym stopniu nakładać, a nawet przewidywać części tego procesu (np. Rozpocząć wykonywanie, gdy dekoduje się 10 innych instrukcji, podczas gdy procesor pobiera znacznie przed wskaźnikiem „bieżącej instrukcji”, aby utrzymać „potoki” pełne), ale niezbędny proces jest naprawdę taki sam.

Istnieje wiele rodzajów instrukcji, przykładem większości z nich są:

  • Instrukcje „Przenieś”. Mogą one skopiować X do innego X, gdzie X to pamięć (RAM), rejestr lub adres w przestrzeni we / wy, jeśli procesor obsługuje takie pojęcie.

  • Instrukcje manipulacji na stosie, w tym pop-rejestr, rejestr push na stosie itp. Są to szczególne przypadki instrukcji „przenoszenia”, które używają i aktualizują rejestr „wskaźnika wskaźnika stosu”.

  • Instrukcje, które wykonują operacje matematyczne, między dwoma rejestrami lub pamięcią a rejestrem. Te instrukcje automatycznie wpływają na rejestr flag. Jedną z takich flag jest flaga „zero”, która jest ustawiana, jeśli wynik jest zerowy, inna to flaga „ujemna”, która jest ustawiana, jeśli ustawiony jest najbardziej znaczący bit wyniku. Mogą być inne w zależności od procesora.

  • Szczególnym przypadkiem operacji matematycznych są instrukcje porównania, które są takie same jak odejmowanie, ale wynik nie jest zachowywany. Flagi są nadal naruszone.

  • Istnieją instrukcje rozgałęzień, które przeskakują na adres pamięci JEŻELI ustawione są określone flagi. Pamiętasz wspomnianą wyżej flagę „zero”? Podwaja się również jako flaga „jeśli równa się”, więc widzisz instrukcje jak BEQna wielu procesorach, które faktycznie rozgałęziają się, jeśli ustawiona jest flaga „zero”.

  • Instrukcje, które wykonują operacje logiczne (AND, OR, NOT), bity shift i bity testowe. Mogą wpływać na flagi, takie jak instrukcje matematyczne, w zależności od procesora.

  • Instrukcje, które skaczą bezwarunkowo.

  • Instrukcje, które skaczą i zapisują adres zwrotny na stosie („wywołanie”), oraz inne instrukcje, które usuwają adres ze stosu („zwrot”).

  • Specjalne instrukcje, takie jak te, które zatrzymują procesor, identyfikują procesor lub procedury obsługi przerwań połączeń.

  • „No Operation” - prawie wszystkie procesory mają instrukcję „no-op”, która po prostu zużywa cykle i przechodzi dalej.

To naprawdę tylko przykład, są procesory z mniejszą liczbą instrukcji i procesory z większą liczbą.

Chodzi o to, aby zilustrować, że istnieje wiele rodzajów instrukcji oprócz instrukcji matematycznych w CPU. Wszystko w języku wyższego poziomu jest podzielone na powyższe typy operacji i tylko niektóre z nich będą instrukcjami matematycznymi lub ALU.

LawrenceC
źródło