Posix
Author @Saief1999
C'est quoi une tâche?
- processus lourd : 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
- processus léger(thread): partage le même espace mémoire et la même table des fichiers ouverts que son processus père. Mais dispose de sa propre pile
Création des tâches
int pthread_create(pthread_t* thread, pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);
Parametres des tâches
- thread: pointeur sur l'identificateur de la tâche qui vient d'être créée
- attr: indiquent les différents propriétés de la tâche:
- type d'ordonnancement
- priorité
- joignable ? détachable ?
- start_routine: fonction qui va être executée en premier
- it is the signature of a function pointer that takes and returns
void *
.
- it is the signature of a function pointer that takes and returns
- arg: variables passées en paramètre de start_routine ( NULL for none )
Return Type
- 0 si tout va bien à la création
Joinning
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.
int pthread_join(pthread_t thread, void** thread_return)
Parametres
- thread: indentificateur de la tâche fils
- thread_return: valeur de retour de tâche fils
NULL
si fils ne retourne rien ou on veut pas stocker la valeur
Detaching
- Uitlisé pour forcer la non-attente d'une tâche , même si on utilise
pthread_join
Steps
pthread_attr_t attr
Déclaration de variables des propriétés.pthread_attr_init(&attr)
: Initialisation de attr aux valeurs par défaut (obligatoire).pthread_att_setdatachstate(&att, PTHREAD_CREATE_DETACHED)
: Affectation de la propriété détachable à attr.pthread_create(&tache1, &attr, fonc, 1)
: Créer notre tache avec la propriétépthread_attr_destory($attr)
: Détruire attr pour libérer la mémoire allouée
Scheduling : Priorité et ordonnancement
Pour affecter la priorité à une tache :
struct sched_param param
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched)
: l'os prend en considération les priorités + type d'ordonnancelent, inheritsched:- PTHREAD_EXPLICIT_SCHED: utilise les paramétres d'ordonnancement depuis sont attr
- PTHREAD_INHERIT_SCHED : utilise les paramétres d'ordonnancement de son père.
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param)
: affecter la Prioritéint pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
: affecter le type d'ordonnancement Policy- SCHED_FIFO : ordonnancement préemptif à priorités fixes. Les tâches de même priorité sont ordonnancées en FIFO
- SCHED_RR : Round-Robin à priorité préemptif. Une tâche utilise un quantum de temps puis est déplacée en queue de la file d'attente du niveau de sa priorité.
- SCHED_OTHER : il s'agit d'un ordonnancement à temps partagé entre tâches. Il pointe généralement sur SCHED_FIFO.
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param)
: Affecter policy + priorité lors de l'execution
Data Sharing & critical sections
Exclusion mutuelle
- Sémaphore d'exclusion mutuelle => semaphore(1) ( lock )
- Déclaration de la ressource partagé
pthread_mutex_t verrou
: Declaration de verroupthread_mutex_init(pthread_mutex_t* verrou, const pthread_mutexattr_t* m_attr)
: initialisation du verrou (obligatoire)pthread_mutex_lock(pthread_mutex_t *verrou)
prendre la verroupthread_mutex_unlock(pthread_mutex_t *verrou)
libérer le verrou
Exclusion mutuelle et variable condition
Une tâche accédant à la donnée peut être endormie si la condition n'est pas vérifiée. Elle ne sera réveillée que lorsqu'une autre tâche accédera à cette donnée et rendra la condition vraie.
Lorsqu'une tâche est endormie , elle libére le verrou avant Ceci est équivalent à l'utilisation de
wait
etnotify
dans Java. C'est le même principe.notifyAll
(JAVA) équivalent àpthread_cond_broadcast
(POSIX)
Etapes
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
: crée variable conditionint pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *verrou)
: endormit une tâche (possédant le verrou sur la donnée partagée) si la condition cond est fausseint pthread_cond_signal(pthread_cond_t *cond)
rend la condition cond vraie. Cela envoie un signal de réveil aux tâches qui ont été endormies sur cette condition. Si plusieurs tâches attendent sur une condition, cela ne réveille que l'une d'entre elles.int pthread_cond_broadcast(pthread_cond_t *cond)
: Réveille toute les tâches
Inversion de priorité
- Probleme d'inversion de priorité : Le fait d'attendre un thread moins prioritaire ( qui détient le mutex qu'on nécessite), donc réellement c'est comme on est moins prioritaire de n'importe quel autre thread qui ne nécessite pas ce mutex.
- Ce qu'on fait c'est que on donne la priorité maximale des threads au verrou, pour que n'importe autre thread ne préempte P1
Solution Steps (explained in depth in sheet)
pthread_mutexattr_t m_attr;
: La déclaration de la propriété à affecter au mutex d'exclusion mutuelle (le verrou)int pthread_mutexattr_init(pthread_mutexattr_t *m_attr)
: Init (Oblig)int pthread_mutexattr_setpshared(pthread_mutexattr_t *m_attr, int pshared)
: fonction permettant à un mutex d'être partagé par les tâches appartenant à n'importe quel processus.- PTHREAD_PROCESS_SHARED.
- PTHREAD_PROCESS_PRIVATE: le partage est interne au processus/tâche créateur du mutex, alorspshared doit prendre la valeur
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *m_attr, int prioceiling)
La fonction permettant de mettre dans la propriété m_attr qui sera affectée au mutex, sa priorité plafond prioceilingint pthread_mutexattr_setprotocol(pthread_mutexattr_t *m_attr, int protocol)
: affecte le protocole d'héritage- PTHREAD_PRIO_PROTECT : toute tâche s'emparant du mutex doit hériter de sa priorité
- PTHREAD_PRIO_NONE
- PTHREAD_PRIO_INHERIT
...
Finish this part
Périodicité
struct timespec time
: La déclaration de la structure de données de gestion du tempsint clock_gettime(clockid_t clk_id, struct timespec *time)
: Avoir temps système, clockid_t:- CLOCK_REALTIME
- CLOCK_MONOTONIC
- CLOCK_PROCESS_CPUTIME_ID
- CLOCK_THREAD_CPUTIME_ID
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *verrou, struct timespec *time)
: utilise un décompteur (timeout) sur le temps time pour réveiller la tâche endormie sur l'attente de la variable conditioncond
qui ne sera jamais signalée
Synchronisation : Sémaphores
include <semaphore.h>
etsem_t s
int sem_wait(sem_t* s)
: attendre evtint sem_post(sem_t* s)
: signaler evtint sem_init(sem_t* s, int pshared, unsigned int valeur)
:- Initialement, ce sémaphore est fait pour gérer la synchronisation entre processus lourd. Mais, on peut le restreindre à gérer uniquement la synchronisation entre les tâches du processus courant (dans notre cas, le processus issu de la fonction main(void)), en fixant la variable pshared à 0. Ce sémaphore est partagé entre plusieurs processus si la variable pshared est différente de 0.
- valeur : compteur associé au sémaphore evt
int sem_destroy(sem_t* s)
: détruire evt, Une fois cette fonction appelée, aucune tâche ne doit plus être bloquée sur ce dernier