From c0166072c15e5660cb2ca82a76cdd2233f3a9b08 Mon Sep 17 00:00:00 2001 From: Yorick Barbanneau Date: Sun, 7 Nov 2021 01:48:58 +0100 Subject: [PATCH] Add Operating System Processes --- .../2-processus/index.md | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 content/systemes_exploitation/2-processus/index.md diff --git a/content/systemes_exploitation/2-processus/index.md b/content/systemes_exploitation/2-processus/index.md new file mode 100644 index 0000000..9b56f6e --- /dev/null +++ b/content/systemes_exploitation/2-processus/index.md @@ -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.