2015 ZV tétel kidolgozások

This commit is contained in:
TMD44
2021-06-13 11:18:20 +02:00
parent ba63c24f40
commit fdf04680b1
248 changed files with 11400 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,543 @@
\documentclass[margin=0px]{article}
\usepackage{listings}
\usepackage[utf8]{inputenc}
\usepackage{graphicx}
\usepackage{float}
\usepackage[a4paper, margin=1in]{geometry}
\usepackage{amsthm}
\renewcommand{\figurename}{ábra}
\newenvironment{tetel}[1]{\paragraph{#1 \\}}{}
\usepackage{listings}
\usepackage{color}
% A dokument itt kezdődik
\title{Záróvizsga tételsor \\ \large 9. Programok fordítása és végrehajtása}
\date{}
\author{}
\begin{document}
\maketitle
\begin{tetel}{ Programok fordítása és végrehajtása }
Fordítás és interpretálás összehasonlítása.
Fordítási egység és a szerkesztés fogalma.
Fordítóprogramok komponenseinek feladata és működési elveik vázlatos ismertetése.
Kódgenerálás assemblyben alapvető imperatív vezérlési szerkezetekhez.
A szekvenciális és
párhuzamos/elosztott végrehajtás összehasonlítása
\end{tetel}
\section{Bevezetés}
Amikor programot írunk, azt valamilyen programozási nyelven tesszük. Ezután a nyelvtől függően vagy lefordítjuk, vagy interpreterrel futtatjuk.
\section{Fordítás és Interpretálás}
\subsection{Fordítás}
A fordítás során általában egy magas szintű programozási nyelvből gépi kód keletkezik, amelyet a processzor már képes értelmezni és futtatni. Előnye, hogy gyors, mivel a lexikális, szintaktikus és szemantikus elemzés fordítási időben, egyszer fut le, valamint ekkor optimalizáljuk a kódot. Fordítási időben sok hibát ki lehet szűrni, ezáltal megkönnyítve a debugolást. A gépi kód nehezen visszafejthető. Általában nagyobb programokhoz használjuk, ahol fontos a hatékonyság. A lefordított kódon később már nem (vagy csak nagyon nehezen) tudunk változtatni.
Hátránya, hogy a keletkezett kód nem platformfüggetlen, minden architektúrára külön-külön le kell fordítani.
\textbf{Példák}: C, C++, Ada, Haskell
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{img/forditas_folyamatabra.png}
\caption{a fordítás folyamata}
\label{fig:forditas_folyamatabra}
\end{figure}
\subsection{Interpretálás}
Az interpretálás során a programkódot az értelmező futás közben hajtja végre. Platformfüggetlen, csak az interpretert kell minden rendszerre egyszer megírni. Nehéz benne a hibakeresés, mivel sok olyan hiba maradhat a kódban, amit egy fordító kiszűrt volna (pl. típus egyezőség).
\textbf{Példák}: PHP, JavaScript, ShellScript
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{img/interpretalas_folyamatabra.png}
\caption{az interpretálás folyamata}
\label{fig:interpretalas_folyamatabra}
\end{figure}
\subsection{Fordítás és Interpretálás együtt}
Egyes nyelvek (pl. Java) előfordítást használnak, melynek eredménye a \textit{bájtkód}, amely gépi kód egy virtuális gép számára. Ezzel elérhető a fordítási idejű hibaellenőrzés és optimalizálás, de megmarad a platformfüggetlenség.
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{img/bajtkod_folyamatabra.png}
\caption{az interpretálás folyamata}
\label{fig:bajtkod_folyamatabra}
\end{figure}
\section{Fordítási egység és a szerkesztés}
A tárgykód létrehozása két fázisban történik. Először a forrásfájlokat \textit{lefordítjuk}, ebből keletkezik az un. \textit{objektumkód} (pl.: .obj, .class). Ebben a gépi utasítások már megvannak, de hiányzik belőle a hivatkozások (pl változók, függvények), melyek más fájlokban vannak megvalósítva. \textit{Fordítási egységnek} nevezzük azt, amiből egy objektumkód keletkezik.
A \textit{linker} (szerkesztő) feladata, hogy a hiányzó referenciákat kitöltse, hogy egyetlen fájlt generálva futtatható kódot kapjunk.
A linkelés lehet statikus, amikor a fordító tölti fel a hiányzó referenciákat; vagy dinamikus, mikor fordítási időben, jellemzően egy másik fájlból (pl.: .dll) tölti be a hiányzó kódot. Az utóbbi akkor praktikus, ha egy modult több, különálló program használ.
\section{A fordítóprogram komponensei}
\begin{figure}[H]
\centering
\includegraphics[width=0.5\textwidth]{img/forditas_teljes_folyamata.png}
\caption{a fordítás lépései}
\label{fig:forditas_teljes_folyamatabra}
\end{figure}
\subsection{Lexikális elemző}
Bemenete maga a forráskód. A lexikális elemző feladata, hogy tokenekre bontsa a forráskódot. Adott egy reguláris (hármas típusú) nyelvtan, mely a nyelvre jellemző. Ez adja meg, hogy milyen típusú tokenek szerepelhetnek a forrásban. A tokenekhez tulajdonságokat rendelhet (pl. változó neve, literál értéke). Kimenete ez a tokensorozat. Amennyiben az elemző olyan karaktersorozatot talál, amelynek nem feleltethető meg token, akkor az lexikális hibát vált ki.
\textit{megjegyzés: Lexikális hibánál nem feltétlen szakad meg a fordítás folyamata, megpróbálhatjuk átugrani az adott részt és folytatni az elemzést, így ha több hiba is van, akkor azokat egyszerre jelezhetjük.}
A reguláris kifejezéseket \textit{véges determinisztikus automatákkal} ismerjük fel. Amennyiben egy lexikális elemre az egyik automata elfogadó állapotba kerül, úgy felismertünk egy tokent. Egy karaktersorozatot egyszerre több automata is felismerhet. Amennyiben ezek azonosan hosszúak, akkor a nyelv konfliktusos. Ennek nem szabad előfordulnia. Az viszont lehetséges, hogy egy szót, és az ő prefixét is felismerte egy automata. Ekkor mindig a hosszabbat választjuk.
\subsection{Szintaktikus elemző}
Bemenete a lexikális elemző kimenete. Feladata, hogy \textit{szintaxisfát} építsen a tokenekből, a nyelvez tartozó egy környezetfüggetlen (kettes típusú) grammatika alapján, vagy ha ez lehetetlen, akkor jelezze ezt \textit{szintaktikus hiba}ként.
\subsubsection{LR0 elemzés}
A lexikális elemző által előállított szimbólumsorozatot balról
jobbra olvassuk, a szimbólumokat az elemző vermébe tesszük.
\textit{Léptetés}: egy új szimbólumot teszünk a bemenetről a verem
tetejére.
\textit{Redukálás}: a verem tetején lévő szabály-jobboldalt
helyettesítjük a szabály bal oldalán álló nemterminálissal
A háttérben egy véges determinisztikus automata működik:
az automata átmeneteit a verem tetejére kerülő szimbólumok
határozzák meg
ha az automata végállapotba jut, redukálni kell
egyéb állapotban pedig léptetni.
Az automata bizonyos nyelvek esetén konfliktusos lehet: nem tudjuk eldönteni, hogy léptessünk vagy redukáljunk.
\subsubsection{LR1 elemzés}
Az előző problémára kínál megoldást, kibővítve a lehetséges nyelvek halmazát.
Az ötlet, hogy \textit{olvassunk előre} egy szimbólumot.
Ha az aktuális állapot \textit{i}, és az előreolvasás eredménye az a
szimbólum:
ha $ [A \rightarrow \alpha.a\beta, b] \in I_i $ és $read(I_i, a) = I_j$
akkor léptetni kell, és átlépni a \textit{j} állapotba.
ha $ [A \rightarrow \alpha., a] \in I_i (A \neq S'), $
akkor redukálni kell az $ A \rightarrow \alpha $ szabály szerint.
ha $ [S' \rightarrow S., \#] \in I_i $ és $ a = \# $, akkor el kell fogadni a szöveget, minden más esetben hibát kell jelezni.
Ha az \textit{i} állapotban \textit{A} kerül a verem tetejére:
ha$ read(I_i,A) = I_j $ ,
akkor át kell lépni a \textit{j} állapotba, egyébként hibát kell jelezni.
\subsubsection{Jelmagyarázat/Kanonikus halmazok}
\paragraph{Closure/lezárás}
Ha $ I $ a grammatika egy $ LR(1) $ elemhalmaza, akkor $ closure(I) $ a
legszűkebb olyan halmaz, amely az alábbi tulajdonságokkal
rendelkezik:
$ I \subseteq closure(I) $ ha $ [A \rightarrow \alpha.B\gamma,a] \in closure(I) $,
és $ B \rightarrow \beta $ a grammatika egy szabálya,
akkor $ \forall b \in FIRST1(\gamma{}a) $ esetén $ [B \rightarrow .\beta,b] \in closure(I) $
\paragraph{Read/olvasás}
Ha $ I $ a grammatika egy $ LR(1) $ elemhalmaza, $ X $ pedig terminális vagy nemterminális szimbóluma, akkor $ read(I, X) $ a legszűkebb olyan halmaz, amely az alábbi tulajdonsággal rendelkezik:
Ha $ [A \rightarrow \alpha. X\beta,a] \in I $, akkor $ closure([ A \rightarrow \alpha X.\beta,a]) \subseteq read(I, X) $.
\paragraph{LR(1) kanonikus halmazok ($ I_n $)}
\begin{itemize}
\item
$ closure([S' \rightarrow .S, \#]) $ a grammatika egy kanonikus halmaza.
\item
Ha $ I $ a grammatika egy kanonikus elemhalmaza, $ X $ egy terminális vagy nemterminális szimbóluma, és $ read(I, X) $ nem üres, akkor $ read(I, X) $ is a grammatika egy kanonikus halmaza.
\item
Az első két szabállyal az összes kanonikus halmaz előáll.
\end{itemize}
\subsection{Szemantikus elemző}
A szemantikus elemzés jellemzően a környezetfüggő ellenőrzéseket
valósítja meg.
%Dévai diáiról
\begin{itemize}
\item
deklarációk kezelése: változók, függvények, eljárások, operátorok, típusok
\item
láthatósági szabályok
\item
aritmetikai ellenőrzések
\item
a program szintaxisának környezetfüggő részei
\item
típusellenőrzés
\item
stb.
\end{itemize}
A szemantikus elemzéshez ki kell egészítenünk a grammatikát. Rendeljünk a szimbólumokhoz attribútumokat és a szabályokhoz akciókat! Egy adott szabályhoz tartozó feltételek csak a szabályban előforduló attribútumoktól függhetnek. (Ha egy feltétel nem teljesül, akkor szemantikus hibát kell jelezni!). A szemantikus rutinok csak annak a szabálynak az attribútumait használhatják és számíthatják ki, amelyikhez az őket reprezentáló akciószimbólum tartozik. Minden szintaxisfában minden attribútumértéket pontosan egy
szemantikus rutin határozhat meg. Az így létrejövő nyelvtant \textit{attribútum fordítási grammatikának} (ATG) hívjuk.
A \textit{jól definiált attribútum fordítási grammatika}, olyan attribútum fordítási grammatika, amelyre igaz, hogy a grammatika által definiált nyelv mondataihoz tartozó minden szintaxisfában minden attribútum értéke egyértelműen kiszámítható.
Egy attribútumot kétféleképpen lehet meghatározni:
\paragraph{Szintézissel} a szintaxisfában alulról felfelé terjed az információ, egy szülő attribútumát a gyerekekből számoljuk. Kitüntetettnek hívjuk azokat az attribútumokat, melyeket a lexikális elemző szolgáltat.
\paragraph{Öröklődéssel} a szintaxisfában felülről lefelé terjed az információ. A gyerekek attribútumait a szülőé határozza meg.
Az \textit{L-ATG} olyan attribútum fordítási grammatika, amelyben minden
$ A \rightarrow X_1X_2 . . . X_n $szabályban az attribútumértékek az alábbi sorrendben meghatározhatók:
\begin{itemize}
\item
A örökölt attribútumai
\item
$ X_1 $ örökölt attribútumai
\item
$ X_1 $ szintetizált attribútumai
\item
$ X_2 $ örökölt attribútumai
\item
$ X_2 $ szintetizált attribútumai
\item
\dots
\item
$ X_n $ örökölt attribútumai
\item
$ X_n $ szintetizált attribútumai
\item
A szintetizált attribútumai
\end{itemize}
Amennyiben a nyelvtanunk ennek eleget tesz, úgy hatékonyan meghatározható minden attribútum.
A szemantikus elemzéshez jellemzően szimbólumtáblát használunk, verem szerkezettel és keresőfával vagy hash-táblával. Minden blokk egy új szint a veremben, egy szimbólum keresése a verem tetejéről indul.
% Kódgenerálás assemblyben alapvető imperatív vezérlési szerkezetekhez.
\section{Kódgenerálás alapvető vezérlési szerkezetekhez}
A kódgenerálás feladata, hogy a szintaktikusan és szemantikusan elemzett programot tárgykóddá alakítsa. Általában szorosan összekapcsolódik a
szemantikus elemzéssel.
\subsection{Értékadás}
assignment $ \rightarrow $ variable assignmentOperator expression
\begin{verbatim}
a kifejezést az eax regiszterbe kiértékelö kód
2 mov [Változó],eax
\end{verbatim}
\subsection{Egy ágú elágazás}
statement $ \rightarrow $ if condition then program end
\begin{verbatim}
1 a feltételt az al regiszterbe kiértékelö kód
2 cmp al,1
3 je Then
4 jmp Vége
5 Then: a then-ág programjának kódja
6 Vége:
\end{verbatim}
\textit{megjegyzés: a dupla ugrásra azért van szükség, mert a feltételes ugrás hatóköre limitált.}
\subsection{Több ágú elágazás}
statement $ \rightarrow $
\\if $ condition_1 $ then $ program_1 $
\\elseif $ condition_2 $ then $ program_2 $
\\\dots
\\elseif $ condition_n $ then $ program_n $
\\else $ program_{n+1} $ end
\begin{verbatim}
1 az 1. feltétel kiértékelése az al regiszterbe
2 cmp al,1
3 jne near Feltétel_2
4 az 1. ág programjának kódja
5 jmp Vége
6
...
7 Feltétel_n: az n-edik feltétel kiértékelése az al regiszterbe
8 cmp al,1
9 jne near Else
10 az n-edik ág programjának kódja
11 jmp Vége
12 Else: az else ág programjának kódja
13 Vége:
\end{verbatim}
\subsection{Switch-case}
statement $ \rightarrow $ switch variable
\\case $ value_1 $ : $ program_1 $
\\...
\\case $ value_n $ : $ program_n $
\begin{verbatim}
1 cmp [Változó],Érték_1
2 je near Program_1
3 cmp [Változó],Érték_2
4 je near Program_2
5
. ..
6 cmp [Változó],Érték_n
7 je near Program_n
8 jmp Vége
9 Program_1: az 1. ág programjának kódja
10
. ..
11 Program_n: az n-edik ág programjának kódja
12 Vége:
\end{verbatim}
\subsection{Ciklus}
%Ez egy vagy két szó?
\subsubsection{Elől tesztelő}
statement $ \rightarrow $ while condition statements end
\begin{verbatim}
1 Eleje: a ciklusfeltétel kiértékelése az al regiszterbe
2 cmp al,1
3 jne near Vége
4 a ciklusmag programjának kódja
5 jmp Eleje
6 Vége:
\end{verbatim}
%Ez egy vagy két szó?
\subsubsection{Hátul tesztelő}
statement $ \rightarrow $ loop statements while condition
\begin{verbatim}
1 Eleje: a ciklusmag programjának kódja
2 a ciklusfeltétel kiértékelése az al regiszterbe
3 cmp al,1
4 je near Eleje
\end{verbatim}
\subsubsection{For ciklus}
statement $ \rightarrow $ for variable from $ value_1 $ to $ value_2 $ statements end
\begin{verbatim}
1 a "from" érték kiszámítása a [Változó] memóriahelyre
2 Eleje: a "to" érték kiszámítása az eax regiszterbe
3 cmp [Változó],eax
4 ja near Vége
5 a ciklusmag kódja
6 inc [Változó]
7 jmp Eleje
8 Vége:
\end{verbatim}
\subsection{Statikus változók}
Kezdőérték nélküli változódefiníció fordítása:
\begin{verbatim}
section .bss
; a korábban definiált változók...
Lab12: resd 1 ; 1 x 4 bájtnyi terület
\end{verbatim}
Kezdőértékkel adott változódefiníció fordítása:
\begin{verbatim}
section .data
; a korábban definiált változók...
Lab12: dd 5 ; 4 bájton tárolva az 5-ös érték
\end{verbatim}
\subsection{Logikai kifejezések}
\subsubsection{kifejezés1 $ < $ kifejezés2 }
\begin{verbatim}
; a 2. kifejezés kiértékelése az eax regiszterbe
push eax
; az 1. kifejezés kiértékelése az eax regiszterbe
pop ebx
cmp eax,ebx
jb Kisebb
mov al,0 ; hamis
jmp Vége
Kisebb:
mov al,1 ; igaz
Vége:
\end{verbatim}
\subsubsection{kifejezés1 $ \lbrace $ és, vagy, nem, kizáróvagy $ \rbrace $ kifejezés2 }
\begin{verbatim}
; a 2. kifejezés kiértékelése az al regiszterbe
push ax ; nem lehet 1 bájtot a verembe tenni!
; az 1. kifejezés kiértékelése az al regiszterbe
pop bx ; bx-nek a bl részében van,
; ami nekünk fontos
and al,bl
\end{verbatim}
\subsubsection{ lusta "és" kiértékelés }
\begin{verbatim}
; az 1. kifejezés kiértékelése az al regiszterbe
cmp al,0
je Vége
push ax
; a 2. kifejezés kiértékelése az al regiszterbe
mov bl,al
pop ax
and al,bl
Vége:
\end{verbatim}
\subsubsection{ Alprogramok megvalósítása }
\begin{verbatim}
; az 1. kifejezés kiértékelése az al regiszterbe
cmp al,0
je Vége
push ax
; a 2. kifejezés kiértékelése az al regiszterbe
mov bl,al
pop ax
and al,bl
Vége:
\end{verbatim}
\subsubsection{ Alprogramok hívása }
\begin{verbatim}
Alprogramok sémája
; utolsó paraméter kiértékelése eax-be
push eax
; ...
; 1. paraméter kiértékelése eax-be
push eax
call alprogram
add esp,a paraméterek összhossza
\end{verbatim}
\section{Kódoptimalizáló}
Az optimalizálás feladata, hogy a keletkezett kód kisebb és gyorsabb legyen, úgy hogy a futás eredménye nem változik. A gyorsaság és a tömörség gyakran ellentmondanak egymásnak, és az egyik csak a másik rovására lehet javítani. Általában három lépésben szokás elvégezni:
\begin{itemize}
\item
Optimalizálási lépések végrehajtása az eredeti programon (vagy annak egyszerűsített változatán)
\item
Kódgenerálás
\item
Gépfüggő optimalizálás végrehajtása a generált kódon
\end{itemize}
\subsection{Lokális optimalizáció}
Egy programban egymást követő utasítások sorozatát \textit{alapblokknak} nevezzük, ha az első utasítás kivételével egyik utasítására sem lehet távolról átadni a vezérlést (assembly programokban: ahová a jmp, call, ret utasítások "ugranak"; magas szintű nyelvekben: eljárások, ciklusok eleje, elágazások ágainak első utasítása, goto utasítások célpontjai). Az utolsó utasítás kivételével nincs benne vezérlés-átadó utasítás (assembly programban: jmp, call, ret magas szintű nyelvekben: elágazás vége, ciklus vége, eljárás vége, goto). Az utasítás-sorozat nem bővíthető a fenti két szabály megsértése nélkül.
Ha az optimalizálás az alapblokkok keretein belül történik, akkor garantált, hogy az átalakításnak nincs mellékhatása. Ez a \textit{lokális optimalizálás}.
\paragraph{Ablakoptimalizálás}
Ez egy módszer a lokális optimalizálás egyes fajtáihoz. Egyszerre csak egy néhány utasításnyi részt vizsgálunk a kódból. A vizsgált részt előre megadott mintákkal hasonlítjuk össze. Ha illeszkedik, akkor a mintához megadott szabály szerint átalakítjuk ezt az "ablakot" végigcsúsztatjuk a programon. Az átalakítások megadása:
\begin{center}
$ \lbrace$ minta $ \rightarrow $ helyettesítés $\rbrace$ szabályhalmazzal
(a mintában lehet paramétereket is használni)
\end{center}
Példák:
\begin{itemize}
\item
felesleges műveletek törlése: nulla hozzáadása vagy kivonása
\item
egyszerűsítések: nullával szorzás helyett a regiszter törlése
\item
regiszterbe töltés és ugyanoda visszaírás esetén a visszaírás elhagyható
\item
utasításismétlések törlése: ha lehetséges, az ismétlések törlése
\end{itemize}
\subsection{Globális optimalizáció}
A teljes program szerkezetét meg kell vizsgálni. Ennek módszere az adatáram-analízis:
\begin{itemize}
\item
Mely változók értékeit számolja ki egy adott alapblokk?
\item
Mely változók értékeit melyik alapblokk használja fel?
\end{itemize}
Ez lehetővé teszi az azonos kifejezések többszöri kiszámításának kiküszöbölését akkor is, ha különböző alapblokkokban szerepelnek; valamint a konstansok és változók továbbterjesztését alapblokkok között is elágazások, ciklusok optimalizálását.
% kell ide példa? tudok szerezni de nem hiszem hogy annyira fontos lenne
\section{A szekvenciális és párhuzamos/elosztott végrehajtás összehasonlítása}
\subsection{Szekvenciális végrehajtás:}
Ilyenkor a végrehajtás egy processzoron történik. Minden művelet atomi. Egy inputhoz egy output tartozik. Két szekvenciális program ekvivalens, ha ezek a párosok megegyeznek. Nem használja fel az összes rendelkezésre álló erőforrást.
\subsection{Párhuzamos végrehajtás:}
Több processzoron hajtódik végre a program. A párhuzamos folyamatok egymással kommunikálva, szinkronban oldják meg az adott problémát. A konkurens program szétbontható elemi szekvenciális programokra, ezek a folyamatok. A folyamatok használhatnak közös erőforrásokat: pl. változók, adattípus objektumok, kommunikációs csatornák.
A kommunikációt általában kétféleképpen szokták megvalósítani.
\paragraph{Osztott memóriával.} Ekkor szinkronizálni kell, hogy ki mikor fér hozzá, hogy ne legyen ütközés.
\paragraph{Kommunikációs csatornával.} Garantálni kell, hogy ha egy folyamat üzenetet küld egy másiknak, akkor az meg is kapja azt, és jelezzen is vissza. Ügyelni kell, nehogy deadlock alakuljon ki.
\end{document}