Vous êtes sur la page 1sur 45

2- Systèmes d’exploitation et programmation concourante

Systèmes d’exploitation et programmation concourante


Cours du Système Embarqué et Temps Réel

Prof. Nabil KANNOUF

UAE – ENSAH

18 octobre 2021
2- Systèmes d’exploitation et programmation concourante
2.4- Threads

Fin de Séance
Vos Questions ?
2- Systèmes d’exploitation et programmation concourante
2.5- Problème du producteur-consommateur

2.5- Problème du producteur-consommateur


2- Systèmes d’exploitation et programmation concourante
2.5- Problème du producteur-consommateur

État de concurrence

Threads dans un processus se partagent la mémoire


� Un thread peut accéder à l’ensemble de la mémoire de son processus, sans contrainte
� Partage et échange d’information entre threads par mémoire partagée
� Risque d’incohérence si lecture-écriture ou écriture-écriture simultanées
État de concurrence (race condition) : situation où deux ou plusieurs threads (ou
processus) lisent et écrivent des données partagées et que le résultat dépend de quel
thread s’est exécuté à quel moment
� Débogger des états de concurrence est ardu
� Ex. : exécuter dans certaines conditions peut toujours donner des résultats valides,
mais avec un léger changement de conditions peut mener à d’autres résultats (ou une
erreur)
2- Systèmes d’exploitation et programmation concourante
2.5- Problème du producteur-consommateur

Problème du producteur-consommateur

Deux threads roulent en concurrence sur un processeur


� Thread producteur : produit des données qui sont ajoutées dans le tableau partagé
� Thread consommateur : retire des données du tableau partagé
Problèmes lorsque tableau plein ou vide
� Lorsque tableau plein, producteur se met en veille, se réveillant lorsque consommateur
a retiré des données
� Lorsque tableau vide, consommateur se met en veille, se réveillant lorsque producteur
a ajouté des données
2- Systèmes d’exploitation et programmation concourante
2.5- Problème du producteur-consommateur

Problème du producteur-consommateur

Tiré de Andrew S. Tanenbaum et Herbert Bos. Modern Operating Systems. Prentice Hall Press, 2015.
2- Systèmes d’exploitation et programmation concourante
2.5- Problème du producteur-consommateur

Problème du producteur-consommateur

État de concurrence avec problème du producteur-consommateur


� Tableau vide, consommateur viens de lire que count vaut 0 (ligne 16)
� Ordonnanceur change de thread avant que consommateur soit en veille, exécutant
producteur
� Producteur ajoute élément au tableau, incrémente count (lignes 8 et 9)
� Comme count vaut maintenant 1, réveille consommateur (ligne 10)
� Mais consommateur n’est pas en veille et signal de réveil est perdu
� Prochaine exécution du consommateur, il va se mettre en veille
� Producteur va remplir le tableau et aussi se mettre en veille
� Les deux threads sont en veille, attendant l’un après l’autre
Essence du problème : signal de réveil envoyé avant que l’autre thread soit en veille

� Courses entre threads doivent être bien encadrées


� Préemption à moments arbitraires génère des bogues parfois très difficiles à trouver
2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

2.6- Exclusion mutuelle et conditions


2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

Primitive d’exclusion mutuelle


Contrôler les accès aux sections critiques est nécessaire
� Primitive couramment utilisée à cette fin : MUTEX (MUTual EXclusion)
Mutexes peuvent être bloqués (lock) et débloqués (unlock) par des opérations
atomiques
� Opérations atomiques ne peuvent être préemptées
� Souvent implémenté à l’aide d’une primitive matériel
� Typiquement, instruction test-and-set : copie valeur d’un emplacement mémoire dans
registre et écrit valeur 1 dans emplacement mémoire
2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

Exclusion mutuelle
2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

Mutex POSIX

Création et destruction de mutex POSIX

� Retourne 0 si exécution correcte, code d’erreur autrement


� Utiliser NULL pour attr pour créer mutex avec attributs par défaut
Bloquer/débloquer mutex POSIX

� pthread_mutex_lock bloquant, retourne lorsque mutex obtenu


� pthread_mutex_trylock non bloquant, retourne même si le mutex non disponible
(valeur de retour EBUSY)
2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

Attributs de mutex
Mutexes peuvent être créés avec différents attributs

Modifier types de mutex

� PTHREAD_MUTEX_NORMAL : mutex standard sans vérification d’erreurs


� PTHREAD_MUTEX_ERRORCHECK : mutex faisant vérification d’erreurs
� PTHREAD_MUTEX_RECURSIVE : permet de bloquer plusieurs fois le même mutex
par même thread sans inter blocage
� Maintient compte de blocage, libéré seulement lorsque autant de déblocages que de
blocages préalables effectués
� PTHREAD_MUTEX_DEFAULT : modèle par défaut, peut être un des trois précé-
dents selon l’implémentation
2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

Reader-Writer Locks

Avec mutex, l’état est soit bloqué, soit débloqué


� Écriture de valeurs : exclusion mutuelle est nécessaire
� Lectures simultanées de valeurs : accès simultanés peuvent être possibles
Primitive POSIX de reader-writer locks

� Cohérent avec interface de mutex, mais accès simultanés en lecture permis


2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

Conditions

Condition : permettre à des threads de se synchroniser


2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

Conditions

Attendre qu’une condition soit satisfaite

� Chaque condition est protégée par un mutex


� Thread doit acquérir mutex avant d’évaluer ou changer la condition
� Mutex libéré durant l’attente
Signaler qu’une condition est satisfaite

� pthread_cond_signal réveille au moins un thread en attente


� pthread_cond_broadcast réveille tous les threads en attente
2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

Problème du producteur-consommateur

Solution au problème du producteur-consommateur avec mutex et conditions


� Solution tirée de Andrew S. Tanenbaum et Herbert Bos. Modern Operating Systems.
Prentice Hall Press, 2015.
� Thread consommateur : retire des données du tableau partagé
2- Systèmes d’exploitation et programmation concourante
2.6- Exclusion mutuelle et conditions

Problème du producteur-consommateur
2- Systèmes d’exploitation et programmation concourante
2.7- Barrières, fonctions réentrantes et lecture de fichiers

2.7- Barrières, fonctions réentrantes et lecture de fichiers


2- Systèmes d’exploitation et programmation concourante
2.7- Barrières, fonctions réentrantes et lecture de fichiers

Barrières

Barrières : synchroniser plusieurs threads s’exécutant en parallèle

� pthread_barrier_wait : threads en veille tant que le compte de la barrière n’est pas


atteint
� Compte de la barrière donné comme argument count de la fonction pthread_barrier_init
2- Systèmes d’exploitation et programmation concourante
2.7- Barrières, fonctions réentrantes et lecture de fichiers

Barrières
2- Systèmes d’exploitation et programmation concourante
2.7- Barrières, fonctions réentrantes et lecture de fichiers

Fonctions réentrantes
Fonctions réentrantes
� Plusieurs exécutions simultanées de la fonctions possibles
� Exemple problématique : malloc
� Allocation dynamique de mémoire demande de modifier structure de donnée interne
� Si deux ou plusieurs threads modifient la structure de données simultanément, sérieux
problèmes en perspective
Deux stratégies principales
� Utiliser mutex et autres primitives pour protéger sections critiques
� Engendre surcoût significatifs, autant pour processus à un thread que multithreadé
� Ne pas utiliser de variables globales, utiliser seulement arguments de la fonction
� Exige de modifier l’API
� Expose structure de données internes à l’utilisateur
mplémentation modernes offrent fonction réentrantes
� Par défaut, fonctions dans POSIX sont réentrantes
� Pour les exceptions, existe souvent version _r qui est réentrante (ex. asctime
vs asctime_r )
2- Systèmes d’exploitation et programmation concourante
2.7- Barrières, fonctions réentrantes et lecture de fichiers

Lecture de fichiers et multithreading

Accès à un fichier par plusieurs threads peut être problématique


� Fonctions standards sont réentrantes
� Mais les performances peuvent se dégrader si on ne fait pas attention
� Chaque appel requiert d’acquérir un lock interne (appel au SE)
� Répéter lecture de quelques caractères à la fois peut être très coûteux
Accès à un fichier par plusieurs threads peut être problématique

� Quatre dernières fonctions doivent être encadrées par flockfile (ou ftrylockfile) et
funlockfile
2- Systèmes d’exploitation et programmation concourante
2.8- Pipes Unix

2.8- Pipes Unix


2- Systèmes d’exploitation et programmation concourante
2.8- Pipes Unix

Communication inter processus

Processus dans SE modernes sont indépendants, sans mémoire partagée


� Comme échanger de l’information entre processus ?
Communication inter processus dans Unix
� Pipes
� FIFO
� Files de messages
� Sémaphores
� Mémoire partagée
� Sockets
Plusieurs concepts définis alors que threads n’existaient pas
� Pour le cours, on se limite aux pipes et sockets
� Mémoire partagée et sémaphores se font naturellement avec des threads
2- Systèmes d’exploitation et programmation concourante
2.8- Pipes Unix

Pipes

Pipes (en français, canaux de communication) : forme plus ancienne de communi-


cation inter processus dans Unix
� Canal unidirectionnel entre deux processus
� En général utilisé seulement entre processus ayant un ancêtre commun
� Créé par un parent avant de faire un fork, hérité par l’enfant
Création d’un pipe

� Deux descripteurs de fichiers retournés par la fonction


� fd[0] ouvert en lecture et fd[1] ouvert en écriture
� Sortie de fd[1] est entrée de fd[0]
� Après fork, processus doivent fermer descripteurs de fichier non utilisés
� Canal du parent vers l’enfant, parent ferme fd[0], enfant ferme fd[1]
2- Systèmes d’exploitation et programmation concourante
2.8- Pipes Unix

Lecture / écriture dans un pipe

Information transmise par les fonctions read et write, comme pour un fichier régulier

� Un pipe se comporte comme un fichier


� Lorsqu’on fait un read sur pipe dont bout d’écriture (fd[1]) est fermé, retourne 0
(EOF)
� Lorsqu’on fait un write sur pipe dont bout de lecture (fd[0]) est fermé, signal SIGPIPE
est généré
� Si on gère le signal SIGPIPE, write retourne -1
Pour un canal bidirectionnel, il faut créer deux pipes, un pour chaque direction
2- Systèmes d’exploitation et programmation concourante
2.8- Pipes Unix

Tiré de W. Richard Stevens et Stephen A. Rago. Advanced programming in the UNIX environment. Addison-Wesley, 2013.
2- Systèmes d’exploitation et programmation concourante
2.8- Pipes Unix

Fonctions popen et pclose

Opération commune : lancer un processus pour lire sa sortie ou fournir son entrée

� popen fait fork et exec pour exécuter programme cmdstring et retourne pointeur de
fichier aux E/S standards
� Si argument type est "r", pointeur de fichier retourné est sortie standard de cmdstring
� Si valeur à argument type est "w", pointeur de fichier retourné est entrée standard de
cmdstring
� pclose ferme E/S standard, attend la commande pour terminer et retourne le statut
de terminaison
� Commande cmdstring exécutée dans un shell Bourne, comme
sh -c cmdstring
2- Systèmes d’exploitation et programmation concourante
2.9- Sockets Unix

2.9- Sockets Unix


2- Systèmes d’exploitation et programmation concourante
2.9- Sockets Unix

Sockets Unix

Socket Unix : interface de communication bidirectionnelle


� Communication bidirectionnelle et entre processus pas nécessairement relatés
� Interface similaire pour communication TCP/IP
Fonction pour pipes bidirectionnels

� Interface général, mais portable seulement pour sockets de domaine Unix (domain=
AF_UNIX) int fd[2] ;
socketpair(AF_UNIX, SOCK_STREAM, 0, fd) ;
� Type : flot (SOCK_STREAM) ou datagram (SOCK_DGRAM)
� Datagram préserve frontières entre messages, alors que ot ne le fait pas
� Taille limite imposée sur messages avec datagram
2- Systèmes d’exploitation et programmation concourante
2.9- Sockets Unix

Sockets Unix

Socket Unix : interface de communication bidirectionnelle


� Communication bidirectionnelle et entre processus pas nécessairement relatés
� Interface similaire pour communication TCP/IP
Fonction pour pipes bidirectionnels

� Interface général, mais portable seulement pour sockets de domaine Unix (domain=
AF_UNIX) int fd[2] ;
socketpair(AF_UNIX, SOCK_STREAM, 0, fd) ;
� Type : flot (SOCK_STREAM) ou datagram (SOCK_DGRAM)
� Datagram préserve frontières entre messages, alors que ot ne le fait pas
� Taille limite imposée sur messages avec datagram
2- Systèmes d’exploitation et programmation concourante
2.9- Sockets Unix

Sockets Unix

Sockets créés par socketpair sont connectés, mais anonymes


� Connections entre processus non relatés impossible
Interface général des sockets (IP et Unix)

� Domaine : AF_INET (IPv4), AF_INET6 (IPv6), AF_UNIX (Unix),


AF_UNSPEC (non spécifié)
� Type : SOCK_DGRAM (datagram), SOCK_RAW (protocole IP brut),
SOCK_SEQPACKET (séquence taille fixe), SOCK_STREAM (flot séquentiel)
� Protocole : selon le domain utilisé
Créer un socket Unix (datagram)
int fd = socket(AF_UNIX, SOCK_DGRAM, 0) ;
2- Systèmes d’exploitation et programmation concourante
2.9- Sockets Unix

Sockets nommés

Assigner socket Unix a une adresse (nom)

Nom du chemin est pour publier le socket


� SE créé fichier du même nom
� Fichier sun_path ne peut être ouvert, seulement pour publier le nom
Associer socket à l’adresse avec fonction bind
2- Systèmes d’exploitation et programmation concourante
2.9- Sockets Unix

Protocole orienté connections

Tiré de W. Richard Stevens et Stephen A. Rago. Advanced programming in the UNIX environment. Addison-Wesley, 2013.
2- Systèmes d’exploitation et programmation concourante
2.9- Sockets Unix

Sockets nommés

Protocole sans connexion (SOCK_DGRAM)


� Une fois bind sur adresse effectué, serveur prêt à recevoir messages
� Client se connecte par fonction connect
Protocole orienté connexion (SOCK_STREAM)
� Côté serveur
� bind : se lie à une adresse
� listen : se prépare à recevoir demandes de connections
� accept : créé un nouveau socket pour accepter nouvelle connexion et la communiquer
avec client
� Côté client : se connecter à un serveur avec fonction connect
Échange des données
� send (aussi write) : envoyer/écrire un message
� recv (aussi read) : recevoir/lire un message
2- Systèmes d’exploitation et programmation concourante
2.9- Sockets Unix

Serveur avec sockets Unix

Se préparer à recevoir connections

� sockfd : socket publié, en attente de demandes de connections


� backlog : nombre maximum de connections en attente permises
Créer nouveau socket anonyme pour nouvelles connections

� sockfd : socket publié (bind et listen) avec connections en attente


� addr : identité du client
� Retourne descripteur de fichier du socket anonyme de la nouvelle connexion
2- Systèmes d’exploitation et programmation concourante
2.9- Sockets Unix

Transmettre des données avec sockets Unix

Ouvrir une connexion client

Transmettre des données

Alternativement, on peut utiliser version de lecture/écriture de fichiers


2- Systèmes d’exploitation et programmation concourante
2.10- Signaux

2.10- Signaux
2- Systèmes d’exploitation et programmation concourante
2.10- Signaux

Signaux

Signaux : interruptions logicielles pour gérer évènements asynchrones


� Concept important de Unix pour communication inter processus
Réception d’un signal par un processus
� SE interrompt exécution normale du processus et traite le signal
� Actions de traitement possible
� Ignorer le signal
� Intercepter le signal et exécuter routine de gestion du programme
� Exécuter routine par défaut
� Pour quelques signaux, routine de gestion non modifiables
Chaque signal a un nom et un entier associé
� SIGINT (2) : interrompre le programme (ex. via CTRL-C dans le terminal)
� SIGSEGV (11) : accès mémoire à adresse invalide
� SIGCHLD (20) : état du processus enfant a changé
2- Systèmes d’exploitation et programmation concourante
2.10- Signaux

Transmettre signal

Transmettre signal dans terminal : commande kill


$ kill -s TERM pid
Appel de fonction pour transmettre signal

� Si pid=0, signal envoyé aux processus du groupe courant (processus enfants)


� Si pid=-1, signal envoyé à tous les processus de l’utilisateur, excepté le processus
courant
� Autrement, signal envoyé au processus pid
� Si sig=0, vérifie si processus courant peut signaler selon pid
2- Systèmes d’exploitation et programmation concourante
2.10- Signaux

Intercepter signal

Modifier routine de gestion de signal

� signum : signal dont routine de gestion est modifiée


� act : routine de gestion à utiliser
� sigaction.sa_handler : pointeur de fonction à utiliser
� oldact : si différent de NULL, retourne ancienne routine de gestion
2- Systèmes d’exploitation et programmation concourante
2.10- Signaux

Exemple : modifier routine de gestion de signal


2- Systèmes d’exploitation et programmation concourante
2.10- Signaux

Autres éléments sur signaux

Ignorer signal
act.sa_handler = SIG_IGN ;
Utiliser signal par défaut
act.sa_handler = SIG_DFL ;
Fonctions réentrantes
� Fonction de gestion peut être appelée à tout moment
� Seules fonctions réentrantes doivent être appelées
Signaux non gérables
� outines pour signaux SIGKILL et SIGSTOP non modifiables, car gérés par SE
2- Systèmes d’exploitation et programmation concourante
2.10- Signaux

Quelques signaux courants

Signal Numéro Action Discription


SIGINT 2 Term Interruption de clavier (CTRL-C)
SIGQUIT 3 Core Quitter du clavier (CTRL-n)
SIGILL 4 Core Instruction illégale
SIGABRT 6 Core Signal de abort(3)
SIGFPE 8 Core Exception à virgule flottante
SIGKILL 9 Term Tuer processus (non modifiable)
SIGSEGV 11 Core Référence mémoire invalide
SIGPIPE 13 Term Écrire sur un pipe sans lecteur
SIGALRM 14 Term Signal de alarm(2)
SIGTERM 15 Term Signal de terminaison
SIGUSR1 30,10,16 Term Signal défini par l’utilisateur 1
SIGUSR2 31,12,17 Term Signal défini par l’utilisateur 2
SIGCHLD 20,17,18 Ign Processus enfant arrêté ou terminé
SIGCONT 19,18,25 Cont Continue si arrêté
SIGSTOP 17,19,23 Stop Arrêter processus (non modifiable)
2- Systèmes d’exploitation et programmation concourante
2.10- Signaux

Fin de Séance
Vos Questions ?

Vous aimerez peut-être aussi