• Aucun résultat trouvé

4.2 Les modules essentiels

4.2.4 Les services de l’exo-noyau indépendants du matériel

Le module mutek regroupe tous les services de l’exo-noyau qui sont indépendants du matériel. Une des fonctions de l’exo-noyau est le partage et le multiplexage des ressources pour les services de plus haut niveau utilisés par les applications. Ces services de plus haut niveau sont les bibliothèques proposant les diverses interfaces standardisées.

L’exo-noyau de MutekH fournit les services suivants : – Ordonnanceur de contextes d’exécution.

– Primitives de synchronisation basées sur l’ordonnanceur. – Allocation de la mémoire.

– Base de temps globale abstraite du matériel. – Centralisation des messages vers la console. Ordonnancement dans les systèmes hétérogènes

L’ordonnanceur utilise la sauvegarde/restauration de contextes d’exécution, fournie par le module hexo. Ainsi que nous l’avons souligné, un contexte sauvegardé ne peut être restauré que sur un

78 CHAPITRE 4. RÉALISATION DU NOYAU processeur du même type. Ceci interdit la migration d’un contexte déjà démarré sur un processeur de type différent.

Cependant l’ordonnancement des contextes doit être générique et global. Ceci est nécessaire pour qu’une tâche s’exécutant sur un certain processeur puisse interagir avec des tâches s’exécutant sur un processeur d’un autre type, et ce, en utilisant les primitives de synchronisation standards. Ainsi l’ordonnanceur fourni par le module mutek définit plusieurs files d’exécution selon la politique d’ordonnancement choisie et permet de définir et manipuler des files d’attente pour l’implémentation de toutes sortes de primitives de synchronisation.

Il est fréquent qu’un processeur d’un certain type ait à modifier la file d’exécution des contextes associée à un processeur d’un autre type. Ceci est un exemple de mécanisme du noyau qui s’appuie fortement sur le partage des variables en mémoire et sur les accès atomiques entre processeurs de différents types.

La figure 4.9 présente un exemple de primitive de synchronisation basée sur l’ordonnanceur de MutekH. Cette primitive de synchronisation de type pthread_mutex_t est un verrou. Elle est réalisée à l’aide d’une file d’attente de l’ordonnanceur et d’un booléen indiquant la prise du verrou. La fonction pthread_mutex_lock permet de prendre le verrou et place le contexte appelant en attente, seulement si le verrou est déjà pris. La fonction pthread_mutex_unlock tente de réveiller le contexte suivant, en attente sur le verrou, et marque le verrou comme libre le cas échéant. On note que tous les mécanismes permettant le support natif de l’hétérogénéité sont exploitables par les bibliothèques standards et par les applications, mais qu’ils permettent également l’utilisation d’algorithmes et d’approches classiques dès les couches basses du noyau.

Spécialisation des contextes d’exécution

L’approche exo-noyau implique que l’ordonnanceur de contextes reste générique et propose des contextes non spécialisés. Ces contextes génériques permettent d’implémenter des types de contextes variés, tels les threads POSIX ou les processus UNIX.

Le service de variables contextuelles fourni par Hexo se révèle très utile dans cette situation car il permet à chaque bibliothèque de haut niveau de spécialiser un contexte d’exécution en lui accrochant des données qui lui sont propres. Ainsi chaque bibliothèque de parallélisation peut proposer des contextes d’exécution plus complexes que les contextes de base fournis par Hexo, en les spécialisant via des attributs supplémentaires.

De la même manière, Hexo permet de prendre en charge des routines de traitement des exceptions et des routines de traitement des appels système utilisateurs, propres à un contexte d’exécution. Une bibliothèque implémentant UNIX peut par exemple accrocher toutes les données propres à un processus UNIX, telles que les descripteurs de fichiers ouverts, dans des variables globales locales au contexte. Il est aussi possible de définir une routine de traitement des exceptions propre à ce contexte, liée à la gestion des signaux UNIX. D’autres contextes s’exécutant simultanément peuvent être spécialisés différemment par une autre bibliothèque de parallélisation.

4.2. LES MODULES ESSENTIELS 79

typedef struct pthread_mutex_s {

bool_t locked; /* currently locked */

sched_queue_root_t wait; /* wait queue */

} pthread_mutex_t;

error_t pthread_mutex_lock(pthread_mutex_t *mutex) {

sched_queue_wrlock(&mutex->wait); if (mutex->locked) {

/* already locked, wait on queue */

sched_wait_unlock(&mutex->wait); } else {

/* mark mutex as locked */

mutex->locked = 1;

sched_queue_unlock(&mutex->wait); }

return 0; }

error_t pthread_mutex_unlock(pthread_mutex_t *mutex) {

sched_queue_wrlock(&mutex->wait);

/* try to wake any waiting thread */

if (!sched_wake(&mutex->wait)) mutex->locked = 0;

sched_queue_unlock(&mutex->wait); return 0;

}

FIGURE4.9 – Exemple d’implémentation de mutex utilisant les primitives de l’ordonnanceur

Primitives de synchronisation

Rappelons que l’ordonnanceur permet l’implémentation de primitives de synchronisation standards. Certains services du noyau implémentés dans des bibliothèques de services, comme la gestion du système de fichiers, la couche réseau ou les accès bloquants aux périphériques, ont besoin de primi-tives de synchronisation.

L’utilisation de primitives de synchronisation de haut niveau, à titre d’exemples celles qui sont dé-finies dans la bibliothèque de threads POSIX, n’est pas souhaitable dans le noyau pour des raisons de dépendances et de modularité. En effet, leur utilisation poserait deux problèmes : elle forcerait l’activation de la bibliothèque des threads POSIX au niveau de la configuration, alors que de telles primitives sont normalement réservées à l’application ; et elle permettrait l’utilisation des services du noyau exclusivement depuis des contextes d’exécution de type threads POSIX. On ne peut im-poser à l’application l’utilisation des primitives de synchronisation POSIX à la seule activation de la couche réseau ou le système de fichiers, par exemple.

80 CHAPITRE 4. RÉALISATION DU NOYAU sémaphores et des verrous de type rwlock permettant de distinguer les accès en lecture et en écriture. Celles-ci peuvent être invoquées depuis n’importe quel contexte d’exécution.

Allocation de la mémoire

Le module mutek propose différents algorithmes d’allocation de complexités diverses. Tout comme l’ordonnanceur de contextes, l’allocateur mémoire travaille sur des structures de données avec accès concurrents entre processeurs.

L’allocateur mémoire basique utilisé initialement par MutekH a rapidement été remplacé par une version plus évoluée permettant le placement des données pour le support des architectures NUMA. Cette évolution a été effectuée grâce à des projets dépassant le cadre de cette thèse et sans rapport avec l’hétérogénéité. Il est intéressant de noter que la couche d’abstraction Hexo a permis aux nou-veaux algorithmes implémentés, de fonctionner directement sur des plateformes hétérogènes, sans effort ni attention particulière de la part de leurs développeurs.