16
Architecture des Systemes

Visualiser Thread Unix

Embed Size (px)

Citation preview

Page 1: Visualiser Thread Unix

Architecture des Systemes

Page 2: Visualiser Thread Unix

GESTION DES PROCESSUS

Applications comportant des processus concurrents• Motivations pour les processus concurrents :– améliorer l'utilisation de la machine (cas monoprocesseur)– multiprogrammation sur des serveurs– parallélisme entre le calcul et les E/S sur PC– lancer des sous-calculs dans une machine parallèle ou répartie– créer des processus réactifs pour répondre à des requêtes ou à des événements externes (systèmes temps réel, systèmes embarqués et/mobiles)• Construire des applications concurrentes, parallèles ouréparties

Définitions• Processus:– Instance d’un programme en exécution– et un ensemble de données– et un ensemble d’informations (BCP, bloc de contrôle de processus)• Deux types de processus– Système: propriété du super-utilisateur (démons)– Utilisateur: lancés par les utilisateurs• Code de retour d’un processus– =0: fin normale– ≠0: comportement anormal (en cours d’exécution ou à la terminaison)• Interruption d’un processus à la réception d’un signal– A partir d’un programme (SIGINT, SIGQUIT)– A partir d’un shell avec la commande: kill num_signal pid_processus• La table des descripteurs de fichiers:– Chaque processus peut posséder au maximum OPEN_MAX descripteurs de fichier– En plus des descripteurs qu’un processus hérite de son père, il peut en créer d’autres avec open, creat, dup, …• Enchaînement d’exécution des processus– Si les commandes associées sont séparées par « ; »– Exemple: commande1 ; commande2 ; … ; commanden– Communication par l’intermédiaire de tube. Les résultats d’unprocessus servent de données d’entrée à l’autre

Exemple: commande | commande | … | commande

Descripteur des processus Linux• Bloc de contrôle (utilisé par Linux) comporte :- État du processus- Priorité du processus- Signaux en attente et masqués- Pointeur sur le processus suivant dans la liste des processus prêts- Numéro du processus- Numéro du groupe contenant le processus- Pointeur sur le processus père- Numéro de la session contenant le processus

Page 3: Visualiser Thread Unix

- Identificateur de l’utilisateur réel et effectif- Politique d’ordonnancement utilisé- Temps processeur consommé en mode noyau et en mode utilisateur

Date de création du processus, etc.Visualisation de processus• La commande « ps » permet de visualiser les processus existant à son lancement sur la machine• Sans option, elle ne concerne que les processus associés au terminal depuis lequel elle est lancée

Ordonnancement de processusL’ordonnanceur (scheduller) d’Unix:– Associe une priorité dynamique à chaque processus, recalculée périodiquement– Utilise la stratégie du tourniquet (round robin) pour les processus de même prioritéLa fonction de calcul de la priorité courante utilise les paramètres suivants:– Une priorité de base: correspondant au niveau d’interruption du processus– Le temps CPU consommé récemment– Une priorité ajustable par le processus lui-même grâce à:

#include <unistd.h>int nice(int incr);

qui permet d’augmenter la valeur de incr [0-39] et donc de diminuer sa prioritéLe super-utilisateur peut augmenter la priorité en donnant une valeur <0 à incr.

État d’un processus

Page 4: Visualiser Thread Unix

Création de processus• La primitive fork() permet de créer un nouveau processus (fils) par duplication• Afin de distinguer le père du fils, fork() retourne: -1 : en cas d’erreur de création 0 : indique le fils > 0 : le pid du processus fils au processus père• Les primitives getpid() et getppid() permettent d’identifier un processus et son père.

#include <unistd.h>pid_t fork(void)// crée un nouveau processuspid_t getpid(void) // donne le pid du processuspid_t getppid(void) // donne le pid du père du processus

L’héritage du fils• Le processus fils hérite de beaucoup d’attributs du père mais n’hérite pas:De l’identification de son pèreDes temps d’exécution qui sont initialisés à 0Des signaux pendants (arrivées, en attente d’être traités) du pèreDe la priorité du père; la sienne est initialisée à une valeur standardDes verrous sur les fichiers détenus par le père• Le fils travaille sur les données du père s’il accède seulement en lecture. S’il accède en écriture à une donnée, celle-ci est alors recopiée dans son espace local.

Page 5: Visualiser Thread Unix

Primitives wait et waitpid#include <sys/types.h> #include <sys/wait.h>pid_t wait(int *etat);pid_t waitpid(pid_t pid, int *etat, int options);

• Ces deux primitives permettent l’élimination des processus zombis et la synchronisation d’un processus sur la terminaison de ses descendants avec récupération des informations relatives à cette terminaison.• Elles provoquent la suspension du processus appelant jusqu’à ce que l’un de ses processus fils se termine• La primitive waitpid permet de sélectionner un processus particulier parmi les processus fils (pid)

Page 6: Visualiser Thread Unix

Communication avec l’environnement• int main(int argc, char *argv[], char *argp[])• Le lancement d’un programme C provoque l’appel de sa fonction principale main() qui a 3 paramètres optionnels:– argc: nombre de paramètres sur la lignes de commande (y compris le nom de l’exécutable lui-même)– argv: tableau de chaînes contenant les paramètres de la ligne de commande– envp: tableau de chaînes contenant les variables d’environnement au moment de l’appel, sous la forme variable=valeur

Primitives de recouvrement• Objectif: charger en mémoire à la place du processus fils un autre programme provenant d’un fichier binaire• Il existe 6 primitives exec. Les arguments sont transmis:– sous forme d’un tableau (ou vecteur, d’où le v) contenant les paramètres:• execv, execvp, execve– Sous forme d’une liste (d’où le l): arg0, arg1, …, argN, NULL:• execl, execlp, execle– La lettre finale p ou e est mise pour path ou environnement• La fonction system("commande…") permet aussi de lancer une commande à partir d’un programme mais son inconvénient est qu’elle lance un nouveau Shell pour interpréter la commande.

Tubes & Signaux

Introduction aux tubes

Tube : mécanisme de communication unidirectionnel.

Possède deux extrémités, une pour y lire et l’autre pour écrire

La lecture dans un tube est destructrice: l’info lue est supprimée du tube

Les tubes permettent la communication d’un flot continu de caractères (mode stream)

Un tube a une capacité finie

La gestion des tubes se fait en mode FIFO

Les tubes ordinaires

Page 7: Visualiser Thread Unix

• A un tube est associé un nœud du système de gestion de fichiers (son compteur de liens est = 0 car aucun répertoire ne le référence).

• Le tube sera supprimé et le noeud correspondant libéré lorsque plus aucun processus ne l'utilise.

• Un tube ordinaire n’a pas de nom.

• L'existence d'un tube correspond à la possession d'un descripteur acquis de deux manières:

-un appel à la primitive de création de tube pipe;

-par héritage: un processus fils hérite de son père des descripteurs de tubes, entre autres.

#include<unistd.h>

int pipe(int p[2]);

crée un tube, c-à-d:

- un noeud sur le disque des tubes,

- deux entrées dans la table des fichiers ouverts (1 en lecture, 1 en écriture), c-à-d deux descripteurs dans la table des processus appelant. pipe retourne dans p respectivement les descripteurs de lecture et d'écriture:

p[0] est le descripteur en lecture / p[1] celui en écriture.

La lecture se fait grâce à la primitive read EXEMPLE :

nb_lu= read(p[0], buf, nbr_car); correspond à la lecture d'au plus nbr_car caractères qui seront rangés dans une zone pointée par buf.

Fonctionnement d’un read

Si tube non vide et contient nbr caractères alors la primitive extrait du tube nb_lu=min (nbr, nbr_car) caractères et les place à l'adresse buf;

Sinon

Si nombre d'écrivains = 0, la fin de fichier est atteinte alors, aucun caractère n'est lu et la primitive renvoie la valeur nb_lu=0;

Fsi

Si nombre d'écrivains != 0 alors

Si lecture bloquante alors le processus est bloqué jusqu'à tube non vide;

Fsi;

Si lecture non bloquante alors le retour est immédiat et la valeur de retour est -1 et errno=EAGAIN

Fsi;

Fsi

Fsi

L’écriture dans un tube ordinaire

Page 8: Visualiser Thread Unix

Elle se fait à l'aide de la primitive write.

Exemple:

nb_ecrit= write(p[1], buf, n); demande l'écriture dans le tube de descripteur p[1] de n caractères accessibles à l'adresse buf.

Fonctionnement d’un write

Si nombre de lecteurs dans le tube = 0 alors le signal SIGPIPE est envoyé au processus, ayant pour handler de terminer ce processus;

Sinon

Si écriture bloquante alors le retour de la primitive (avec la valeur n) n'a lieu que lorsque les n caractères ont été écrits (le processus passe à l'état bloqué dans l'attente que le tube se vide);

sinon

si n > PIPE_BUF alors le retour est un nombre < n éventuellement -1

fsi

si n ≤ PIPE_BUF et nbre emplacements libres dans le tube >= n alors une écriture atomique est réalisé;

fsi

si n ≤ PIPE_BUF et nbre emplacements libres dans le tube < n alors le retour est immédiat sans écriture avec la valeur de retour 0 ou -1

fsi

fsi

fsi

Les signaux

Introduction

• Signal

– Interruption: événement extérieur au processus

• Frappe au clavier

• Signal déclenché par programme: primitive kill, …

– Déroutement: événement intérieur au processus généré par le hard

• FPE (floating point error)

• Violation mémoire

• Il existe NSIG (<signal.h>) signaux différents, identifiés par un numéro de 1 à NSIG

Page 9: Visualiser Thread Unix

Envoi d’un signal

• Les processus peuvent communiquer par le biais des signaux.

La commande kill envoie un signal à un processus (ou groupe de processus) :

int kill (pid_t pid, int sig);

• sig est le nom d'un signal ou un entier entre 1 et NSIG.

• On peut utiliser sig = 0 pour tester l'existence d'un processus.

Traitements par défaut

• A chaque type de signal est associé un handler par défaut appelé SIG_DFL.

• Les traitements par défaut sont:

1- terminaison du processus (avec/sans image mémoire : fichier core)

3- signal ignoré (sans effet)

4- suspension du processus

5- continuation : reprise d'un processus stoppé.

• Un processus peut ignorer un signal en lui associant le handler SIG_IGN.

• Les signaux SIGKILL, SIGCONT et SIGSTOP ne peuvent avoir que le handler SIG_DFL.

Page 10: Visualiser Thread Unix

Traitements spécifiques

• Les signaux (autres que SIGKILL, SIGCONT et SIGSTOP) peuvent avoir un handler spécifique installé par un processus:

• La primitive signal() fait partie du standard de C et non de la norme de POSIX:

#include<signal.h>

void (*signal (int sig, void (*p_handler)(int))) (int);

• Elle installe le handler spécifié par p_handler pour le signal sig. La valeur retournée est un pointeur sur la valeur ancienne du handler.

Attente d’un signal

• La primitive pause() bloque en attente le processus appelant jusqu’à l’arrivée d’un signal.

#include<unistd.h>

int pause (void);

• A la prise en compte d'un signal, le processus peut :

- se terminer car le handler associé est SIG_DFL;

- passer à l'état stoppé; à son réveil, il exécutera de nouveau pause() et s’il a été réveillé par SIGCONT ou SIGTERM avec le handler SIG_IGN, il se mettra de nouveau en attente d'arrivée d'un signal;

- exécuter le handler correspondant au signal intercepté.

• pause() ne permet pas d'attendre un signal de type donné ni de connaître le nom du signal qui l'a réveillé.

Gestion des threads

Descripteur des processus Unix

Bloc de contrôle (utilisé par Linux) comporte :

-État du processus

-Priorité du processus

-Signaux en attente et signaux masqués

-Pointeur sur le processus suivant dans la liste des processus prêts

-Numéro du processus

-Numéro du groupe contenant le processus

-Pointeur sur le processus père

-Numéro de la session contenant le processus

-Identificateur de l’utilisateur réel

-Identificateur de l’utilisateur effectif

-Politique d’ordonnancement utilisé

-Temps processeur consommé en mode noyau et en mode utilisateur

-Date de création du processus

Page 11: Visualiser Thread Unix

Création d’un processus Unix

Un processus Unix = { un espace d’adressage et une unité d’exécution unique }

Contexte d’un processus Unix :

- pid,

- répertoire de travail,

- descripteurs,

-propriétaires,

-implantation en mémoire,

-registres,

-priorité d’ordonnancement,

-masque des signaux,

-pile d’exécution

Cela rend coûteuse la création d’un processus

Création de threads Unix

Thread Unix: - processus léger

- permet de multiplier l’implémentation d’activités caractérisées par des contextes plus légers que ceux des processus Unix

Contexte d’exécution d’une thread Unix:

- registres,

- priorité d’ordonnancement,

-masque des signaux,

-pile d’exécution.

L’espace virtuel d’une thread :

- code exécuté par la thread,

- données définies dans la thread et

une pile d’exécution propre à la thread.

Primitives de manipulation de threads Unix

Création et activation d’une thread :

int pthread_create(

pthread_t *idptr, /* ptr sur la zone où sera retournée l’identité de la thread */

pthread_attr_t attr,/* attributs de la thread: pthread_attr_default ou 0 */

void *(*fonc) (void *), /* pointeur sur la fonction exécutée par la thread */

/* pointeur sur le(s) argument(s) passé(s) à la thread */

void *arg

);

Page 12: Visualiser Thread Unix

Terminaison d’une thread :

void pthread_exit (

void *status /* ptr sur le résultat retourné par la thread */

);

Libération des ressources d’une thread :

int pthread_detach(

pthread_t *idptr /* pointeur sur l’identité de la thread */

);

Attente de la terminaison d’une thread :

int pthread_join(

pthread_t id, /* identité de la thread attendue */

void *status /* pointeur sur le code retour de la thread attendue */

);

Création de segments de mémoire partagés

• Pour créer un segment de mémoire, on attache à ce segment un certain nombre d’informations.

Partage de données

• Les segments de mémoire partagés permettent à deux processus distincts de partager

physiquement des données.

Page 13: Visualiser Thread Unix

Obtention d’un segment de mémoire

int shmget( key_t cle, int taille, int options);

- cle est la clé obtenu par la fonction ftok()

- taille est le nombre d'octets du segment

- options indique les droits d'accès au segment, PC_CREAT | 0666

Attachement d’un segment de mémoire

*shmat(int shmid, const void *adr, int options);

- shmid est l'identificateur du segment

-adr est l'adresse virtuelle à laquelle doit être implanté le segment dans l'espace adressable du processus; une valeur 0 permet de laisser le choix de cette adresse au système.

-options peut valoir SHM_RDONLY pour s'attacher un segment en lecture seule.

Détachement d’un segment de mémoire

La primitive shmdt() détache un segment de l'espace adressable d'un processus.

int shmdt( const void *adr ); //adr est l’adresse retournée par shmat

Un segment non détaché sera détaché automatiquement lorsque tous les processus qui se le sont attachés se terminent.

Suppression d’un segment de mémoire

int shmctl(int shmid, int option, struct shm_ds *p ) //permet d'agir sur un segment partagé.

Si l’option est égale à :

- IPC_RMID : suppression du segment identifié par shmid (la suppression sera effective lorsque aucun processus n'est plus attaché au segment.