les états d'un processus (ou d'une tache) Remarques : Une tache en état running est une tache qui est entrain d'être exécutée par le CPU, Une task ready est une task prête à être exécutée par le CPU. Dans les autres états la tache est bloquée (momentanément ou définitivement). La décision de "bloquer" l'exécution d'une tache dépend des paramètres suivants: La disponibilité des ressources. Exemple: si la tache essaye d'accéder à un fichier (ressource) alors en attendant que les données soient transférées depuis le disque, la tache est mise en attente (waiting) Le timing (si la tache décide d'elle-même de s'endormir en appelant la fonction sleep() ou une autre fonction d'attente alors elle est mise en attente) La politique de scheduling. En effet le scheduler peut décider de bloquer une tache afin de donner le CPU à une autre tache (dans ce cas l'état de la tache devient "ready") ESME - Programmation Système sous Linux - A.TALBI
l'ordonnancement (scheduling) Remarques : Le scheduling (ordonnancement) consiste à élire une tache qui se trouve dans l'état ready afin qu'elle soit exécutée par le CPU. Le scheduler fait partie du noyau Question: Comment se fait l'opération d'élection d'une tache ? ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Scheduling FIFO Remarques : Dans le scheduling FIFO (First-In First-Out ou First-Come First-Served) le scheduler choisi la première tache de la liste des taches "ready" et l'exécute jusqu'à ce qu'elle bloque (en demandant une ressource ou en faisant un sleep…) ESME - Programmation Système sous Linux - A.TALBI
Scheduling Round Robin Remarques : Dans le scheduling Round Robin le scheduler choisi une tache de la même manière que dans le cas FIFO par contre elle est exécuté pour au max un "quantum" de temps puis elle est réinsérée à la fin de la FIFO ESME - Programmation Système sous Linux - A.TALBI
Scheduling: priorités des taches Note : En fait le noyau (scheduler) gère plusieurs listes suivant les priorités des taches. La tache de plus haute priorité est toujours exécutée avant celle de moindre priorité. Remarque: La gestion des taches suivant leurs priorités est cruciale dans les systèmes embarqués temps réel. Dans l'exemple du robot mobile vu dans le cours sur les taches, la tache d'acquisition/traitement d'images doit avoir une priorité moins élevée que la tache de control commande ceci afin que l'asservissement des actionneurs ne soit pas bloqué (période de 5ms) pendant que l'on fait le traitement d'image (période de 33ms) ESME - Programmation Système sous Linux - A.TALBI
Problème de synchronisation Soit un processus server d'une banque qui crée deux taches pour gérer les débits et crédits sur un compte main() { … pthread_create (tache1…); // création tache de gestion des crédits pthread_create (tache2…); // création tache de gestion des débits } Problème: Les deux taches s'exécute en parallèle sur le même cœur. Supposant qu'à un instant donné le compte avait une somme de 2000, la tache1 crédite (+1000) le compte au même moment la tache2 débite (-500) le même compte. Le pseudo-assembleur de ces deux opérations est représenté en bleu pour la tache1 et en jaune pour la tache2 Si la tache 2 préempte la tache1 au moment ou ces deux taches manipulent la même variable (compte) alors le compte sera erroné à la fin de ces deux opérations (normalement à la fin compte doit être égale à 2500 = 2000 + 1000 – 500, or il est égale à 3000). Dans cette situation la variable compte est une variable critique on doit la protéger contre des manipulations concurrentes (manipulation simultanées par plusieurs taches). Remarque: La commutation de contexte (basculement d'une tache à l'autre) provoque la sauvegarde des registres (contexte) de la taches courante. Cette commutation est réalisé par le kernel (scheduler). Elle peut survenir à n'importe quel moment sur un évènement asynchrone (ex: interruption), ou lorsque la tache courante effectue une opération bloquante (ex: accès disque) Question: Comment peut on protéger la variable compte ? Réponse: A l'aide d'une variable mutex (verrou) ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de synchronisation: Les mutexes ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de synchronisation: blocage conditionnel Solution 1: Problème: Dans le code ici-bas, un processus créer deux taches qui s'exécutent en parallèle. La première est une taches d'acquisition d'images, la deuxième est une tache de traitement d'images. Si la tache de traitement est plus rapide que la tache d'acquisition alors il faut insérer un mécanisme pour que la tache de traitement se bloque lorsqu'il n y a plus d'images à traiter Utiliser une variable conditionnelle ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de synchronisation: Sémaphores Solution 2: Problème: Dans le code ici-bas, un processus créer deux taches qui s'exécutent en parallèle. La première est une taches d'acquisition d'images, la deuxième est une tache de traitement d'images. Si la tache de traitement est plus rapide que la tache d'acquisition alors il faut insérer un mécanisme pour que la tache de traitement se bloque lorsqu'il n y a plus d'images à traiter Utiliser une variable sémaphore ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Mécanismes de synchronisation: mutexes, variables conditionnelles et sémaphores Remarques: La fonction pthread_cond_wait relâche implicitement le mutex qu'on a verrouillé avant (sinon on tombe sur une situation d'inter-blocage) Une variable conditionnelle s'utilise conjointement avec un mutex. Ceci est obligatoire pour protéger les variables partagées par les taches (nb_images ainsi que d'autres variables internes…). Dans l'exemple précédent, si on avait plusieurs taches de traitements qui s'exécutent en parallèle, alors la taches acquisition doit utiliser la fonction pthread_cond_broadcast pour réveiller toutes les taches d'acquisitions. Il faut toujours re-tester la condition de blocage une fois réveillé (while(nb_image <=0)). Ceci est obligatoire dans le cas de plusieurs taches de traitement On doit toujours tester les valeurs de retour des fonctions qui manipulent les mutexes et les variables conditionnelles (par soucis de concision ces tests ont été omis dans les exemples précédents) Contrairement aux variables conditionnelles, les sémaphores peuvent être utilisés non seulement entre taches (threads) du même processus mais entre différent processus (on peut imaginer un processus pour l'acquisition et un processus pour le traitement d'images). Dans ce cas l'initialisation se fait à l'aide de la fonction sem_open . Dans le cas multi-taches, le choix d'utilisation d'un semaphore ou d'une variable conditionnelle est un choix de développeur, les deux solutions peuvent êtres envisagées. En général on utilise les sémaphore dans les problèmes où un compteur est partagé entre producteur(s) et consommateur(s) (comme c'est le cas dans l'exemple plus haut) et on utilise les variables conditionnelles pour signaler des évènements asynchrones sur lesquels il peut y avoir une, plusieurs ou aucune tache en attente de cette évènement (ex: une tache accède au disque => elle configure puis lance le DMA et se bloque à l'aide de cond_wait. A la fin de l'accès disque, le handler d'interruption FIN_DMA signal à la taches que ces données sont dispo) ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: Les message queues Process/tache sender Process/tache receiver Définition: Une message queue est un mécanisme de communication interprocessus (inter-taches). Il peut être vu comme une boite aux lettres. Elle permet à un processus (tache) d'émettre (déposer) un message (un buffer contenant des données) et à un autre processus (tache) de récupérer les messages contenus dans la boite aux lettres kernel ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: Les message queues inter-processus ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: Les mémoires partagées (shared memory) Remarques: Un processus (P1) peut créer et mapper une mémoire partagée d'une certaine taille. C'est une zone mémoire qui est allouée dans la section Heap de ce processus. Elle représente une projection d'une zone mémoire physique vers une zone mémoire virtuelle. Un autre processus (P2) peut mapper la même zone mémoire (même adresse mémoire physique mais adresses virtuelles différentes) du coup les deux processus peuvent s'échanger des data via cette zone mémoire partagée. Process 1 V-memory Process 2 V-memory PHY memory Stack Stack Heap Heap Virt_addr1 Virt_addr2 Phy_addr SHARED MEMORY Data Data Code Code ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: Les mémoires partagées exemple ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: les tubes (pipes) processus P1 processus P2 Tubes anonymes: Un tube anonyme permet de créer deux flux de données unidirectionnels (fifo) entre un processus et un autre. Un flux est utilisé pour envoyer des données du processus P1 vers le processus P2 et l'autre pour les sens inverse. Un flux peut être vu comme un fichier qui sauvegarde les données envoyées jusqu'au moment où elle sont lues par le processus destinataire, puis ces données sont supprimées de ce fichier. FIFO veut dire que les premières données arrivées sont les premières lues fifo0 fifo1 tube anonyme Tubes nommés: Un tube nommé permet de créer un seul flux de données unidirectionnel (fifo) entre un processus et un autre. kernel ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: les tubes (pipes) anonymes exemple ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: Le réseau TCP(UDP)/IP et ethernet ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: TCP/IP, ports et sockets Paramètres d'une socket: Adresses IP source et destination Protocole: SOCK_STREAM (TCP: connexion fiable mais lente à cause des retransmission en cas d'erreurs, ex: pages web, ssh, ftp), SOCK_DGRAM (UDP: connexion non fiable mais rapide, ex streaming audio/vidéo) Ports source et destination: utilisés pour identifier l'application, ex: port 80 pour HTTP (adresses site web), port 20 pour FTP, port 22 pour ssh… ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: routage Remarques: Les routers sont responsable de l'acheminement des packets réseau depuis une source vers une destination. Chaque host/routeur contient une table de routage. Cette table de routage indique quelle sortie doit prendre un packet à transmettre selon l'adresse IP destination qu'il contient. A chaque fois qu'un nœud veut transmettre un packet IP il consulte sa table de routage pour déterminer l'adresse IP du nœud cible: Si l'adresse IP destination du packet appartient au réseau local alors il consulte sa table ARP pour récupérer l'adresse MAC (ethernet) du nœud (cible) local Si l'adresse IP appartient à un autre réseau inconnu (distant) alors ce nœud consulte sa table ARP pour récupérer l'adresse MAC du noeud (cible) passerelle (router). Le nœud source construit sa frame ethernet en insérant son adresse MAC (adresse source) et l'adresse MAC du nœud cible (adresse destination) puis transmet le packet sur le réseau (via sa carte réseau). Chaque nœud écoute le trafique réseau (les packets échangés sur le réseau). Si un packet transmis contient comme adresse MAC destination son adresse alors il le traite (le remonte au couches supérieures) sinon il l'ignore. ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Mécanismes de communication: Etablissement d'une connexion réseau (socket) ESME - Programmation Système sous Linux - A.TALBI
Mécanismes de communication: exemple socket (net) client/server ESME - Programmation Système sous Linux - A.TALBI
Communication et synchronisation inter-processus/taches: TP Exercice 1: Dans un fichier test_mutex.c, écrire un programme test_mutex qui crée 5 threads, puis en boucle demande à l'utilisateur de saisir un message (utilisez getc pour lire le message de l'utilisateur). Une fois le message saisi, la fonction main doit réveiller tous les threads. Chacun des thread, une fois réveillé, doit afficher à l'écran le message fourni par l'utilisateur. Ce message doit être précédé par le numéro du thread (exemple: "Thread N°2: message utilisateur"). L'affichage du message se fera lettre par lettre avec une temporisation de 10ms entre l'affichage d'une lettre et l'affichage de la lettre suivante (utilisez la fonction usleep()). Pendant l'affichage, un thread ne doit pas être dérangés par les autres threads (Un thread doit afficher le message en entier sans être interrompu par un autre thread). Indication: Utilisez un mutex et une variable conditionnelle pour réaliser ce comportement Exercice 2: Dans un fichier acquire.c, écrire une fonction void get_image(int *tab) qui rempli un tableau (tab) de dix entiers d'une manière aléatoire, affiche ce tableau, puis s'endorme (fonction sleep) pour une période aléatoire (entre 0 et 5 seconde avec un pas de 1 seconde) avant de retourner. Un tableau peux être vu comme une image et les entiers représentent le niveau de gris des pixels de l'image. Ces entiers doivent être compris entre 0 et 50. Utilisez la fonction rand et l'opérateur modulo "%" pour générer des entiers aléatoire compris dans un certain intervalle Dans un fichier process.c, écrire une fonction int process_image(int *tab) qui trie par ordre croissant un tableau de dix d'entiers (image) qu'on lui passe en paramètre, puis s'endorme pour une période aléatoire (entre 0 et 10 seconde avec un pas de 1 seconde), puis affiche ce tableau. Cette fonction retourne 1 s'il y a une détection d'obstacle dans l'image (un obstacle correspond à 2 nombres identiques dans le tableau) sinon 0. Dans un fichier client.c écrire un programme qui contient une variable globale "images" représentant dix tableaux chacun d'eux contient dix entiers (matrice de 10x10 entiers). Le programme crée 3 taches: Une tache acquisition qui boucle et remplie le tableau d'images (matrice) à l'aide de la fonction get_image(). Si la matrice est remplie, la tache se bloque. Une tache traitement qui prend une à une les entrées (tableau de 10 entiers) de la matrice des images puis trie ce tableau à l'aide de la fonction process_image(). Si un obstacle est détecté alors elle doit réveiller la tache suivante. S’il n y a plus d’images à traiter la tache s’endorme. Une tache obstacle qui se réveille à chaque fois qu'un obstacle est détecté. Quand elle se réveille, cette tache doit envoyer l'image (tableau) à un autre processus server au travers d'une socket réseau puis s'endorme pour une période de 5 seconde Dans un fichier server.c, créez un programme qui reçoit en boucle les images envoyés par le programme client et les affichent à l'écran Indication: utilisez des sémaphores pour remplir/vider le tableau d'images (matrice) et un mutex/variable conditionnelle pour le réveil sur obstacle Ne pas oublier le makefile ESME - Programmation Système sous Linux - A.TALBI