-
Notifications
You must be signed in to change notification settings - Fork 3
/
m06.html
257 lines (226 loc) · 15.2 KB
/
m06.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
<!DOCTYPE HTML>
<html moznomarginboxes mozdisallowselectionprint>
<head>
<title>DSOP LI0 — Systemy operacyjne — Materiały do zajęć</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="robots" content="index,follow"/>
<link rel="stylesheet" href="css/reset.css" type="text/css" media="all">
<link rel="stylesheet" href="css/main.css" type="text/css" media="all">
<link href="https://fonts.googleapis.com/css?family=Oxygen:400&subset=latin,latin-ext" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Fira+Sans:400,500,700&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Fira+Mono&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<!--[if lt IE 9]>
<script type="text/javascript" src="js/html5.js"></script>
<![endif]-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML'></script>
<script>
MathJax.Hub.Config({
tex2jax: {
inlineMath: [['$','$'], ['\\(','\\)']],
processEscapes: true,
delayStartupUntil: onload
},
TeX: { equationNumbers: {autoNumber: "AMS"} }
});
</script>
<script type="text/javascript" src="js/js.js"></script>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/styles/arduino-light.min.css">
<script src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</head>
<body>
<div id="wrapper">
<header id="main-header">
<h1>Moduł 6</h1>
<h2>Materiały do zajęć z <i>Systemów operacyjnych</i> prowadzonych na Wydziale Matematyki i Informatyki Uniwersytetu im. Adama Mickiewicza w Poznaniu.</h2>
<h3><a href="index.html" class="button button-grey">« Wróć do spisu materiałów</a></h3>
</header>
<article id="m06">
<div class="notice">
<p>Materiał do tych i kolejnych ćwiczeń zakłada, że umiesz swobodnie pisać programy w języku ANSI C. Jeśli nie czujesz się w tym zbyt pewnie, powtórz sobie materiał z zajęć z <i>Podstaw programowania</i> lub poczytaj literaturę we własnym zakresie.</p>
</div>
<h1>Proces kompilacji programu w języku C</h1>
<div class="howitworks">
<p>Proces kompilacji programu w języku C składa się zwykle z czterech etapów:<sup>[1]</sup>
<ol>
<li><b>Prekompilacja</b> (preprocessing). W czasie tego etapu przetwarzane są instrukcje preprocesora związane z dołączaniem plików, kompilacją warunkową czy makrami.</li>
<li><b>Kompilacja</b> (compilation). W czasie tego etapu kod przetworzony przez preprocesor jest przekształcany na kod źródłowy w języku assemblera.</li>
<li><b>Assemblacja</b> (assembly). W czasie tego etapu tworzone są pliki obiektów, zbudowane z przetworzonego na język maszynowy kodu assemblera.</li>
<li><b>Linkowanie/konsolidacja</b> (linking). W czasie tego etapu pliki obiektów i bibliotek łączone są tak, że powstaje jeden wykonywalny plik, w którym odwołania do elementów zdefiniowanych w innych plikach obiektów mogą być realizowane.</li>
</ol>
<p>W praktyce, wiele funkcji jest wspólnych dla różnych programów (rozważmy chociażby funkcję <code>printf</code> z biblioteki standardowej). Na szczęście współczesne systemy dobrze radzą sobie z tzw. <i>dynamicznym linkowaniem</i>, którego idea polega na tym, że niektóre obiekty nie są dołączane do programu w procesie konsolidacji (<i>statyczne linkowanie</i>), lecz dopiero w czasie jego uruchamiania.</p>
<p class="footnote">[1] Zwięzłe, ale szczegółowe wyjaśnienie zagadnień związanych z kompilacją można znaleźć choćby na stronie <a href="http://www.tenouk.com/ModuleW.html">http://www.tenouk.com/ModuleW.html</a>.</p>
</div>
<p>W przeszłości do kompilowania programów w języku C wykorzystywano uniksowy kompilator języka C o nazwie <code>cc</code>. Dziś jednak o wiele większą popularność zdobywa pakiet <code>gcc</code> (ang. <i>GNU Compiler Collection</i>), umożliwiający kompilację programów w różnych językach. Z niego będziemy korzystać w czasie tych i kolejnych ćwiczeń. Okazuje się, że w większości instalacji systemu GNU/Linux polecenia <code>cc</code> i <code>gcc</code> odwołują się do tego samego programu. Warto jednak zdawać sobie sprawę z historii polecenia <code>cc</code>.</p>
<div class="exercise">
<p>Utwórz plik źródłowy <code>program.c</code> o treści:</p>
<pre><code class="c"><!--
-->#include <stdio.h>
<!-- -->
<!-- -->main(){
<!-- --> printf("Hello World\n");
<!-- -->}
<!-- --></code></pre>
<p>Skompiluj program, wykonując polecenie</p>
<pre><code class="no-highlight">$ gcc program.c</code></pre>
<p>Następnie uruchom go, wykonując polecenie</p>
<pre><code class="no-highlight">$ ./a.out</code></pre>
<p>Dlaczego ostatnie z poleceń rozpoczyna się od ciągu <code>./</code>?</p>
</div>
<div class="exercise">
<p>Sprawdź co się stanie, jeśli nie dołączysz do pliku źródłowego deklaracji zawartych w nagłówku <code>stdio.h</code>. Czy wówczas program się skompiluje?</p>
</div>
<div class="exercise">
<p>Korzystając z opcji <code>-o</code> programu <code>gcc</code>, zapisz efekt działania kompilatora do pliku o nazwie <code>aplikacja</code>.</p>
</div>
<div class="howitworks">
<p>W przypadku języka C przyjęło się, że:</p>
<ul>
<li>kod źródłowy programu przechowuje się w pliku o rozszerzeniu <code>.c</code>,</li>
<li>przetworzony przez preprocesor kod przechowuje się w pliku o rozszerzeniu <code>.i</code>,</li>
<li>kod assemblera przechowuje się w pliku o rozszerzeniu <code>.s</code>,</li>
<li>plik obiektu ma rozszerzenie <code>.o</code>.</li>
</ul>
</div>
<div class="exercise">
<p>Sprawdź efekt działania kompilatora <code>gcc</code> w poszczególnych etapach kompilacji. W tym celu użyj:</p>
<ul>
<li>opcji <code>-c</code>, aby zakończyć działanie kompilatora na procesie assemblacji,</li>
<li>opcji <code>-S</code>, aby zakończyć działanie kompilatora na procesie kompilacji,</li>
<li>opcji <code>-E</code>, aby zakończyć działanie kompilatora na procesie prekompilacji.</li>
</ul>
<p>Jakie są domyślne nazwy plików, do których kompilator zapisuje efekt swojej pracy w poszczególnych przypadkach? Sprawdź, czy da się wpłynąć na te nazwy korzystając z opcji <code>-o</code>. Czy zawsze kompilator zapisuje dane do pliku? Jeśli nie, to jak sobie z tym poradzić?</p>
<p><a href="#" class="button button-blue" rel="answer">Zobacz odpowiedź</a></p>
<div class="answer">
<p>W przypadku opcji <code>-E</code> kompilator wypisuje dane na wyjściu. Można je przekierować do odpowiedniego pliku standardowym przekierowaniem strumienia danych. W przypadku skorzystania z opcji <code>--save-temps</code> efekty poszczególnych etapów kompilacji są zachowywane.</p>
</div>
</div>
<div class="exercise">
<p>Usuń wszystkie pliki związane z kompilacją pliku <code>program.c</code>, poza nim samym. Następnie skompiluj plik <code>program.c</code>, wywołując program <code>gcc</code> z opcją <code>--save-temps</code>. Skomentuj efekty.</p>
</div>
<div class="exercise">
<p>Utwórz pliki <code>hw.c</code>, <code>gb.c</code> oraz <code>app.c</code> o treściach, kolejno:</p>
<pre><code class="c"><!--
-->#include <stdio.h>
<!-- -->
<!-- -->int helloworld(){
<!-- --> printf("Hello World\n");
<!-- --> return 0;
<!-- -->}
<!-- --></code></pre>
<pre><code class="c"><!--
-->#include <stdio.h>
<!-- -->
<!-- -->int goodbye(){
<!-- --> printf("Good bye\n");
<!-- --> return 0;
<!-- -->}
<!-- --></code></pre>
<pre><code class="c"><!--
-->int helloworld();
<!-- -->int goodbye();
<!-- -->
<!-- -->int main(){
<!-- --> helloworld();
<!-- --> goodbye();
<!-- --> return 0;
<!-- -->}
<!-- --></code></pre>
<p>Następnie wykonaj polecenia:</p>
<pre><code class="no-highlight"><!--
-->$ gcc app.c
<!-- -->$ gcc -c app.c
<!-- -->$ gcc -c hw.c
<!-- -->$ gcc hw.o gb.c app.o
<!-- --></code></pre>
<p>Które z poleceń nie zadziałały? Dlaczego? Zastanów się nad znaczeniem pozostałych.</p>
<p><a href="#" class="button button-blue" rel="answer">Zobacz odpowiedź</a></p>
<div class="answer">
<p>Pierwsze polecenie zakończyło się niepoprawnie. Kompilator nie potrafił znaleźć definicji funkcji <code>helloworld</code> oraz <code>goodbye</code>.</p>
</div>
</div>
<h1>Dołączanie bibliotek</h1>
<div class="howitworks">
<p>W czasie pisania programu możemy korzystać z funkcji pochodzących z pewnych bibliotek, takich jak biblioteka standardowa (<code>libc</code>) czy biblioteka funkcji matematycznych (<code>libm</code>). Wymaga to dołączenia odpowiednich plików nagłówkowych (zwykle z rozszerzeniem <code>.h</code>), zawierających między innymi deklaracje funkcji bibliotecznych.<sup>[1]</sup></p>
<p>Aby proces konsolidacji zakończył się powodzeniem, konieczne jest także poinformowanie kompilatora o tym, jakie biblioteki mają brać w nim udział. Kompilator <code>gcc</code>, o ile nie zdecydowano inaczej, zawsze dołącza w procesie konsolidacji bibliotekę <code>libc</code>, zawierającą definicje funkcji zadeklarowanych w wielu plikach nagłówkowych, np. <code>stdlib.h</code> oraz <code>stdio.h</code>.</p>
<p>Same biblioteki dzielimy jednak w ogólności na <i>statyczne</i> i <i>dynamiczne</i>. W przypadku tych pierwszych, wykorzystywane funkcje są dołączane na stałe do pliku wynikowego. Biblioteki dynamiczne umożliwiają współdzielenie swoich zasobów pomiędzy różnymi procesami, a tym samym zapewniają lepsze zarządzanie pamięcią. System operacyjny dołącza wówczas wymagane definicje do programu w momencie jego uruchamiania. Przyjęło się, że biblioteki statyczne są plikami o rozszerzeniu <code>.a</code>, a dynamiczne <code>.so</code>.</p>
<p class="footnote">[1] Należy ostrożnie obchodzić się z pojęciami <i>deklaracji</i> i <i>definicji</i> zmiennych i funkcji. Pliki nagłówkowe zawierają deklaracje funkcji (a czasem też stałych), zaś biblioteki ich definicje.</p>
<div class="exercise">
<p>Sprójrz na <i>Ćwiczenie 6.1</i>. Odwoływaliśmy się w nim do funkcji <code>printf</code>, a jednak nigdzie jej nie definiowaliśmy. Gdzie została zadeklarowana? Gdzie była zdefiniowana? Dlaczego udało się skompilować program bez dołączania dodatkowych bibliotek?</p>
</div>
</div>
<p>Aby dołączyć bibliotekę do programu, należy skorzystać z opcji <code>-l</code> programu <code>gcc</code>. Opcję tę można wywołać wielokrotnie, wskazując każdorazowo nazwę jednej biblioteki (z pominięciem przedrostka <code>lib</code>). Przyjęło się, że jeśli nazwa biblioteki jest jednoznakowa, to umieszcza się ją za opcją <code>-l</code> bez dodatkowego znaku odstępu, tworząc np. zbitkę <code>-lm</code> (dla biblioteki <code>libm</code>). Program <code>gcc</code> poprawnie interpretuje takie wywołania.</p>
<div class="exercise">
<p>Utwórz plik źródłowy <code>sin.c</code> o następującej treści:</p>
<pre><code class="c"><!--
-->#include <stdio.h>
<!-- -->#include <math.h> // plik nagłówkowy math.h deklaruje stałe oraz
<!-- --> // funkcje zdefiniowane w bibliotece
<!-- --> // matematycznej (libm)
<!-- -->
<!-- -->main(){
<!-- --> int x = 5;
<!-- --> printf("sin(%d)=%f\n", x, sin(x));
<!-- -->}
<!-- --></code></pre>
<p>Spróbuj go skompilować, wykonując polecenia:</p>
<pre><code class="no-highlight"><!--
-->$ gcc -o sin sin.c
<!-- -->$ gcc -l m -o sin sin.c
<!-- -->$ gcc -o sin sin.c -l m
<!-- -->$ gcc -o sin sin.c -lm
<!-- --></code></pre>
<p>Skomentuj efekty.</p>
</div>
<div class="exercise exercise-star">
<p>Korzystając z programu <code>ldd</code> sprawdź, z jakich bibliotek dynamicznych korzysta skompilowany przed chwilą program.</p>
</div>
<div class="exercise exercise-star-star">
<p>Wypełnij plik <code>hellobye.c</code> treścią:</p>
<pre><code class="c"><!--
-->#include <stdio.h>
<!-- -->
<!-- -->int helloworld(){
<!-- --> printf("Hello World\n");
<!-- --> return 0;
<!-- -->}
<!-- -->
<!-- -->int goodbye(){
<!-- --> printf("Good bye\n");
<!-- --> return 0;
<!-- -->}
<!-- --></code></pre>
<p>Aby utworzyć z niego bibliotekę dynamiczną, wykonaj następujące polecenia:</p>
<pre><code class="no-highlight"><!--
-->$ gcc -c -fPIC hellobye.c
<!-- -->$ gcc -shared -o libhellobye.so hellobye.o
<!-- --></code></pre>
<p>Utwórz plik <code>hello.c</code> o treści:</p>
<pre><code class="c"><!--
-->int helloworld();
<!-- -->int goodbye();
<!-- -->
<!-- -->int main(){
<!-- --> helloworld();
<!-- --> goodbye();
<!-- --> return 0;
<!-- -->}
<!-- --></code></pre>
<p>i skompiluj go z wykorzystaniem utworzonej biblioteki dynamicznej <code>libhellobye.so</code>, wykonując polecenie:</p>
<pre><code class="no-highlight">$ gcc -L . hello.c -l hellobye</code></pre>
<p>Za co odpowiada opcja <code>-L</code>? Dlaczego program nie uruchomi się? Zmodyfikuj wartość zmiennej systemowej <code>LD_LIBRARY_PATH</code> w taki sposób, aby możliwe było uruchomienie programu. Usuń plik biblioteki i sprawdź, czy program dalej będzie działać.</p>
<p><a href="#" class="button button-blue" rel="answer">Zobacz odpowiedź</a></p>
<div class="answer">
<p>Program nie uruchomi się, bo poszukuje bibliotek dynamicznych w określonych katalogach. Katalog bieżący nie należy do tego zbioru. Aby możliwe było przeszukiwanie lokalnego katalogu, należy ustawić wartość zmiennej <code>LD_LIBRARY_PATH</code> na <code>.</code>. Zmienna ta pozwala ustalić, oddzielone przecinkami, ścieżki, które powinny być przeszukiwane przed standardowymi lokalizacjami. Opcja <code>fPIC</code> (ang. <i>Position Independent Code</i>) programu <code>gcc</code> wprowadza odpowiednie zarządzanie pamięcią.</p>
</div>
</div>
</article>
<footer id="main-footer">
<p><a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank"><img src="img/cc-by-nc-sa.png" /></a></p>
<p>© 2016–2018 Bartłomiej Przybylski. Wszystkie materiały zebrane na tej stronie udostępniane są na licencji Creative Commons Uznanie autorstwa-Użycie niekomercyjne-Na tych samych warunkach 4.0 Międzynarodowe.</p>
</footer>
</div>
</body>
</html>