add os & concurrent programmation (#22)

This commit is contained in:
Rémi Heredero authored and GitHub committed 2024-03-23 17:06:51 +01:00
commit 7d45d9d8e4
14 files changed
+437 -1

No files matched your search

+1
View File
@@ -164,6 +164,7 @@
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-xml.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-sql.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-pascal.js"></script>
<!----- Dark Theme ----->
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"> </script>
+240
View File
@@ -0,0 +1,240 @@
<header><h1>
Programmation concurrente
</h1></header>
Un programme concurrent est un programme non séquentiel.
Soit l'expression arithmétique suivante : $(a+b) * (c+d)$.
***Programmation séquentiel***
1. $x = (a+b)$
2. $y = (c+d)$
3. $x*y$
***Programmation concurrente***
1. $x = (a+b) \qquad y = (c+d)$
2. $x*y$
La programmation concurrente sur 1 CPU permet d'exploiter pleinement les périphériques parallèlement au processeur.
Cela permet d'éviter de bloquer toutes les applications lorsqu'une seule est en attente d'une réponse.
La programmation concurrente sur plusieurs CPU permet en plus un gain de temps.
# Exclusion mutuelle
un programme est une entité statique
un processus est une entité dynamique
Les processus ne sont jamais totalement indépendants.
Ils partagent des ressources (mémoire, fichiers, périphériques, variable, etc.)
Ceci peut conduire à des erreurs et doit donc être controlé.
- ***Section critique :*** partie du code qui accède à une ressource partagée.
- ***Exclusion mutuelle :*** un seul processus à la fois peut accéder à la section critique.
- ***Ressource critique :*** ressource qui doit être accédé en exclusion mutuelle
On pourrait éviter les problèmes d'exclusion mutuelle si on pouvait assurer que deux processus n'exécute jamais en même temps une section
critique.
On peut résoudre ce problème à l'aide de **mutex** ou **sémaphore**.
## Exclusion mutuelle par attente active
```pascal
type processNum = 1..2;
module CS; /* Section critique */
defines Enter, Leave; /* Primitive */
var
request : array processNum of boolean;
tour : processNum;
procedure Enter(num: processNum);
begin
request[num] := true; /* Demande d'accès */
tour := 3 – num; /* Autre processus */
repeat
/* Attendre que l'autre processus ait fini avec "not request" */
/* Priorité si les deux processus demandent en même temps à la section critique
grâce à "tour = num" */
until (not request[3-num]) or (tour = num)
end Enter;
procedure Leave(num: processNum);
begin
request[num] := false
end Leave;
begin /* Initialisation */
request[1] := false;
request[2] := false
end CS;
/***********************************************************************/
process P1;
uses CS, Console;
begin
CS.Enter(1);
Console.Write(”Error on hard disk !”);
CS.Leave(1);
end P1;
/***********************************************************************/
process P2;
uses CS, Console;
begin
CS.Enter(2);
Console.Write(”Printer not ready !”);
CS.Leave(2);
end P2;
```
- si les processus *P1* et *P2* ne tentent pas de pénétrer simultanément dans la section
critique, la variable *request[3-num]* est fausse et l'entrée en section critique est
autorisée
- si par contre les processus *P1* et *P2* tentent de pénétrer simultanément dans la section
critique, la variable *tour* permet d'arbitrer entre les deux demandes. La variable
*tour* (à la ligne 15) indique le numéro du processus élu
Cette méthode peut facilement être généralisée à n processus. Il suffit de faire *tour := n+1 – num* et
*request[n+1-num]*.
Cette solution n'est pas satisfaisante, car elle consomme beaucoup de ressources CPU. En effet, un processus
qui attend l'accès à la section critique est en attente active. Il est donc en boucle constante et consomme
beaucoup de ressources CPU.
## Exclusion mutuelle par masquage des interruptions
Masquer les interruptions pour résoudre le problème de l'exclusion mutuelle.
Plus d'interruption $=>$ plus de commutation de processus $=>$ plus de risque de violer l'exclusion mutuelle.
Le danger avec cette solution est le risque de perte d'information provenant des périphériques. Cela ne peut être une bonne solution
que si la section critique est très courte. Il est dangereux de laisser la possibilité de masquer les interruptions à l'utilisateur.
## Instruction TAS (TestAndSet)
L'exclusion mutuelle par masquage des interruptions n'est pas possible dans une architecture multiprocesseur.
Il est possible de résoudre le problème de l'exclusion mutuelle en utilisant l'instruction *TAS*.
Elle permet de tester et de modifier une variable en **une seule instruction** assembleur.
# Mutex
Le mutex est utilisé pour bloquer l'accès simultané aux sections critiques avec les primitives suivantes :
- ***block() :*** Il laisse passer un seul processus à la fois. Si la section est déjà bloquée, le processus et mis dans
une file d'attente.
```pascal
procedure Block(var v:lock);
begin
if v.state = OPEN then
v.state := CLOSE
else
block the process in the list "v.waiting"
end if
end Block;
}
```
- ***release() :*** Il libère la section critique, si la file d'attente est vide. Sinon, il passe la main au processus
suivant.
```pascal
procedure Release(var v:lock);
begin
if queue "v.waiting" not empty then
unblock first process in the list "v.waiting"
else
v.state := OPEN
end if
end Release;
}
```
Ces procédures sont elles même des sections critiques. Elles sont cepandant très courte et ne contiennent aucunes entrées-sorties.
Elles peuvent donc être implémentées à l'aide d'une [Exclusion mutuelle par masquage des interruptions](#exclusion-mutuelle-par-masquage-des-interruptions)
ou d'une [instruction TAS](#instruction-tas-testandset).
Les Mutex restent un outil primitif. Ils ne s'appliquent pas sur des problèmes plus complexes.
# Sémaphore
Le sémaphore est une généralisation du mutex. Il permet de bloquer l'accès à une ressource partagée par plusieurs processus.
Il limite l'accès à plusieurs même ressources utilisées par plusieurs processus.
Par exemple : les processus ont besoin d'imprimer et il y a plusieurs imprimantes à disposition.
Le sémaphore va limiter l'accès au nombre d'imprimantes disponibles.
Les primitives de sémaphores sont :
- ***P*** (Passeren = passer) appelé aussi *wait*, *up*, *get* ou *lock*
```pascal
procedure P(var s:semaphore);
begin
s.num := s.num – 1;
if s.num < 0 then
block the process at the end of the list "s.waiting"
end if
end P;
```
- ***V*** (Vrijgeven = libérer) appelé aussi *signal*, *down*, *release* ou *unlock*
```pascal
procedure V(var s:semaphore);
begin
s.num := s.num + 1;
if s.num <= 0 then
unblock first process in the list "s.waiting"
end if
end V;
```
À l'initialisation, le sémaphore est initialisé au nombre de mêmes ressources disponibles.
Ou alors, il est initialisé à 1 pour un mutex.
Ou alors, il est initialisé à 0 pour un sémaphore binaire (file d'attente).
## Interblocage
L'interblocage est un problème qui peut survenir lorsqu'un ensemble de processus se bloquent mutuellement.
On appelle aussi cela un **DEADLOCK**.
## Résolution reader / writter
Le but est qu'il peut avoir plusieurs processus qui puisse lire la même ressource et aucun modifier la ressource.
Sinon, **un** processus peut modifier la ressource et aucun peut lire.
```cpp
void BeginRead(void) {
osSemaphoreAcquire(rw, osWaitForever);
osSemaphoreAcquire(mutexNbReader, osWaitForever); // ENTER CRITICAL SECTION
nbReader++;
if(nbReader == 1) osSemaphoreAcquire(w, osWaitForever);
osSemaphoreRelease(mutexNbReader); // EXIT CRITICAL SECTION
osSemaphoreRelease(rw);
}
void EndRead(void) {
osSemaphoreAcquire(mutexNbReader, osWaitForever); // ENTER CRITICAL SECTION
nbReader--;
if(nbReader == 0) osSemaphoreRelease(w);
osSemaphoreRelease(mutexNbReader); // EXIT CRITICAL SECTION
}
void BeginWrite(void) {
osSemaphoreAcquire(rw, osWaitForever);
osSemaphoreAcquire(w, osWaitForever);
}
void EndWrite(void) {
osSemaphoreRelease(w);
osSemaphoreRelease(rw);
}
```
# Synchronisation
Etat d'un processus :
![img_1.png](img_1.png)
Le processeur possède une liste de descripteur caractérisant chaque processus. Ces descripteurs de processus qui
contiennent le registre de status et un pointeur sur sa stack. Ils sont utilisé pour faire des switching context :
1. Le noyau va sauvegarder les registres de travail du processus en cours sur la stack.
2. Il va sauvegarder aussi le registre de status et le pointeur de stack dans la liste de descripteurs.
3. Il va prendre le descripteur du prochain processus et utiliser ses registres.
4. Le processus continue jusqu'au nouveau switching context.
+3 -1
View File
@@ -4,4 +4,6 @@ PTR - Programmation Temps Réel
- [XF](/infotro/PTR/XF.md)
- [UML](/infotro/PTR/UML)
- [RealTime](/infotro/PTR/RealTime)
- [RealTime](/infotro/PTR/RealTime)
- [SYS - Systèmes](/infotro/PTR/SYS)
- [PCO - Programmation Concurrente](/infotro/PTR/PCO)
+163
View File
@@ -0,0 +1,163 @@
<header><h1>
Introduction aux systèmes d'exploitation
</h1></header>
![operating_system.png](medias/operating_system.png)
Le système d'expoitation virtualise la partie hardward pour :
- Simplifie l'utilisation
- Gère les ressources physiques
- Processeur
- Mémoire principale
- Entrées-sorties (disques, interfaces réseaux, ...)
L'OS gère les ressources à l'aide de technique de gestion des mémoires et de partage de ressources
partageable entre plusieurs utilisateurs (ex : threads).
Pour ce faire, il
- connait à tout moment l'utilisateur d'une ressource
- gère les accès à une ressource
- accorde l'usage d'une ressource
- évite les conflits
Les premiers systèmes d'exploitation étaient des systèmes à traitement par lots.
Les programmes étaient regroupés en lots et exécutés les uns après les autres.
Les programmes étaient chargés en mémoire et exécutés sans intervention de l'utilisateur.
## Machine à transistors
- Coûtent cher
- Les utilisateurs ne peuvent pas interagir avec le système
- Les utilisateurs écrivent leurs programmes en Fortran et en assembleur
<b><u>Fonctionnement :</b></u>
1. Programmation d'une carte perforée en Fortran ou en assembleur
2. L'opérateur prend les cartes perforées et les charge dans le compilateur
3. L'opérateur récupère les cartes perforées compilées et les charge dans "l'ordinateur"
4. L'ordinateur exécute le programme
5. S'il y a une erreur, l'opérateur imprime l'image de la mémoire sur un listing et doit tout recommencer.
## Systèmes à traitement par lots
Pour accélérer les opérations, deux méthodes sont utilisées :
- ***Le traitement par lots (batch processing) :*** Regroupe et traite à la suite les travaux similaires
- ***Le moniteurs d'enchaînement (linking monitor) :*** Les travaux sont automatiquement enchaîné et
executé par un programme spécialisé (ancêtre des systèmes d'exploitation)
- Une place mémoire lui est réservée dans la mémoire centrale qui se divise donc en une zone moniteur
et une zone utilisateur
## Sysèmes avec spooling
Les systèmes avec spooling permettent d'accéder simultanément à plusieurs périphériques d'entrée/sortie.
Les travaux sont stockés dans une file d'attente (spool) et sont traités par un programme spécialisé.
Le spooling permet de traiter plusieurs travaux en même temps.
La mémoire est partagée en quatre programmes :
- Le programme d'entrée
- Le programme d'enchaînement
- Le programme de l'utilisateur
- Le programme de sortie
![spooling.png](medias/spooling.png)
## Système à multi-programmation
La multi-programmation permet de partager le processeur entre plusieurs programmes.
Chaque programme est chargé en mémoire et est exécuté en alternance.
Dès qu'un programme doit lire des données ou écrire des résultats, il est mis en attente et un autre
programme est exécuté afin d'optimiser l'utilisation du processeur.
![multiprogramming .png](medias/multiprogramming.png)
La commutation de l'exécution d'un programme à un autre est assuré par l'**ordonnanceur** (scheduler).
*Inconvénients :* Les programmes cours, sont pénalisés car leur temps d'exécution dépend aussi des autres
programmes en cours d'exécution.
## Système à temps partagé
Le système à temps partagé permet à plusieurs utilisateurs d'accéder simultanément à l'ordinateur.
Chaque utilisateur dispose d'un temps d'utilisation du processeur (time slice) qui est très court (ex. 40 msec).
![time-sharing.png](medias/time-sharing.png)
## Système temps réel
Les systèmes temps réel doivent être capables réagire au rythme où les données arrivent et repartent.
Ils ont des contraîntes plus hautes ques les systèmes à temps partagé, qui peuvent se permettent d'attendre
alors qu'un système temps réel aurait des conséquences fâcheuses.
## Systèmes multi-processeur répartis
Dans un système multi-processeur répartis, chaque processeur dispose de son propre système d'exploitation.
Les systèmes d'exploitation communiquent entre eux pour partager les ressources.
# Mécanisme d'entrées-sorties
Pour accéder aux périphériques, le processeur utilise des mécanismes d'entrées sorties.
## Les registres d'un processeur
Les registres contenus dans le processeur sont regroupés selon leur fonctionnalités
- Registre généraux
- Registre spécialisés
- Le **registre d'instruction** IR (Instruction Register) contient l'instruction courante
- Le **compteur ordinal** PC (Program Counter) contient l'adresse de l'instruction courante
- Le **registre d'état** SR (Status Register) contient des informations sur l'état du processeur
Ces regstres caractérisent le contexte d'un programme. Il s'agit de la partie sauvegardée lors d'un
*switching context* (changement de contexte).
## Les registres d'un périphérique
Les périphériques disposent, pour comuniquer avec le processeur, d'un certain nombre de registres:
- **Registre de données** DR (Data Register) : Contient les données à lire ou à écrire
- **Registre d'état** SR (Status Register) : Contient des informations sur l'état du périphérique
- **Registre de commande** CR (Command Register) : Détermine les caractéristiques du périphérique
## Entrées-sorties en boucle d'attente
Dans ce mode, le processeur attend que le périphérique soit prêt pour effectuer une opération.
Le processeur effectue une boucle d'attente jusqu'à ce que le périphérique soit prêt.
1. Le processeur contrôle le registre d'état du périphérique
2. Si le périphérique n'est pas prêt, le processeur effectue une boucle d'attente. S'il est prêt :
3. Le processeur écrit les données dans le registre de données
4. Le processeur écrit la commande dans le registre de commande
5. Le périphérique effectue l'opération
6. Le périphérique met à jour le registre d'état
7. Le processeur lit les données dans le registre de données
Si le périphérique notifie le processeur :
1. Le périphérique va changer son statut
2. Le processeur va pouvoir aller chercher les données dans le registre de données
## Les interruptions
Les interruptions permettent de signaler au processeur qu'un événement s'est produit.
Le signal est la conséquence d'un évènement interne au programme et résultant de son exécution ou
externe et indépendant de son exécution. Toute l'information nécessaire pour traiter l'interruption
est regroupé en mémoire dans deux mots consécutis, appelés **vecteur d'interruption** (vector interrupt).
On y trouve :
- L'adresse de la routine de traitement de l'interruption
- Le registre d'état du processeur pour cette routine.
Lorsqu'une interruption se produit, le processeur effectue un switching context et exécute la routine.
On distingue en général au moins trois niveaux d'interruption :
- Les interruptions externes
- Les exceptions internes
- Les appels systèmes
![interruptions.png](medias/interruptions.png)
## La DMA
La [DMA](/infotro/SEm/DMA) (Direct Access Memory) permet de faire des transferts vers la mémoire rapidement.
Le processeur laisse le controller DMA accéder au périphérique pour amener les données directement dans la
mémoire et éviter de perdre du temps en passant par le processeur qui doit faire une copie des données.
Elle est très utile lorsqu'il y a un grand nombre de données.
+26
View File
@@ -5,6 +5,13 @@ UML
## Class
### Association bidirectionnel
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
classDiagram
direction LR
A -- B
```
```plantuml
@startuml
A - B : " "
@@ -98,7 +105,26 @@ vt is Infection
@enduml
```
## Sequence
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
sequenceDiagram
participant a as A
participant b as B
a ->> b: synchrone / call
a -) b: asynchrone
activate b
b -->> a: replay
deactivate b
b ->> b: sync auto call
b -) b: async auto call
```
```plantuml
@startuml
participant A as a
Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

+2
View File
@@ -4,6 +4,8 @@
- [XF](/infotro/PTR/XF.md)
- [UML](/infotro/PTR/UML)
- [RealTime](/infotro/PTR/RealTime)
- [SYS - Systèmes](/infotro/PTR/SYS)
- [PCO - Programmation Concurrente](/infotro/PTR/PCO)
## [SDi - Système Distribué](/infotro/SDi/)
+2
View File
@@ -2,6 +2,8 @@
* [XF](/infotro/PTR/XF)
* [UML](/infotro/PTR/UML)
* [RealTime](/infotro/PTR/RealTime)
* [SYS - Systèmes](/infotro/PTR/SYS)
* [PCO - Programmation Concurrente](/infotro/PTR/PCO)
* [SDi - Systèmes Distribués](/infotro/SDi)
* [Transmission process](/infotro/SDi/transmission_process.md)
* [IOT Technologies](/infotro/SDi/iot_technologies.md)