• Aucun résultat trouvé

COURS Multitâche (processus et thread)

N/A
N/A
Protected

Academic year: 2022

Partager "COURS Multitâche (processus et thread)"

Copied!
12
0
0

Texte intégral

(1)

Extrait du référentiel : BTS Systèmes Numériques option A (Informatique et Réseaux) Niveau(x) S6. Systèmes d’exploitation

S6.2. S.E. Multitâches professionnelles

Processus lourds / légers, diagramme des états d’une tâche

Ordonnancement des processus

3

Objectifs du cours :

Ce cours traitera essentiellement les points suivants : - Programme et processus

- Espace d’adressage des processus - S.E. (préemptif et coopératif) - Le multitâche

- État d’un processus (PID, processus lourds et légers) - Interfaces de programmation (WIN32, System calls) - Les threads

- Création dynamique de processus - Création de tâches en C sous POSIX

La base des ordinateurs modernes, c'est le parallélisme. Il est désormais inconcevable qu'un ordinateur ne puisse exécuter plusieurs programmes en même temps.

Du point de vue matériel, le processeur passe d'un programme à un autre en quelques

millisecondes, ce qui va donner à l'utilisateur une impression de simultanéité. C'est le pseudo- parallélisme, à différencier avec le véritable parallélisme des systèmes multiprocesseurs.

Les processus sont la base du pseudo-parallélisme.

PROGRAMME ET PROCESSUS

Il est important de différencier la notion de programme et la notion de processus.

(2)

La définition d'un processus : c'est un programme en cours d'exécution auquel est associé un environnement processeur et un environnement mémoire.

En effet, au cours de son exécution, les instructions d'un programme modifient les valeurs des registres ainsi que le contenu de la pile.

Un programme est une suite d'instructions (notion statique), tandis qu'un processus est l'image du contenu des registres et de la mémoire centrale (notion dynamique).

ESPACE D’ADRESSAGE DES PROCESSUS

Chaque processus possède un espace d'adressage, c'est-à-dire un ensemble d'adresse mémoires dans lesquelles il peut lire et écrire.

Cet espace est divisé en trois parties :

le segment de texte (le code du programme) ; le segment de données (les variables) ; la pile.

Pile

Segment de données Segment de texte

Espace d’adressage des processus

La pile empiète sur cet espace de manière automatique et l'extension du segment de données commence lors d'une allocation dynamique.

Le segment de texte (le code), ne bouge pas.

Lorsqu'un processus est lancé, le système doit gérer la mémoire et l'allocation du processeur lui étant accordée. Il fait appel à l'ordonnanceur (scheduler en anglais).

S.E. PRÉEMPTIF ET COOPÉRATIF

Un système d'exploitation est préemptif lorsqu'il peut arrêter à tout moment n'importe quelle application pour passer à la suivante (exemple : Windows XP, Windows 7-8-10 et GNU/Linux sont des systèmes préemptifs).

Un S.E. peut être aussi coopératif quand il permet à plusieurs applications de fonctionner et d'occuper la mémoire, et leur laissant le soin de gérer cette occupation (exemple : Windows 95, 98 et Millénium sont des systèmes coopératifs).

LE MULTITÂCHE

Un S.E. est multitâche (multitasking) s'il permet d'exécuter, de façon apparemment simultanée, 0x00000000

0xFFFFFFFF

Adresse

(3)

plusieurs programmes informatiques (processus). La simultanéité apparente est le résultat de l'alternance rapide d'exécution des processus présents en mémoire. Le passage de l'exécution d'un processus à un autre est appelé commutation de contexte. Ces commutations peuvent être initiées par les programmes eux-mêmes (multitâche coopératif) ou par le système d'exploitation (multitâche préemptif).

En résumé :

multitâche préemptif : le système d'exploitation garde le contrôle et se réserve le droit de fermer l'application.

multitâche coopératif : le système d'exploitation laisse les applications gérer.

Le multitâche n'est pas dépendant du nombre de processeurs présents physiquement dans l'ordinateur : un système multiprocesseur n'est pas nécessaire pour exécuter un système d'exploitation multitâche. Le multitâche coopératif n'est plus utilisé. Unix et ses dérivés, Windows10 et MAC OS X sont des systèmes basés sur le multitâche préemptif.

Le multitâche coopératif est plus « dangereux » pour le système, car il y a risque de blocage si une application prend « le dessus ».

Le multitâche permet de paralléliser les traitements par l'exécution simultanée de programmes informatiques.

Exemples de besoins :

Permettre à plusieurs utilisateurs de travailler sur la même machine.

Utiliser un traitement de texte tout en naviguant sur le Web.

Transférer plusieurs fichiers en même temps.

Améliorer la conception : écrire plusieurs programmes simples, plutôt qu'un seul programme capable de tout faire, puis de les faire coopérer pour effectuer les tâches nécessaires.

La préemption est la capacité d'un système d'exploitation multitâche à suspendre un processus au profit d'un autre. Le multitâche préemptif est assuré par l'ordonnanceur (scheduler).

L'ordonnanceur distribue le temps du processeur entre les différents processus. Il peut aussi interrompre à tout moment un processus en cours d'exécution pour permettre à un autre de s'exécuter.

Une quantité de temps définie est attribuée par l'ordonnanceur à chaque processus : les processus ne sont donc pas autorisés à prendre un temps non-défini pour s'exécuter dans le processeur.

Dans un ordonnancement (statique à base de priorités) avec préemption, un processus peut être préempté (remplacé) par n'importe quel processus plus prioritaire qui serait devenu prêt.

tv

(4)

DÉFINITIONS

Multitâche : exécution en parallèle de plusieurs tâches (processus ou threads).

Commutation de contexte : passage de l'exécution d'un processus à un autre.

Multitâche préemptif : mode de fonctionnement d'un système d'exploitation multitâche permettant de partager de façon équilibrée le temps processeur entre différents processus.

Ordonnancement : mécanisme d'attribution du processeur aux processus (blocage, déblocage, élection, préemption).

Un processus est un programme en cours d'exécution par un système d'exploitation.

Un programme est une suite d'instructions permettant de réaliser un traitement. Il revêt un caractère statique.

Une image représente l'ensemble des objets (code, données, ...) et des informations (états, propriétaire, ...) qui peuvent donner lieu à une exécution dans l'ordinateur.

Un processus est donc l'exécution d'une image. Le processus est l'aspect dynamique d'une image.

Un fil d'exécution (thread) est l'exécution séquentielle d'une suite d'instructions.

LE SYSTÈME D’EXPLOITATION

Rappel : un système d'exploitation multitâche est capable d'exécuter plusieurs processus de façon quasi-simultanée.

Le système d'exploitation est chargé d'allouer les ressources (mémoires, temps processeur, entrées/sorties) nécessaires aux processus et d'assurer son fonctionnement isolé au sein du système.

Un des rôles du système d'exploitation est d'amener en mémoire centrale l'image mémoire d'un processus avant de l'élire et de lui allouer le processeur. Le système d'exploitation peut être amené à sortir de la mémoire les images d'autres processus et à les copier sur disque. Une telle gestion mémoire est mise en oeuvre par un algorithme de va et vient appelée aussi swapping.

Il peut aussi fournir une API (Application Programming Interface) pour permettre leur gestion et la communication inter-processus (IPC : Inter processus Communications).

(5)

ÉTAT D’UN PROCESSUS

Ces états existent dans la plupart des systèmes d'exploitation :

États que peut prendre un processus sous la forme d'un diagramme états-transitions où les flèches sont les transitions autorisées.

L'image d'un processus comporte du code machine exécutable, une zone mémoire pour les données allouées par le processus, une pile ou « stack » (pour les variables locales des fonctions et la gestion des appels et retour des fonctions) et un tas ou « heap » pour les allocations

dynamiques (new/delete, malloc/free).

Ce processus est une entité qui, de sa création à sa mort, est identifié par une valeur numérique : le PID (Process IDentifier).

(6)

Chaque processus a un utilisateur propriétaire, qui est utilisé par le système pour déterminer ses permissions d'accès aux ressources (fichiers, ports réseaux, ...).

Exemple du Gestionnaire de tâches sous Windows PROCESSUS LOURDS ET LÉGERS

Rappel : l'exécution d'un processus se fait dans son contexte. Quand il y a changement de processus courant, il y a une commutation contexte.

En raison de ce contexte, la plupart des systèmes offrent la distinction entre :

« Les processus lourds », qui sont complètement isolés les uns des autres car ils ont chacun leur contexte.

« Les processus légers » (threads), qui partagent un contexte commun sauf la pile (les threads possèdent leur propre pile d'appel).

(7)

Tous deux représentent l'exécution d'un ensemble d'instructions du langage machine d'un processeur (notion de fil).

Une tâche est une unité active d'une application. Il s'agit d'un ensemble d'instructions séquentielles correspondant souvent à une procédure ou à une fonction.

Une tâche est l'équivalent d'un processus léger dans le jargon des systèmes d'exploitation.

Contrairement à un processus lourd qui, lorsqu'il est créé, implique la réservation des ressources telles qu'un espace mémoire, une table de fichiers ouverts et une pile interne qui lui sont toutes dédiées, un processus léger, lorsqu'il est créé, partage le même espace mémoire et la même table des fichiers ouverts que son processus père (son créateur), mais dispose de sa propre pile.

L'avantage des processus légers sur les processus lourds est qu'ils sont très favorables à la programmation multitâche, car la création d'une tâche est moins coûteuse en termes de

ressources du fait qu'elles ne sont pas toutes dupliquées. De plus, l'utilisation des tâches simplifie la gestion des problématiques liées à l'exécution concurrente (communication, synchronisation entre tâches...).

Sous Posix, l'anglicisme utilisé pour dénommer un processus léger et donc une tâche est thread.

Dans ce cours, nous représenterons graphiquement une tâche par un parallélogramme avec un contour en trait interrompu. Un processus lourd sera représenté par un parallélogramme avec un contour en trait continu.

En résumé :

Processus : programme en cours d'exécution. C'est l'exécution d'une image composée de code machine et de données mémoire.

Contexte : image d'un processus en mémoire auquel s'ajoute son état (registres, ...).

(8)

Processus lourd : c'est un processus « normal ». L'exécution d'un processus est réalisée de manière isolée par rapport aux autres processus.

Processus léger : c'est un thread. Un processus lourd peut englober un ou plusieurs threads qui partagent alors le même contexte. C'est l'exécution d'une fonction (ou d'une méthode) au sein d'un processus et en parallèle avec les autres threads de ce processus. La commutation de thread est très rapide car elle ne nécessite pas de commutation de contexte.

INTERFACES DE PROGRAMMATION

Rappel : le noyau d'un système d'exploitation est vu comme un ensemble de fonctions qui forme l'API.

Chaque fonction ouvre l'accès à un service offert par le noyau.

Ces fonctions sont regroupées au sein de la bibliothèque des appels systèmes (system calls) pour UNIX/Linux ou WIN32 pour Windows.

POSIX (Portable Operating System Interface) est une norme relative à l'interface de programmation du système d'exploitation.

De nombreux systèmes d'exploitation sont conformes à cette norme, notamment les membres de la famille Unix.

L’API WIN32

L'API Windows est orientée « handle » et non fichier.

Un handle est un identifiant d'objet système.

(9)

L’API SYSTEM CALLS

L'API System Calls d'UNIX est orientée « fichier », dans ce système : TOUT est FICHIER ! Un descripteur de fichier est une clé abstraite (c'est un entier) pour accéder à un fichier, c'est-à- dire le plus souvent une ressource du système.

LES THREADS

Les systèmes d'exploitation mettent en oeuvre généralement les threads :

Le standard des processus légers POSIX est connu sous le nom de pthread. Le standard POSIX est largement mis en oeuvre sur les systèmes UNIX/Linux.

Microsoft fournit aussi une API pour les processus légers : WIN32 threads (Microsoft Win32 API threads).

En C++, il est conseillé d'utiliser un framework (Qt, Builder, commoncpp, boost, ...) qui fournira le support des threads sous forme de classes prêtes à l'emploi. La version C++11 intégrera le support de threads en standard.

Java propose l'interface Runnable et une classe abstraite Thread de base.

En résumé :

API : c'est une interface de programmation (Application Programming Interface) qui est un

ensemble de classes, de méthodes ou de fonctions qui offre des services pour d'autres logiciels.

WIN32 : c'est l'API de Windows qui permet la programmation système (création de processus ou de thread, communication inter-processus,...). Elle est orientée handle.

System calls : c'est l'API Unix/Linux composée d'appels systèmes pour la création de processus ou de thread, la communication inter-processus, ...). Elle est orientée fichier.

(10)

POSIX : c'est une norme d'API que l'on retrouve notamment sous Unix.

CRÉATION DYNAMIQUE DE PROCESSUS

Lors d'une opération « fork », le noyau Unix crée un nouveau processus qui est une copie conforme du processus père. Le code, les données et la pile sont copiés et tous les fichiers ouverts par le père sont ainsi hérités par le processus fils.

Si l'on désire exécuter du code à l'intérieur de ce nouveau processus, on utilisera un appel de type

« exec » : execl, execlp, execle,execv ou execvp.

La famille de fonctions « exec » remplace l'image mémoire du processus en cours par un nouveau processus.

Chaque processus est identifé par un numéro unique, le PID (Processus IDentification). Un processus est créé par un autre processus (notion père-fils).

Le PPID (Parent PID) d'un processus correspond au PID du processus qui l'a créé (son père).

Sous Windows pour connaitre la liste des processus et leur PID associé en autres et en mode

« CLI » utilisez la commande : « tasklist ».

Sous Linux, vous pouvez utiliser la commande « top » ou encore « ps ».

CRÉATION DE TÂCHES EN C SOUS POSIX

Un programme C utilisant la bibliothèque « pthread » doit disposer de l'entête suivant :

#include <pthread.h>

(11)

Sous Posix, la création d'une tâche est réalisée avec la fonction suivante :

int pthread_create(pthread_t *thread, pthread_attr_t *attr, void* (*start_routine)(void*), void *arg);

thread : pointeur de type « pthread_t » contenant l'identificateur de la tâche qui vient d'être créée ; attr : variable de type « pthread_attr_t ». Elle correspond à une sorte de conteneur qui va

permettre d'indiquer les différentes propriétés de la tâche qui doit être exécutée (son type d'ordonnancement, sa priorité, tâche joignable/détachable? ...). Nous développerons au fur et à mesure cette variable dans les prochains cours ;

start_routine : c'est la fonction C qui sera exécutée par la tâche qui est créée ;

arg : pointeur correspond aux variables passées en paramètre à la fonction « start_routine ». Il vaut « NULL » si aucun paramètre n'est passé à la fonction ;

Comme valeur de retour, la fonction « pthread_create » renvoie « 0 » si tout s'est bien passé et un nombre négatif sinon.

Lorsqu'un processus lourd crée une tâche sous Posix, s'il ne lui est pas explicitement indiqué d'attendre la fin d'exécution de cette tâche, alors à sa terminaison elle forcera l'arrêt de la tâche créée. Pour éviter cela, Posix propose la fonction « pthread_join ».

Le prototype de la fonction pthread_join est :

int pthread_join(pthread_t *thread, void** thread_return)

Son premier paramètre thread représente l'identificateur de la tâche sur laquelle le processus ou la tâche créatrice doit attendre. Son deuxième paramètre représente une variable dans laquelle est stockée une éventuelle valeur de retour produite par la tâche. Ce dernier vaut « NULL » si la tâche ne renvoie aucune valeur.

Exemple :

Considérons un programme où deux tâches doivent être créées par le processus exécutant la fonction main(void) et s'exécuter de manière concurrente. Chacune, lorsqu'elle devient active, doit s'identifier et effectuer un nombre quelconque d'itérations en l'écrivant à l'écran avant d'être préemptée.

Le code en C :

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <pthread.h>

void* fonc(void* arg) {

int i;

(12)

for (i = 0;i<7;i++) {

printf("Tache %d : %d\n", (int)arg, i);

usleep(1000000);

} }

int main(void) {

pthread_t tache1, tache2;

pthread_create(&tache1, NULL, fonc, (void*)1);

pthread_create(&tache2, NULL, fonc, (void*)2);

pthread_join(tache1, NULL);

pthread_join(tache2, NULL);

return 0;

}

Affichage en console après compilation et exécution :

Nous remarquons lors de l'exécution un ordre d'affichage arbitraire entre les tâches 1 et 2.

Cet ordre d'affichage peut varier d'une exécution à une autre.

Références

Documents relatifs

La préemption est la capacité d'un système d'exploitation multitâche à suspendre un processus au prot d'un autre.. Le multitâche préemptif est assuré par

– au standard POSIX pthread largement mis en oeuvre sur les systèmes UNIX/Linux – à l’API WIN32 threads fournie par Microsoft ©Windows pour les processus légers Les pages man

Sous Windows, il y a principalement trois options pour écrire des programmes multitâches : Utiliser la Win32 Application Programming Interface (API) en conjonction avec la C run-time

La grnnde ma~e des instituteurs usera bientôt des fichiers auto- correct.llis dont, nous sommes les ilùtiatcurs comme elle use du texte libre, cette grande et

Q15.1 : Décrivez l’algorithme d’allocation contiguë de mémoire avec partitions à taille variables pour placer des processus en mémoire. Décrivez, pour cet algorithme, comment le

Q15.1 : Décrivez , pour l’allocation contiguë de mémoire avec partitions à taille variables pour placer des processus en mémoire , comment le MMU fait la translation entre

on entre son login et mot de passe (attention mot de passe en MAJUSCULES) vert sur la tranche horaire à laquelle on veut réserver on ne peut pas supprimer la résa d’un autre :

La topologie initiale T des espaces E intervenant dans toute la suite sera toujours supposée nucléaire. §