Planification de la synchronisation
Les programmeurs peuvent contrôler l'ordonnancement de l'exécution des unités d'exécution lorsqu'il existe des contraintes, en particulier des contraintes de temps, qui nécessitent que certaines unités d'exécution soient exécutées plus rapidement que d'autres.
Les objets de synchronisation, tels que les mutex, peuvent bloquer même les unités d'exécution à priorité élevée. Dans certains cas, un comportement indésirable, appelé inversion de priorité, peut se produire. La bibliothèque d'unités d'exécution fournit la fonction de protocoles mutex pour éviter les inversions de priorité.
La planification de la synchronisation définit la façon dont la planification de l'exécution, en particulier la priorité, d'une unité d'exécution est modifiée en maintenant un mutex. Cela permet un comportement personnalisé et évite les inversions de priorité. Il est utile lors de l'utilisation de schémas de verrouillage complexes. Certaines implémentations de la bibliothèque d'unités d'exécution ne fournissent pas de planification de synchronisation.
Inversion de priorité
L'inversion de priorité se produit lorsqu'une unité d'exécution de priorité basse contient un mutex, ce qui bloque une unité d'exécution de priorité élevée. En raison de sa faible priorité, le propriétaire du mutex peut conserver le mutex pendant une durée illimitée. Par conséquent, il devient impossible de garantir des délais d'exécution.
L'exemple suivant illustre une inversion de priorité typique. Dans cet example, on considère le cas d'un système uniprocesseur. Les inversions de priorité se produisent également sur les systèmes multiprocesseurs de la même manière.
pthread_mutex_lock(&M); /* 1 */
...
pthread_mutex_unlock(&M);pthread_mutex_lock(&M); /* 2 */
...
fprintf(...); /* 3 */
...
pthread_mutex_unlock(&M);Prenez en compte la chronologie d'exécution suivante. L'unité d'exécution B est planifiée et exécute la ligne 2. Lors de l'exécution de la ligne 3, l'unité d'exécution B est préemptée par l'unité d'exécution A. L'unité d'exécution A exécute la ligne 1 et est bloquée, car le mutex M est maintenu par l'unité d'exécution B. Par conséquent, d'autres unités d'exécution du processus sont planifiées. Etant donné que l'unité d'exécution B a une priorité très faible, elle ne peut pas être replanifiée sur une longue période, ce qui bloque l'unité d'exécution A, bien que l'unité d'exécution A ait une priorité très élevée.
Protocoles Mutex
Pour éviter les inversions de priorité, les protocoles mutex suivants sont fournis par la bibliothèque d'unités d'exécution:
- Protocole d'héritage de priorité
- Parfois appelé protocole d'héritage de priorité de base. Dans le protocole d'héritage de priorité, le détenteur de mutex hérite de la priorité de l'unité d'exécution bloquée dont la priorité est la plus élevée. Lorsqu'une unité d'exécution tente de verrouiller un processus mutex à l'aide de ce protocole et qu'il est bloqué, le propriétaire du processus mutex reçoit temporairement la priorité de l'unité d'exécution bloquée, si cette priorité est supérieure à celle du propriétaire. Il récupère sa priorité d'origine lorsqu'il déverrouille le mutex.
- Protocole de protection des priorités
- Parfois appelée émulation de protocole de plafond de priorité. Dans le protocole de protection de priorité, chaque mutex a un plafond de priorité. Il s'agit d'un niveau de priorité compris dans la plage de priorités valide. Lorsqu'une unité d'exécution possède un mutex, elle reçoit temporairement le plafond de priorité de mutex, si le plafond est supérieur à sa propre priorité. Il récupère sa priorité d'origine lorsqu'il déverrouille le mutex. Le plafond de priorité doit avoir la valeur de la priorité la plus élevée de toutes les unités d'exécution qui peuvent verrouiller le mutex. Sinon, des inversions de priorité ou même des interblocages peuvent se produire et le protocole serait inefficace.
Les deux protocoles augmentent la priorité d'une unité d'exécution contenant un mutex spécifique, de sorte que les échéances peuvent être garanties. En outre, lorsqu'ils sont correctement utilisés, les protocoles mutex peuvent empêcher les interblocages mutuels. Les protocoles mutex sont affectés individuellement aux mutex.
Choix d'un protocole mutex
| Valeur | Descriptif |
|---|---|
| PTHREAD_PRIO_DEFAULT | Aucune valeur |
| PTHREAD_PRIO_NONE | Indique qu'aucun protocole n'a été défini. |
| PTHREAD_PRIO_INHERIT | Indique le protocole d'héritage de priorité. |
| PTHREAD_PRIO_PROTECT | Indique le protocole de protection de priorité. |
Le protocole de protection de priorité utilise un attribut supplémentaire: l'attribut prioceiling. Cet attribut contient le plafond de priorité du mutex. L'attribut prioceiling peut être contrôlé dans l'objet d'attributs mutex, à l'aide des sous-routines pthread_mutexattr_getprioceiling et pthread_mutexattr_setprioceiling .
L'attribut prioceiling d'un mutex peut également être contrôlé dynamiquement à l'aide des sous-routines pthread_mutex_getprioceiling et pthread_mutex_setprioceiling . Lors de la modification dynamique du plafond de priorité d'un mutex, le mutex est verrouillé par la bibliothèque ; il ne doit pas être conservé par l'unité d'exécution qui appelle la sous-routine pthread_mutex_setprioceiling pour éviter un interblocage. La définition dynamique du plafond de priorité d'un mutex peut être utile lors de l'augmentation de la priorité d'une unité d'exécution.
L'implémentation des protocoles mutex est facultative. Chaque protocole est une option POSIX .
Héritage ou protection
Les deux protocoles sont similaires et permettent de promouvoir la priorité de l'unité d'exécution contenant le mutex. Si les deux protocoles sont disponibles, les programmeurs doivent choisir un protocole. Le choix varie selon que les priorités des unités d'exécution qui verrouillent le mutex sont disponibles pour le programmeur qui crée le mutex. Généralement, les mutex définis par une bibliothèque et utilisés par les unités d'exécution d'application utilisent le protocole d'héritage, tandis que les mutex créés dans le programme d'application utilisent le protocole de protection.
- A l'aide du protocole d'héritage, un appel système est effectué chaque fois qu'une unité d'exécution est bloquée lors de la tentative de verrouillage du mutex.
- A l'aide du protocole de protection, un appel système est toujours effectué chaque fois que le mutex est verrouillé par une unité d'exécution.
Dans la plupart des programmes critiques pour les performances, le protocole d'héritage doit être choisi, car les exclusions mutuelles sont des objets à faible conflit. Les mutex ne sont pas conservés pendant de longues périodes ; par conséquent, il est peu probable que les unités d'exécution soient bloquées lorsqu'elles tentent de les verrouiller.