forked from IUTInfoMontp-M2101/cours
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cours04.pug
330 lines (312 loc) · 13 KB
/
cours04.pug
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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
extends coursXX.pug
append preamble
- date = "2019-02-26";
- title = "Cours n°4 :<br>Création de processus";
block document
section.split
h1 Hiérarchie de processus
.side
pre
code
| $ ps ax
|
| PID PPID S COMMAND
| <span class="highlight"> 1 0 S /sbin/init</span>
| 338 1 S /sbin/udevd --daemon
| 481 338 S /sbin/udevd --daemon
| 1200 880 S lightdm --session-child
| 1301 1200 S gnome-session
| 1528 1 S /usr/lib/gvfs/gvfsd
| 1532 1301 S nm-applet
| 1556 1301 S /usr/lib/gnome-settings
| 1590 1 S /usr/lib/gvfs/gvfs-gdu
| 1627 1 S /usr/lib/bamf/bamfdaemon
| 1636 1635 S /usr/bin/gtk-window
| 1639 1 S /usr/lib/unity/unity
| 1845 1 S /usr/lib/gnome-online
| <span class="highlight">1853 1</span> R gnome-terminal
| 2414 1 S /usr/lib/dconf/dconf
| <span class="highlight">2431 1853</span> S bash
| <span class="highlight">2597 2431</span> R ps ax</code></pre>
.side
p Au démarrage de l'ordinateur, le processus <code>init</code> est lancé
ul
li Par la suite, tous les autres processus sont démarrés par un processus existant (parent)
li Chaque processus est identifié par un numéro unique (PID) et connaît le numéro de son parent (PPID)
li La commande <code>ps</code> permet d'obtenir le PID et le PPID d'un processus
section.single
h1 Hiérarchie (pstree)
pre
code
| $ <span class="highlight">pstree -p</span>
|
| <span class="highlight">init(1)</span>─┬─NetworkManager(793)─┬─dhclient(911)
| │ └─dnsmasq(1012)
| ├─accounts-daemon(1214)───{accounts-daemon}(1215)
| ├─cron(855)
| ├─cupsd(729)───dbus(3916)
| ├─dconf-service(1997)─┬─{dconf-service}(1998)
| │ └─{dconf-service}(2000)
| ├─gnome-keyring-d(1407)─┬─{gnome-keyring-d}(1408)
| │ └─{gnome-keyring-d}(1970)
| ├─<span class="highlight">gnome-terminal(1737)</span>─┬─<span class="highlight">bash(1746)</span>───<span class="highlight">pstree(20545)</span>
| │ ├─gnome-pty-helpe(1745)
| │ └─{gnome-terminal}(1747)
| ├─goa-daemon(1895)───{goa-daemon}(1911)
| ├─gvfs-afc-volume(1612)───{gvfs-afc-volume}(1614)
| ├─mission-control(1890)─┬─{mission-control}(1893)
| │ └─{mission-control}(1909)
| ├─sh(884)───initctl(886)
| └─udisks-daemon(1604)───udisks-daemon(1609)
section.split
h1 Création
.side
.only(data-end=1)
pre
code.cpp
| int main(int argc, char **argv) {
| pid_t pid = fork();
| if (pid == -1) {
| perror("fork");
| } else if (pid == 0) {
| for (int i=0; i<3; i++) {
| printf("F%d\n", i);
| }
| } else {
| for (int i=0; i<3; i++) {
| printf("P%d ", i);
| }
| }
| }
hr
pre
code
| $ ./a.out
|
| P0 P1 P2 F0 F1 F2
.only(data-start=2)
pre
code.cpp
| // fork bomb !
| int main(int argc, char **argv) {
| while (1) fork();
| }
.center
img(src="cours04/cours04-BobOmb.jpg" style="width: 50%" alt="Bob Omb")
.side
p.skip Pour créer un nouveau processus, on clone un processus existant à l'aide de l'appel <code>fork()</code>
.only(data-step=0)
p Le système copie :
ul
li la mémoire (pile, tas, code)
li les descripteurs de fichiers ouverts (cependant les pointeurs dans les fichiers sont partagés)
li l'état d'exécution (pointeur d'exécution, registres du processeur)
.only(data-start=1)
p Différences entre les processus :
ul
li le processus fils a un PID et un PPID différent du père (son PPID est le PID du père)
li la fonction <code>fork</code> renvoie 0 dans le processus fils, et le PID du fils dans le processus père
section.split
h1 Attente de complétion
.side
pre
code
| SYNOPSIS
| #include <sys/types.h>
| #include </wait.h>
|
| pid_t wait(int *status);
| pid_t waitpid(pid_t pid, int *status, int options);
.side
p Les commandes <code>wait</code> et <code>waitpid</code> demandent à un processus d'attendre que ses
| processus fils changent d'état
ul
li Lorsqu'un fils se termine, la commande <code>wait</code> permet au parent de recevoir le code de
| retour du fils
li Le fils est supprimé de la table de processus quand son père reçoit le code de retour
section.split
h1 Zombies
.side
pre
code.cpp.only(data-step=0)
| int main(int argc, char **argv) {
| pid_t pid;
| pid = fork();
| if (pid == 0) { // fils
| sleep(10);
| printf("Fin fils\n");
| } else { // père
| sleep(20);
| printf("Fin père\n");
| }
| exit(1);
| }
code.only(style="margin-bottom: calc(20 * var(--mm))")
| $ ps
| PID TTY STAT COMMAND
| 1859 pts/0 Ss bash
| 2570 pts/0 S ./prog
| 2571 pts/0 Z <span class="highlight">[prog] <defunct></span>
img(src="cours04/cours04-zombie.png" style="width: 28%; position: absolute; bottom: 0; left: 27%" alt="Zombie")
.side
p Lorsqu'un processus se termine, il n'est pas immédiatement supprimé de la table
ul
li Le parent doit explicitement le supprimer à l'aide de l'appel <code>wait</code>
li Si le processus reste, il devient un <em>zombie</em>
li Les processus zombies n'occupent pas de mémoire, mais ils bloquent un PID (et sont souvent le signe d'un
| bug dans le processus parent)
li Si le parent est terminé avant le fils, le fils devient orphelin et est récupéré par <code>init</code>
| (qui appelle <code>wait</code> régulièrement)
section.split
h1 Éviter les zombies
.side
pre
code.cpp.only(data-step=0)
| int main() {
| int pid1;
| pid1 = fork();
| if (pid1) {
| // père
| /* Tâche principale */
| } else {
| // fils
| /* Tâche secondaire */
| }
| }
code.cpp.only
| static void handler(int signo) {
| wait(NULL);
| }
|
| int main() {
| <span class="highlight">signal(SIGCHLD, handler);</span>
| int pid1;
| pid1 = fork();
| if (pid1) {
| // père
| /* Tâche principale */
| } else {
| // fils
| /* Tâche secondaire */
| }
| }
code.cpp.only
| int main() {
| int pid1, pid2;
| pid1 = fork();
| if (pid1) {
| // père
| <span class="highlight">waitpid(pid1, NULL, 0);</span>
| /* Tâche principale */
| } else {
| // fils
| <span class="highlight">pid2 = fork();</span>
| if (pid2) {
| // fils
| <span class="highlight">exit(0);</span>
| } else {
| // petit- fils
| /* Tâche secondaire */
| }
| }
| }
.side
p Parfois, la tâche exécutée par le fils est longue et on ne veut pas bloquer le père en attente de complétion
ul
li On peut mettre en place une gestion de signaux (SIGCHLD)
li On peut faire en sorte que le processus fils soit immédiatement adopté par <code>init</code>
section.single
h1 fork
p.skip La fonction <code>fork</code> est appelée par un processus mais renvoie deux résultats, dans deux processus
| distincts
ul
li Le père reçoit le PID du fils
li Le fils peut obtenir le PID du père : <code>getppid()</code>
li Si le fils et le père doivent avoir des comportements différents, il faut que les deux aient l'ensemble
| du code
span.uncover ... sauf s'il existe un moyen de changer le code d'un processus</span>
section.split
h1 Exécution
.side
pre
code.cpp.only(data-end=1)
| #include <unistd.h>
|
| extern char **environ;
|
| int execl(const char *path, const char *arg, ...);
| int execlp(const char *file, const char *arg, ...);
| int execle(const char *path, const char *arg, ..., char * const envp[]);
| int execv(const char *path, char *const argv[]);
| int execvp(const char *file, char *const argv[]);
code.cpp.only(data-step=2)
| int main(int argc, char **argv) {
| pid_t pid;
| int status;
| pid = fork();
| if (pid == 0) {
| <span class="highlight">execlp("ls", "ls", "-l", NULL);</span>
| } else {
| printf("Père \n");
| wait(&status);
| }
| }
code.cpp.only
| int main(int argc, char **argv) {
| pid_t pid;
| int status;
| pid = fork();
| if (pid == 0) {
| char *args[3];
| args[0] = "ls";
| args[1] = "-l";
| args[2] = NULL;
| <span class="highlight">execvp("ls", args);</span>
| } else {
| printf("Père \n");
| wait(&status);
| }
| }
.side
.only(data-step=0)
p La famille de fonctions exec permet de remplacer le code d'un processus par un autre
ul
li le premier argument est un exécutable
li les arguments suivants permettent de passer des paramètres et des variables d'environnement
li à l'appel de la fonction, tout le code du processus est remplacé par celui indiqué et l'état du processus
| est réinitialisé
.only(data-start=1)
p Il existe plusieurs variantes :
ul
li <code>l</code> (<em>list</em>) : les arguments sont passés un par un à la fonction, avec un pointeur
| nul (<code>NULL</code>) en dernier
li <code>v</code> (<em>vector</em>) : les arguments sont passés dans un unique tableau
li <code>p</code> (<em>path</em>) : l'exécutable est cherché dans les répertoires du chemin d'exécution
li <code>e</code> (<em>environment</em>) : permet de passer un tableau contenant des variables
| d'environnement pour l'exécution du nouveau programme
section.split
h1 Exemple : ls
.side
.center
img(src="cours04/cours04-exec.svg" style="width:80%" alt="exec")
.side
p Le shell veut exécuter la commande <code>ls</code>
ul
li <code>fork()</code> pour créer le processus fils
li le père attend que le fils ait fini son exécution (si la commande est en arrière plan le père ne se bloque
| pas)
li le fils appelle <code>exec</code> pour remplacer son code par celui du programme <code>ls</code>
li quand le fils termine, le père reçoit le code de retour, supprime le processus fils et reprend son exécution
section.single
h1 Copie sur écriture
p L'utilisation de <code>fork</code>/<code>exec</code> est le moyen le plus classique (et parfois le seul
| disponible) pour créer des nouveaux processus
ul
li Si le nouveau processus va être remplacé par un autre programme (<code>exec</code>) il est inutile de
| copier toute sa mémoire au moment du <code>fork</code>
li Pour alléger, on utilise souvent une technique appelée <em>copy on write</em> :
ul
li lorsque le nouveau processus est créé, on ne copie pas sa mémoire
li il dispose de pointeurs vers la mémoire du père pour la lecture
li la mémoire est copiée au moment de la première écriture par le fils
li Dans le cas où <code>exec</code> suit <code>fork</code>, la mémoire n'est pas copiée