Tłumacz ustny (informatyka) - Interpreter (computing)

W3sDesign Interpreter wzorca projektowego UML

W informatyce , interpreter jest program komputerowy , który bezpośrednio wykonuje instrukcje napisane w programowania lub języka skryptowego , bez konieczności ich uprzednio zostały skompilowane w języku maszynowym programu. Interpreter zazwyczaj używa jednej z następujących strategii wykonywania programu:

  1. Przetwarza się kod źródłowy i wykonać jego zachowanie bezpośrednio;
  2. Przetłumacz kod źródłowy na jakąś wydajną reprezentację pośrednią lub kod obiektowy i natychmiast to wykonaj;
  3. Jawne wykonanie zapisanego, prekompilowanego kodu utworzonego przez kompilator, który jest częścią systemu interpretera.

Wczesne wersje języka programowania Lisp oraz minikomputerowych i mikrokomputerowych dialektów BASIC byłyby przykładami pierwszego typu. Perl , Raku , Python , MATLAB i Ruby są przykładami drugiego typu, podczas gdy UCSD Pascal jest przykładem trzeciego typu. Programy źródłowe są kompilowane z wyprzedzeniem i przechowywane jako kod niezależny od maszyny, który jest następnie łączony w czasie wykonywania i wykonywany przez interpreter i/lub kompilator (dla systemów JIT ). Niektóre systemy, takie jak Smalltalk i współczesne wersje BASIC i Java, mogą również łączyć dwa i trzy. Interpretery różnych typów zostały również skonstruowane dla wielu języków tradycyjnie kojarzonych z kompilacją, takich jak Algol , Fortran , Cobol , C i C++ .

Chociaż interpretacja i kompilacja to dwa główne sposoby implementacji języków programowania, nie wykluczają się one wzajemnie, ponieważ większość systemów tłumaczeniowych wykonuje również pewne prace tłumaczeniowe, podobnie jak kompilatory. Terminy „ język interpretowany ” lub „ język kompilowany ” oznaczają, że kanoniczną implementacją tego języka jest odpowiednio interpreter lub kompilator. Język wysokiego poziomu jest idealnie abstrakcją niezależną od poszczególnych implementacji.

Historia

Interpretery były używane już w 1952 roku, aby ułatwić programowanie w ramach ograniczeń ówczesnych komputerów (np. brak miejsca do przechowywania programów lub brak natywnej obsługi liczb zmiennoprzecinkowych). Interpretery były również wykorzystywane do tłumaczenia między językami maszynowymi niskiego poziomu, umożliwiając pisanie kodu dla maszyn, które były jeszcze w budowie i testowane na już istniejących komputerach. Pierwszym interpretowanym językiem wysokiego poziomu był Lisp . Lisp został po raz pierwszy zaimplementowany w 1958 roku przez Steve'a Russella na komputerze IBM 704 . Russell przeczytał artykuł Johna McCarthy'ego i zdał sobie sprawę (ku zdziwieniu McCarthy'ego), że funkcja eval Lisp może być zaimplementowana w kodzie maszynowym. Rezultatem był działający interpreter Lispu, który mógł być używany do uruchamiania programów Lisp, a dokładniej, "oceniania wyrażeń Lisp".

Kompilatory kontra interpretatorzy

Ilustracja procesu łączenia. Pliki obiektowe i biblioteki statyczne są składane w nową bibliotekę lub plik wykonywalny

Programy napisane w języku wysokiego poziomu są albo bezpośrednio wykonywane przez jakiś rodzaj interpretera, albo konwertowane na kod maszynowy przez kompilator (oraz assembler i linker ) do wykonania przez procesor .

Podczas gdy kompilatory (i asemblery) zazwyczaj wytwarzają kod maszynowy bezpośrednio wykonywalny przez sprzęt komputerowy, często (opcjonalnie) mogą tworzyć formę pośrednią zwaną kodem wynikowym . Jest to w zasadzie ten sam kod specyficzny dla maszyny, ale wzbogacony o tablicę symboli z nazwami i znacznikami, aby umożliwić identyfikację i przemieszczenie wykonywalnych bloków (lub modułów). Programy skompilowane zazwyczaj używają bloków konstrukcyjnych (funkcji) przechowywanych w bibliotece takich modułów kodu obiektowego. Łącznik służy do łączenia (pre-made) pliki biblioteki z pliku (ów) Przedmiot wniosku, aby utworzyć pojedynczy plik wykonywalny. Pliki obiektowe używane do generowania pliku wykonywalnego są zatem często tworzone w różnym czasie, a czasem nawet w różnych językach (zdolne do generowania tego samego formatu obiektowego).

Prosty interpreter napisany w języku niskiego poziomu (np. assembler ) może mieć podobne bloki kodu maszynowego implementujące funkcje języka wysokiego poziomu przechowywane i wykonywane, gdy wpis funkcji w tablicy przeglądowej wskazuje na ten kod. Jednak interpreter napisany w języku wysokiego poziomu zazwyczaj stosuje inne podejście, takie jak generowanie, a następnie chodzenie po drzewie analizy , generowanie i wykonywanie pośrednich instrukcji zdefiniowanych przez oprogramowanie lub jedno i drugie.

Tak więc zarówno kompilatory, jak i interpretery generalnie zamieniają kod źródłowy (pliki tekstowe) na tokeny, oba mogą (lub nie) generować drzewo parsowania i oba mogą generować natychmiastowe instrukcje (dla maszyny ze stosem , poczwórny kod lub w inny sposób). Podstawowa różnica polega na tym, że system kompilatora, w tym (wbudowany lub oddzielny) linker, generuje samodzielny program kodu maszynowego , podczas gdy system interpretera zamiast tego wykonuje czynności opisane przez program wysokiego poziomu.

Kompilator może więc raz na zawsze wykonać prawie wszystkie konwersje z semantyki kodu źródłowego na poziom maszyny (tzn. dopóki program nie będzie musiał zostać zmieniony), podczas gdy interpreter musi wykonać część tej pracy związanej z konwersją za każdym razem, gdy wykonywana jest instrukcja lub funkcja . Jednak w wydajnym interpreterze większość prac tłumaczeniowych (w tym analiza typów i tym podobne) jest uwzględniana i wykonywana tylko przy pierwszym uruchomieniu programu, modułu, funkcji, a nawet instrukcji, a zatem jest to całkiem podobne do tego, jak kompilator działa. Jednak skompilowany program nadal działa znacznie szybciej, w większości przypadków, po części dlatego, że kompilatory są zaprojektowane do optymalizacji kodu i mogą mieć na to wystarczająco dużo czasu. Jest to szczególnie ważne w przypadku prostszych języków wysokiego poziomu bez (wielu) dynamicznych struktur danych, kontroli lub sprawdzania typu .

W tradycyjnej kompilacji, wykonywalny dane wyjściowe konsolidatorów (pliki .exe lub .dll lub biblioteka, patrz rysunek) są zazwyczaj relokowalne, gdy są uruchamiane w ogólnym systemie operacyjnym, podobnie jak moduły kodu wynikowego, ale z tą różnicą, że ta relokacja odbywa się dynamicznie w czasie wykonywania, tj. gdy program jest ładowany do wykonania. Z drugiej strony, skompilowane i połączone programy dla małych systemów wbudowanych są zazwyczaj przydzielane statycznie, często zakodowane na stałe w pamięci flash NOR , ponieważ często nie ma dodatkowej pamięci masowej ani systemu operacyjnego w tym sensie.

Historycznie większość systemów interpreterów miała wbudowany samodzielny edytor. Staje się to coraz bardziej powszechne również w przypadku kompilatorów (wówczas często nazywanych IDE ), chociaż niektórzy programiści wolą używać dowolnego edytora i uruchamiać kompilator, linker i inne narzędzia ręcznie. Historycznie kompilatory wyprzedzają interpretery, ponieważ sprzęt w tym czasie nie mógł obsługiwać zarówno interpretera, jak i interpretowanego kodu, a typowe środowisko wsadowe w tamtych czasach ograniczało zalety interpretacji.

Cykl rozwoju

Podczas cyklu tworzenia oprogramowania programiści często dokonują zmian w kodzie źródłowym. Podczas korzystania z kompilatora, za każdym razem, gdy dokonywana jest zmiana w kodzie źródłowym, musi on poczekać, aż kompilator przetłumaczy zmienione pliki źródłowe i powiąże wszystkie pliki kodu binarnego, zanim program będzie mógł zostać wykonany. Im większy program, tym dłużej trzeba czekać. W przeciwieństwie do tego, programista używający interpretera znacznie mniej czeka, ponieważ interpreter zwykle musi tylko przetłumaczyć kod, nad którym pracuje, na reprezentację pośrednią (lub nie tłumaczyć go w ogóle), przez co potrzebuje znacznie mniej czasu, zanim zmiany będą mogły być przetestowany. Efekty są widoczne po zapisaniu kodu źródłowego i ponownym załadowaniu programu. Skompilowany kod jest generalnie trudniejszy do debugowania, ponieważ edycja, kompilacja i łączenie są procesami sekwencyjnymi, które muszą być przeprowadzane we właściwej kolejności za pomocą odpowiedniego zestawu poleceń. Z tego powodu wiele kompilatorów ma również pomoc wykonawczą, znaną jako plik i program Make . Plik Make zawiera wiersze poleceń kompilatora i konsolidatora oraz pliki kodu źródłowego programu, ale może pobierać proste dane wejściowe z menu wiersza poleceń (np. „Make 3”), które wybiera trzecią grupę (zestaw) instrukcji, a następnie wydaje polecenia kompilatorowi i linker zasilający określone pliki kodu źródłowego.

Dystrybucja

Kompilator kod źródłowy przekształca się w binarnym instrukcji dla konkretnej architektury procesora, dzięki czemu jest mniej przenośny . Ta konwersja jest wykonywana tylko raz, w środowisku programisty, a następnie ten sam plik binarny może zostać rozesłany na maszyny użytkownika, gdzie może zostać wykonany bez dalszej translacji. Przekroju kompilator może generowania kodu binarnego dla urządzenia użytkownika, nawet jeżeli ma on innego procesora niż ten, w którym kod jest sporządzane.

Interpretowany program może być rozpowszechniany jako kod źródłowy. Musi zostać przetłumaczony na każdej końcowej maszynie, co zajmuje więcej czasu, ale uniezależnia dystrybucję programu od architektury maszyny. Jednak przenośność interpretowanego kodu źródłowego zależy od tego, czy maszyna docelowa faktycznie posiada odpowiedni interpreter. Jeśli tłumacz musi być dostarczony wraz ze źródłem, cały proces instalacji jest bardziej złożony niż dostarczenie monolitycznego pliku wykonywalnego, ponieważ sam tłumacz jest częścią tego, co należy zainstalować.

Fakt, że zinterpretowany kod może być łatwo odczytywany i kopiowany przez ludzi, może budzić niepokój z punktu widzenia praw autorskich . Istnieją jednak różne systemy szyfrowania i zaciemniania . Dostarczenie kodu pośredniego, takiego jak kod bajtowy, ma podobny efekt do zaciemniania kodu, ale kod bajtowy można zdekodować za pomocą dekompilatora lub deasemblera .

Efektywność

Główną wadą interpreterów jest to, że interpretowany program zazwyczaj działa wolniej, niż gdyby został skompilowany . Różnica prędkości może być niewielka lub duża; często o rząd wielkości, a czasem więcej. Na ogół uruchomienie programu w interpreterze zajmuje więcej czasu niż uruchomienie skompilowanego kodu, ale jego interpretacja może zająć mniej czasu niż całkowity czas wymagany do skompilowania i uruchomienia. Jest to szczególnie ważne podczas tworzenia prototypów i testowania kodu, gdy cykl edycja-interpretacja-debugowanie często może być znacznie krótszy niż cykl edycja-kompilacja-uruchom-debugowanie.

Interpretacja kodu jest wolniejsza niż uruchamianie skompilowanego kodu, ponieważ interpreter musi analizować każdą instrukcję w programie za każdym razem, gdy jest wykonywana, a następnie wykonać żądaną akcję, podczas gdy skompilowany kod po prostu wykonuje akcję w ustalonym kontekście określonym przez kompilację. Ta analiza w czasie wykonywania jest znana jako „narzut interpretacyjny”. Dostęp do zmiennych jest również wolniejszy w interpreterze, ponieważ mapowanie identyfikatorów do lokalizacji pamięci musi być wykonywane wielokrotnie w czasie wykonywania, a nie w czasie kompilacji .

Istnieją różne kompromisy między szybkością programowania podczas korzystania z interpretera a szybkością wykonywania podczas korzystania z kompilatora. Niektóre systemy (takie jak niektóre Lispy ) pozwalają interpretowanemu i skompilowanemu kodowi wywoływać się nawzajem i udostępniać zmienne. Oznacza to, że gdy procedura zostanie przetestowana i debugowana w interpreterze, może zostać skompilowana, a tym samym zyskać na szybszym wykonywaniu, podczas gdy inne procedury są opracowywane. Wiele interpreterów nie wykonuje kodu źródłowego w obecnym stanie, ale przekształca go w bardziej zwartą formę wewnętrzną. Wiele PODSTAWOWE tłumaczy zastąpić słowo z pojedynczych bajtów żetonów , które mogą być wykorzystywane w celu znalezienia instrukcji w tabeli skoku . Kilka interpreterów, takich jak interpreter PBASIC , osiąga jeszcze wyższy poziom kompaktowania programu, używając raczej zorientowanej bitowo niż bajtowej struktury pamięci programu, w której tokeny poleceń zajmują prawdopodobnie 5 bitów, nominalnie przechowywane są „16-bitowe” stałe w kodzie o zmiennej długości, wymagającym 3, 6, 10 lub 18 bitów, a argumenty adresu zawierają „przesunięcie bitowe”. Wiele interpreterów języka BASIC może przechowywać i odczytywać własną, tokenizowaną reprezentację wewnętrzną.

Interpreter może równie dobrze użyć tego samego analizatora leksykalnego i parsera co kompilator, a następnie zinterpretować powstałe abstrakcyjne drzewo składni . Przykładowe definicje typów danych dla tych ostatnich oraz zabawkowy interpreter dla drzew składniowych uzyskanych z wyrażeń języka C są pokazane w ramce.

Regresja

Interpretacja nie może być używana jako jedyna metoda wykonania: nawet jeśli sam interpreter może być interpretowany itd., potrzebny jest bezpośrednio wykonywany program gdzieś na dole stosu, ponieważ interpretowany kod nie jest z definicji taki sam jak kod maszynowy, który może wykonać procesor.

Wariacje

Interpretery kodu bajtowego

Istnieje spektrum możliwości między interpretacją a kompilacją, w zależności od ilości analiz wykonanych przed uruchomieniem programu. Na przykład Emacs Lisp jest kompilowany do kodu bajtowego , który jest wysoce skompresowaną i zoptymalizowaną reprezentacją źródła Lisp, ale nie jest kodem maszynowym (a zatem nie jest powiązany z żadnym konkretnym sprzętem). Ten "skompilowany" kod jest następnie interpretowany przez interpreter kodu bajtowego (sam napisany w C ). Skompilowany kod w tym przypadku jest kodem maszynowym dla maszyny wirtualnej , który jest zaimplementowany nie sprzętowo, ale w interpreterze kodu bajtowego. Tacy tłumacze kompilujący są czasami nazywani również kompilatorami . W interpreterze kodu bajtowego każda instrukcja zaczyna się od bajtu, a zatem interpretery kodu bajtowego mają do 256 instrukcji, chociaż nie wszystkie mogą być użyte. Niektóre kody bajtowe mogą zajmować wiele bajtów i mogą być dowolnie skomplikowane.

Tabele sterujące — które niekoniecznie muszą przechodzić przez fazę kompilacji — dyktują odpowiedni algorytmiczny przepływ sterowania za pomocą dostosowanych interpreterów, podobnie jak interpretery kodu bajtowego.

Interpretery kodu wątkowego

Interpretery kodu wątkowego są podobne do interpreterów kodu bajtowego, ale zamiast bajtów używają wskaźników. Każda „instrukcja” to słowo, które wskazuje na funkcję lub sekwencję instrukcji, po której ewentualnie następuje parametr. Interpreter kodu wątkowego albo zapętla pobieranie instrukcji i wywoływanie funkcji, na które wskazują, albo pobiera pierwszą instrukcję i przeskakuje do niej, a każda sekwencja instrukcji kończy się pobraniem i przeskokiem do następnej instrukcji. W przeciwieństwie do kodu bajtowego nie ma efektywnego ograniczenia liczby różnych instrukcji innych niż dostępna pamięć i przestrzeń adresowa. Klasycznym przykładem kodu wątkowego jest kod Forth używany w systemach Open Firmware : język źródłowy jest kompilowany w „kod F” (kod bajtowy), który jest następnie interpretowany przez maszynę wirtualną .

Interpretery abstrakcyjnego drzewa składni

W spektrum między interpretacją a kompilacją innym podejściem jest przekształcenie kodu źródłowego w zoptymalizowane abstrakcyjne drzewo składni (AST), a następnie wykonanie programu zgodnie z tą strukturą drzewa lub użycie go do wygenerowania kodu natywnego just-in-time . W tym podejściu każde zdanie musi zostać przeanalizowane tylko raz. Jako przewaga nad kodem bajtowym, AST zachowuje globalną strukturę programu i relacje między instrukcjami (które są tracone w reprezentacji kodu bajtowego), a po skompresowaniu zapewnia bardziej zwartą reprezentację. W związku z tym zaproponowano użycie AST jako lepszego formatu pośredniego dla kompilatorów just-in-time niż kod bajtowy. Ponadto pozwala systemowi na lepszą analizę w czasie wykonywania.

Jednak dla tłumaczy, AST powoduje większe obciążenie niż interpreter kodu bajtowego, ze względu na węzły związane ze składnią nie wykonującą użytecznej pracy, mniej sekwencyjną reprezentację (wymagającą przechodzenia większej liczby wskaźników) i narzut związany z odwiedzaniem drzewa.

Kompilacja just-in-time

Dalsze zacieranie różnicy między interpreterami, interpreterami kodu bajtowego i kompilacją jest kompilacją just-in-time (JIT), techniką, w której reprezentacja pośrednia jest kompilowana do natywnego kodu maszynowego w czasie wykonywania. Zapewnia to wydajność uruchamiania kodu natywnego kosztem czasu uruchamiania i zwiększonego użycia pamięci podczas pierwszej kompilacji kodu bajtowego lub AST. Najwcześniejszy opublikowany kompilator JIT jest ogólnie przypisywany pracy nad LISP przez Johna McCarthy'ego w 1960 roku. Optymalizacja adaptacyjna jest techniką uzupełniającą, w której interpreter profiluje działający program i kompiluje jego najczęściej wykonywane części do kodu natywnego. Ta ostatnia technika ma kilkadziesiąt lat i pojawiła się w językach takich jak Smalltalk w latach 80. XX wieku.

Kompilacja just-in-time zyskała w ostatnich latach popularność wśród implementatorów języków, dzięki Java , .NET Framework , najnowocześniejszym implementacjom JavaScript i Matlabowi, który teraz zawiera kompilatory JIT.

Tłumacz szablonów

Jeszcze bardziej niejasne rozróżnienie między kompilatorami a interpreterami czyni specjalny projekt interpretera, znany jako interpreter szablonów. Zamiast implementować wykonanie kodu na podstawie dużej instrukcji switch zawierającej każdy możliwy możliwy kod bajtowy, podczas działania na stosie oprogramowania lub chodzeniu po drzewie, interpreter szablonów utrzymuje dużą tablicę kodu bajtowego (lub dowolną wydajną reprezentację pośrednią) mapowaną bezpośrednio do odpowiednie instrukcje maszyny natywnej, które mogą być wykonywane na sprzęcie hosta jako pary klucz-wartość, znane jako „Szablon”. Kiedy dany segment kodu jest wykonywany, interpreter po prostu ładuje mapowanie kodu w szablonie i bezpośrednio uruchamia je na sprzęcie. Ze względu na swoją konstrukcję, interpreter szablonów bardzo przypomina kompilator just-in-time, a nie tradycyjny interpreter, jednak technicznie nie jest JIT, ponieważ po prostu tłumaczy kod z języka na natywne wywołania jednego kodu na raz czasu zamiast tworzenia zoptymalizowanych sekwencji instrukcji wykonywalnych procesora z całego segmentu kodu. Ze względu na prostą konstrukcję interpretera, który po prostu przekazuje wywołania bezpośrednio do sprzętu zamiast bezpośrednio je implementować, jest on znacznie szybszy niż każdy inny typ, nawet interpretery kodu bajtowego, i do pewnego stopnia mniej podatny na błędy, ale jako kompromis jest trudniejszy do utrzymanie, ponieważ tłumacz musi obsługiwać tłumaczenie na wiele różnych architektur zamiast niezależnej od platformy maszyny wirtualnej/stosu. Do tej pory jedyną istniejącą implementacją interpretera szablonów jest interpreter w implementacji referencyjnej wirtualnej maszyny Java HotSpot/OpenJDK.

Tłumacz własnoręcznie

Autointerpreter to interpreter języka programowania napisany w języku programowania, który potrafi sam siebie interpretować; przykładem jest interpreter BASIC napisany w BASIC. Autointerpretery są powiązane z samoobsługowymi kompilatorami .

Jeśli nie istnieje kompilator dla języka, który ma być interpretowany, utworzenie autointerpretera wymaga implementacji języka w języku hosta (którym może być inny język programowania lub assembler ). Mając pierwszego tłumacza takiego jak ten, system jest ładowany i nowe wersje tłumacza mogą być opracowywane w samym języku. W ten sposób Donald Knuth opracował interpreter TANGLE dla języka WEB przemysłowego standardowego systemu składu TeX .

Definiowanie języka komputerowego odbywa się zwykle w odniesieniu do abstrakcyjnej maszyny (tzw. semantyka operacyjna ) lub jako funkcja matematyczna ( semantyka denotacyjna ). Język może być również zdefiniowany przez tłumacza, w którym podana jest semantyka języka goszczącego. Definicja języka przez tłumacza własnego nie jest uzasadniona (nie może zdefiniować języka), ale tłumacz własny mówi czytelnikowi o wyrazistości i elegancji języka. Umożliwia także interpreterowi interpretację swojego kodu źródłowego, co jest pierwszym krokiem w kierunku interpretacji refleksyjnej.

Ważnym wymiarem projektowym w implementacji tłumacza własnego jest to, czy cecha języka tłumaczonego jest implementowana z tą samą cechą w języku hosta tłumacza. Przykładem jest to, czy domknięcie w języku podobnym do Lisp jest zaimplementowane przy użyciu domknięć w języku interpretera, czy też zaimplementowane "ręcznie" ze strukturą danych jawnie przechowującą środowisko. Im więcej funkcji zaimplementowanych przez tę samą funkcję w języku hosta, tym mniejszą kontrolę ma programista interpretera; inne zachowanie w przypadku przepełnień liczb nie może zostać zrealizowane, jeśli operacje arytmetyczne są delegowane do odpowiednich operacji w języku hosta.

Niektóre języki, takie jak Lisp i Prolog, mają eleganckie autotłumacze. Wiele badań nad autointerpreterami (zwłaszcza interpreterami refleksyjnymi) przeprowadzono w języku programowania Scheme , dialekcie Lispu. Ogólnie jednak każdy język Turing-complete umożliwia pisanie własnego tłumacza. Lisp jest takim językiem, ponieważ programy Lisp są listami symboli i innymi listami. XSLT jest takim językiem, ponieważ programy XSLT są napisane w XML. Sub-domena metaprogramowanie jest pisanie specyficzne dla domeny języków (DSL).

Clive Gifford wprowadził miarę jakości tłumacza własnego (eigenratio), czyli granicę stosunku czasu spędzonego przez komputer na uruchamianiu stosu N autointerpretatorów do czasu spędzonego na uruchomieniu stosu N − 1 autointerpretatorów, gdy N przechodzi do nieskończoność. Ta wartość nie zależy od uruchomionego programu.

Książka Structure and Interpretation of Computer Programs przedstawia przykłady interpretacji metakołowej dla Scheme i jego dialektów. Inne przykłady języków z własnym tłumaczem to Forth i Pascal .

Mikrokod

Mikrokod jest bardzo powszechnie stosowaną techniką „narzucającą interpreter między sprzętem a poziomem architektury komputera”. Jako taki, mikrokod jest warstwą instrukcji na poziomie sprzętowym, które implementują instrukcje kodu maszynowego wyższego poziomu lub sekwencjonowanie maszyny stanów wewnętrznych w wielu elementach przetwarzania cyfrowego . Microcode stosowany jest w uniwersalnych jednostek centralnych , jak również w bardziej wyspecjalizowanych procesorów takich jak mikrokontrolerów , procesorów sygnałowych , kontrolerów kanałowych , kontrolerów dyskowych , sterowników sieciowych , procesorów sieciowych , procesory graficzne , aw innym sprzętem.

Mikrokod zazwyczaj znajduje się w specjalnej, szybkiej pamięci i tłumaczy instrukcje maszynowe, dane maszyny stanowej lub inne dane wejściowe na sekwencje szczegółowych operacji na poziomie obwodów. Oddziela instrukcje maszyny od podstawowej elektroniki, dzięki czemu można swobodniej projektować i zmieniać instrukcje. Ułatwia również budowanie złożonych wieloetapowych instrukcji, jednocześnie zmniejszając złożoność obwodów komputerowych. Pisanie mikrokodu jest często nazywane mikroprogramowaniem, a mikrokod w konkretnej implementacji procesora jest czasami nazywany mikroprogramem .

Szersze microcoding pozwala małe i proste microarchitectures do naśladowania bardziej zaawansowanych architektur z szerszym długości słowa , więcej jednostek wykonawczych i tak dalej, który jest stosunkowo prosty sposób, aby osiągnąć zgodność programową między różnymi produktami w rodzinie procesorów.

Procesor komputerowy

Nawet procesor komputerowy bez mikrokodowania może być uważany za interpreter przetwarzania natychmiastowego wykonania, który jest napisany w języku opisu sprzętu ogólnego przeznaczenia, takim jak VHDL, w celu stworzenia systemu, który analizuje instrukcje kodu maszynowego i natychmiast je wykonuje.

Aplikacje

  • Interpretery są często używane do wykonywania języków poleceń i języków klejących, ponieważ każdy operator wykonywany w języku poleceń jest zwykle wywołaniem złożonej procedury, takiej jak edytor lub kompilator.
  • Samomodyfikujący się kod można łatwo zaimplementować w interpretowanym języku. Odnosi się to do początków interpretacji w badaniach nad Lispem i sztuczną inteligencją .
  • Wirtualizacja . Kod maszynowy przeznaczony dla architektury sprzętowej może być uruchamiany przy użyciu maszyny wirtualnej . Jest to często używane, gdy zamierzona architektura jest niedostępna lub między innymi do uruchamiania wielu kopii.
  • Sandboxing : podczas gdy niektóre typy piaskownic opierają się na zabezpieczeniach systemu operacyjnego, często używany jest interpreter lub maszyna wirtualna. Rzeczywista architektura sprzętowa i pierwotnie zamierzona architektura sprzętowa mogą, ale nie muszą być takie same. Może się to wydawać bezcelowe, z wyjątkiem tego, że piaskownice nie są zmuszane do wykonywania wszystkich instrukcji w kodzie źródłowym, który przetwarza. W szczególności może odmówić wykonania kodu, który narusza wszelkie ograniczenia bezpieczeństwa , na których działa.
  • Emulatory do uruchamiania oprogramowania komputerowego napisanego dla przestarzałego i niedostępnego sprzętu na nowocześniejszym sprzęcie.

Zobacz też

Bibliografia

Zewnętrzne linki