Add Operating System Processes
This commit is contained in:
parent
248ca2307e
commit
c0166072c1
1 changed files with 199 additions and 0 deletions
199
content/systemes_exploitation/2-processus/index.md
Normal file
199
content/systemes_exploitation/2-processus/index.md
Normal file
|
@ -0,0 +1,199 @@
|
|||
---
|
||||
title: "Systèmes d'exploitation : Les processus"
|
||||
date: 2021-09-17
|
||||
tags: ["système", "appels système", "processus"]
|
||||
categories: ["Systèmes d'exploitation", "Cours"]
|
||||
---
|
||||
|
||||
Les processus sont des instances vivants de programmes. Un programme représente
|
||||
du code binaire stocké sur un support de stockage.
|
||||
|
||||
Un processus est un espace d'adressage en mémoire et d'un contexte d'exécution.
|
||||
Plus d'information est disponible [dans les cours de prog. système]({{< ref
|
||||
"../../progsys/3-processus/index.md">}} "Les processus")
|
||||
|
||||
## Accès à la mémoire
|
||||
|
||||
L'espace d'adressage contient des segments mémoire :
|
||||
|
||||
* le *segment de texte* / de code: les instructions optimisées par le
|
||||
compilateur, souvent en lecture seule dans les systèmes modernes.
|
||||
* le *segment data* contenant lui même le segment des *données initialisées* et
|
||||
le BSS (données non-initialisées)
|
||||
* le *tas*, zone de mémoire dynamique gérée par la `libc` par l'utilisation de
|
||||
`malloc()` et `free()`. Le système ne peut détecter un accès en dehors de
|
||||
la plage définie par un `malloc()`. Lorsque le tas n'a plus d'espace alors
|
||||
la `libc` effectue un appel système (mais le noyau peut refuser d'allouer)
|
||||
* la *pile d'exécution*, sa taille est de 8MiB maximum sous Linux. Ce segment
|
||||
contient les paramètres des fonctions et leurs variables locales.
|
||||
* les *librairies partagées* mappees à la demande.
|
||||
|
||||
|
||||
Il est possible de voir les espaces de mémoire alloués pour un processus donné :
|
||||
|
||||
```
|
||||
cat /proc/self/maps
|
||||
55fee9705000-55fee9707000 r--p 00000000 fe:01 1979024 /usr/bin/cat
|
||||
55fee9707000-55fee970c000 r-xp 00002000 fe:01 1979024 /usr/bin/cat
|
||||
55fee970c000-55fee970f000 r--p 00007000 fe:01 1979024 /usr/bin/cat
|
||||
55fee970f000-55fee9710000 r--p 00009000 fe:01 1979024 /usr/bin/cat
|
||||
55fee9710000-55fee9711000 rw-p 0000a000 fe:01 1979024 /usr/bin/cat
|
||||
55feead5a000-55feead7b000 rw-p 00000000 00:00 0 [heap]
|
||||
7fbcfa322000-7fbcfa344000 rw-p 00000000 00:00 0
|
||||
7fbcfa344000-7fbcfa62c000 r--p 00000000 fe:01 1987533 /usr/lib/locale/locale-archive
|
||||
7fbcfa62c000-7fbcfa62e000 rw-p 00000000 00:00 0
|
||||
7fbcfa62e000-7fbcfa654000 r--p 00000000 fe:01 1969528 /usr/lib/libc-2.33.so
|
||||
7fbcfa654000-7fbcfa79f000 r-xp 00026000 fe:01 1969528 /usr/lib/libc-2.33.so
|
||||
7fbcfa79f000-7fbcfa7eb000 r--p 00171000 fe:01 1969528 /usr/lib/libc-2.33.so
|
||||
7fbcfa7eb000-7fbcfa7ee000 r--p 001bc000 fe:01 1969528 /usr/lib/libc-2.33.so
|
||||
7fbcfa7ee000-7fbcfa7f1000 rw-p 001bf000 fe:01 1969528 /usr/lib/libc-2.33.so
|
||||
7fbcfa7f1000-7fbcfa7fc000 rw-p 00000000 00:00 0
|
||||
7fbcfa80f000-7fbcfa810000 r--p 00000000 fe:01 1969517 /usr/lib/ld-2.33.so
|
||||
7fbcfa810000-7fbcfa834000 r-xp 00001000 fe:01 1969517 /usr/lib/ld-2.33.so
|
||||
7fbcfa834000-7fbcfa83d000 r--p 00025000 fe:01 1969517 /usr/lib/ld-2.33.so
|
||||
7fbcfa83d000-7fbcfa83f000 r--p 0002d000 fe:01 1969517 /usr/lib/ld-2.33.so
|
||||
7fbcfa83f000-7fbcfa841000 rw-p 0002f000 fe:01 1969517 /usr/lib/ld-2.33.so
|
||||
7ffccf574000-7ffccf595000 rw-p 00000000 00:00 0 [stack]
|
||||
7ffccf5c8000-7ffccf5cc000 r--p 00000000 00:00 0 [vvar]
|
||||
7ffccf5cc000-7ffccf5ce000 r-xp 00000000 00:00 0 [vdso]
|
||||
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
|
||||
```
|
||||
|
||||
On y voit biens les adresses de début, ceux de fin, les droits (`r`ead,
|
||||
`w`rite, e`x`ecute, `p`rivate)
|
||||
|
||||
L'accès par un processus à un espace mémoire invalide donne lieu à la fameuse
|
||||
`segmentation fault`. Mais il est tout à fait possible de lire et écrire vers
|
||||
une zone non allouée du tas. Par exemple j'initialise un tableau de 10 éléments
|
||||
et les rmpli avec une boule de 15 itérations.
|
||||
|
||||
## Attributs d'un processus
|
||||
|
||||
en plus de l'espace mémoire alloué pour le processus, le noyau stocke en mémoire
|
||||
un ensemble d'attributs : son identitiants (`PID`), sa priorité, l'`UID`
|
||||
(réel/effectif), la table des descripteurs de fichiers, la table des signaux, un
|
||||
espace pour sauvegarder les registres (changement de contexte, reprise sur
|
||||
interruption).
|
||||
|
||||
## Création et vie des processus
|
||||
|
||||
Un processus voulant en créer un autre doit faire un appel système `fork`. Lors
|
||||
du changement de contexte, les registres du processus `p0` sont sauvegardés puis
|
||||
remplacés par ceux de `p1`.
|
||||
|
||||
Les signaux sont délivrés au processus lors du passage du noyau à l'exécution.
|
||||
|
||||
### Processus bloquants
|
||||
|
||||
Lorsqu'un processus attends un appel bloquant (par exemple `read()`) il est muse
|
||||
en sommeil. Lorque l'interruption est lancée, alors le noyau réveille le
|
||||
processus.
|
||||
|
||||
## Ordonnancement
|
||||
|
||||
L'ordonnancement essaye définir un **fonctionnement universel** visant à
|
||||
organiser l'exécution concurente de processus sur un CPU. Un fonctionnement
|
||||
universel, convenant donc à tous les usages, *est impossible à obtenir*. Il
|
||||
dépend en effet de l'utilisation qui en est fait : interactif, temps-réel etc.
|
||||
|
||||
Dans le cadre d'un système interactif, la réactivité est la caractéristique la
|
||||
plus importante.
|
||||
|
||||
### Stratégie
|
||||
|
||||
Le type de système influe donc sur la stratégie à adopter. nous allons en
|
||||
détailler certaines.
|
||||
|
||||
#### FIFO - First In First Out
|
||||
|
||||
Une liste chainée de processus, on exécute le premier jusquà la fin de son
|
||||
exécution ou qu'il soit bloqué puis le second et ainsi de suite.
|
||||
|
||||
C'est une technique facile à implémenter, il est très peu couteux en temps
|
||||
processeur (le noyau intervient peu, peu de changement de contexte) mais
|
||||
comporte un gand risque de **famine** : un processus en boucle ne rendrai jamais
|
||||
la main.
|
||||
|
||||
#### Round-Robin
|
||||
|
||||
Un **temporisateur** valable pour tous: le changent de contexte intervient toute
|
||||
les 10ms par exemple. C'est une technique facile à implementer, il y a plus de
|
||||
famine mais on ne **gère pas de priorité**. S'il y a beaucoup de processus,
|
||||
alors notre éditeut de texte sera moind réactif.
|
||||
|
||||
#### Priorité stricte
|
||||
|
||||
Les processus sont triés par priorité et les plus important son exécutés en
|
||||
premier. contrairement au *Round-Robin* on gère la priorité mais ette technique
|
||||
est discriminatoire. Comment **assigner les priorités**? Au faciès?
|
||||
|
||||
#### Priorité dynamique
|
||||
|
||||
La priorité change au cours de la vie du processus car il change de
|
||||
comportement.
|
||||
|
||||
Dans le cas d'une **opération de compilation** par exemple, le
|
||||
compilateur lit les fichiers sources effectuant beaucoup de `read` et se
|
||||
bloauqnt donc. Ensuite il compile et utilise beaucoup de CPU.
|
||||
|
||||
L'ordonnanceur observe donc les métriques du passé pour prévoir l'avenir. Dans
|
||||
l'example du compilateur, le noyau observe que sur les 10ms de temporisation,
|
||||
notre processus **s'est bloqué (et change de contexte) au bout de 1ms**. Puis
|
||||
losqu'il compile, notre compilateur va rester sur le CPU **pour toute sa
|
||||
tempotisation**.
|
||||
|
||||
Mais comment choisir la bonne priorité en fonction de ces métriques? Tout
|
||||
simplement **en choisissant d'abord les processus les plus courts**. C'est la
|
||||
stratégie utilisée en général dans les **système interactifs**.
|
||||
|
||||
#### La stratégie utilisée dans le noyau Linux 2.4
|
||||
|
||||
Cette version du noyau Linux, la gestion de l'ordonnancement se fait par
|
||||
l'attribution de crédits. Un processus utilisant l'UC le fait en dépensant des
|
||||
crédits, plus il l'utilise plus il en dépense.
|
||||
|
||||
Lorqu'un processus n'a plus de crédit, il ne peut plus utiliser l'UC jusqu'à ce
|
||||
que le noyau en redistribue. Il le fait lorque aucun processus prêt n'a de
|
||||
crédit.
|
||||
|
||||
Les processus n'ayant pas dépensé tous ses crédits se voit prélever un
|
||||
"impots":
|
||||
|
||||
```
|
||||
Crédit = Cn + (Cn-1/2) + (Cn-2/4) + (Cn-3/8) + ...
|
||||
```
|
||||
|
||||
Dans la limite de 2C.
|
||||
|
||||
### Et pour les système multi-cœur
|
||||
|
||||
Chaque cœur exécute un ordonnanceur de façon asynchrone, la liste de processus
|
||||
peut-être :
|
||||
|
||||
* partagée entre tous les cœurs
|
||||
* distribuée par cœur
|
||||
|
||||
Il est bon de noter qu'une **UC peut envoyer une interruption à un autre UC**.
|
||||
|
||||
## Threads et processus
|
||||
|
||||
Un processus est un espace d'adressage plus une pile d'exécution. Un thread est
|
||||
juste un **autre flow d'exécution dans le même espace d'adressage**. La création
|
||||
de threads (aussi appelés processus légers) est dons plus efficace : il n'y a
|
||||
pas de création d'espace d'adressage.
|
||||
|
||||
Dans les noyaux modernes, tout est thread.
|
||||
|
||||
### Accès concurents à la mémoire
|
||||
|
||||
Les threads partagent donc des espace commun de mémoire, il est donc important
|
||||
de gérer des accès concurrent. En effet l'accès aux mêmes cases mémoires par
|
||||
plusieurs threads peut conduire à des fonctionnements arbitraires.
|
||||
|
||||
Il est à noter que les accès à la mémoire sont de toute façon atomique: *une
|
||||
opération de lecture ou écriture à la fois*. Mais ce n'est pas suffisant, un
|
||||
example de code est disponible [dans les cours de prog. système]({{< ref
|
||||
"../../progsys/5_les-processus_legers/index.md">}} "Les processus légers")
|
||||
|
||||
|
||||
Le noyau doit donc mettre en place des primitive de synchronisation.
|
Loading…
Add table
Add a link
Reference in a new issue