/** \htmlonly \endhtmlonly \author J. Briat \date Janvier 2004 \mainpage FRED : un noyau minimal de threads FRED est un noyau de thread minimal à but pédagogique. Il donne un exemple d'implantation dit N::1 ou en mode utilisateur. Un processus lourd ( process UNIX ici) est vu comme une machine monoprocesseur dont on va partager les ressources (temps d'exécution et mémoire) entre plusieurs threads. Un thread sera simplement l'exécution d'une procédure C. L'exécution des threads par le processeur virtuel (processus lourd) se fera périodiquement à tour de rôle (temps partagé) ou au gré des synchronisations entre ces threads. L'étudiant trouvera une description de FRED à travers les sections qui suivent : La description de l'interface applicative( \ref Sfred ) :comment écrire un programme concurrent FRED, comment lancer des threads etc.. Il trouvera aussi une description de l'implémentation( \ref ISimpl ) et des mécanismes de base nécessaires. Dans la version html de ce document, il pourra naviguer à son gré dans la description des différents modules de FRED. \htmlonly
\endhtmlonly \section Sfred L'interface applicative de FRED \htmlonly
\endhtmlonly FRED offre les fonctionnalités primitives d'un noyau de threads gérées en temps partagé. Un noyau de thread réaliste doit offrir des fonctions plus élaborées. Toutes ces fonctions ne sont cependant que des extensions des mécanismes de base de FRED. Ces fonctions sont présentées en plusieurs sections : - \ref Sinit : comment définir un programme concurrent - \ref Sthread : comment créer des threads - \ref Smutex : comment faire une section critique - \ref Scond : comment synchroniser des threads - \ref Smem : comment allouer/libérer de la mémoire libre - \ref Sio : comment lire/écrire élémentaire dans un fichier ou flot d'E/S - \ref Sutil : des exemples de réalisation d'objet de synchronisation (\ref Ssema ) ou d'algorithme de synchronisation \e classique (\ref Sphilo ) \htmlonly
\endhtmlonly \subsection Sinit Initialisation & terminaison \htmlonly
\endhtmlonly Pour utiliser FRED, il faut inclure le fichier fred.h pour disposer de toutes les déclaration de types et profils de procédures de l'interface. Il est aussi nécessaire dans la procédure initiale main() du programme principal concurrent d'initialiser les variables statiques de FRED. C'est le rôle de FredInitialize(). Puis le programmeur peut initialiser ses propres variables globales et lancer ses threads initiaux, se synchroniser etc.... FredWaitTerminate() lui permet d'attendre la terminaison des threads initiaux (et des threads créés par ceux-ci et etc....). Quand la procédure main() termine, tout le programme concurrent termine. \code #include int main( int argc, char** argv) {/* FRED n'est pas utilisable. Seul du code C séquentiel est exécutable */ <....> FredInitialize(); /* FRED est partiellement utilisable. On peut créer des threads, intialiser des objets satiques, se synchroniser .... */ <....> FredWaitTerminate(); /* Attente de la terminaison de tous les threads. A la reprise, FRED n'est plus utilisable. Seul du code C séquentiel est exécutable. */ <....> } \endcode N'importe quel thread peut terminer brutalement le programme concurrent par FredAbort() en passant en paramètre une chaine de caractère précisant la raison de l'arrêt brutal. \sa Ginit \htmlonly
\endhtmlonly \subsection Sthread Gestion des threads \htmlonly
\endhtmlonly Un thread est l'exécution concurrente d'une procedure C à \b un paramètre de type (void*) et \b un résultat de type (void*). Son type est défini par ::FredRunnable. On rappelle que tout pointeur (T*)étant \b "castable" en (void*) et réciproquement, le type (void *) veut dire référence sur n'importe quel type. Pour s'exécuter, un thread a besoin d'une pile pour ses contextes (trames) de procédure. La création d'un thread nécessite donc de donner de : - une procédure de type ::FredRunnable - une taille de pile - un paramètre de type (void*) - un endoit où ranger un résultat de type (void*) soit un paramètre de type ((void*)*). Si aucun résultat n'est souhaité, mettre (void**)0. La procédure FredThreadSpawn() crée et lance donc un thread (c.à.d. le met dans la file des threads prêts à s'exécuter). Un thread se termine par terminaison normale de sa procédure initiale ou par suicide. La procédure FredThreadExit() permet à un thread de se terminer en rendant un résultat de type (void*). Un thread peut connaitre son identité (reférence sur son descripteur (type ::FredThread)) par la fonction FredThreadCurrent(). Le thread courant peut spontanément abandonner le processeur au profit d'un autre thread prêt au sens des règles d'ordonnancement par FredThreadYield(). \sa Gthread \htmlonly
\endhtmlonly \subsection Smutex Exclusion mutuelle \htmlonly
\endhtmlonly FRED n'offre qu'un mécanisme d'exclusion mutuelle basé sur les mécanismes matériel : inhibition des interruption sur monoprocesseur qui doit être étendu par un verrouillage de type "TestAndSet" sur multiprocesseur. L'utilisation d'un verrouillage matériel n'est utilisable que sur des durées très courtes pour éviter de retarder ou perdre des signaux externes ou bloquer les autres processeurs(cas multiprocesseur). Dès que la durée d'exclusion mutuelle devient importante, il faut recourir à des mécanismes de synchronisation plus élaborés (sémaphore, verrou Posix etc..). L'exclusion mutuelle repose sur les 2 procédures FredMutexOn() et FredMutexOff(). \warning Il est interdit d'imbriquer les sections critiques. \code /* code incorrect */ ....FredMutexOn();....; FredMutexOn();....FredMutexOff();....FredMutexOff();.... \endcode \sa Gmutex \htmlonly
\endhtmlonly \subsection Scond File d'attente \htmlonly
\endhtmlonly L'exclusion mutuelle ne suffit pas pour exprimer toutes les relations de dépendance et de synchronisation entre threads. Il est nécessaire de bloquer un thread si sa continuation falsifierait un \b invariant \b d'état \b global et, réciproquement, de réveiller les threads dès que leurs continuations préservent cet invariant. FRED offre le type ::FredQueue qui permet de définir une file d'attente de threads gérée selon la statégie FIFO. On initialise une queue par FredQueueInit(). Le thread courant peut se bloquer dans une file d'attente c.à.d. il s'inscrit en dernier dans cette file et donne le processeur à un autre thread prêt. C'est la procédure FredThreadSuspend(). Une fois bloqué dans une file, un thread peut être sorti de la file par FredQueueGetFirst(), remis dans une autre file par FredQueuePutLast(). On peut réveiller le premier d'une file d'attente c.à.d. le remettre dans la file des threads prêts par FredThreadWakeUp(). Utilisées \b nécessairement dans une portion de code en exclusion mutuelle (entre FredMaskOn() et FredMaskOff()), ces procédures permettent de réaliser des opérateurs de sunchronisation plus élaborés (\ref Ssema). \sa Gcond \htmlonly
\endhtmlonly \subsection Smem Gestion mémoire \htmlonly
\endhtmlonly La création des threads comme la programmation concurrente en C nécessitent d'allouer et libérer dynamiquement des blocs de mémoire. Les procédures d'allocation et libération modifient la description de l'état d'occupation de la mémoire. Ce sont des procédures qui doivent s'exécuter en exclusion mutuelle. FRED fournit un carrossage de la gestion mémoire standard de C pour assurer l'atomicité de l'allocation/ libération des blocs mémoires. FredAtomicAlloc(),FredAtomicCalloc() et FredAtomicFree()sont fonctionnellement identiques aux procédures malloc(),calloc() et free() de la libC. Les trois macros FredNEW(), FredNEW_VECTOR() et FredDELETE() sont de simples carrossages des précédentes. \sa Gmem \htmlonly
\endhtmlonly \subsection Sio Entrée/Sortie \htmlonly
\endhtmlonly Pour des raisons identiques à la gestion mémoire, l'accès aux fichiers par les threads doit aussi être synchronisé. FRED ne fournit des procédures atomiques que pour les procédures de type printf() et scanf() de la libC. \sa Gio \htmlonly
\endhtmlonly \section Sutil Exemples d'utilisation \htmlonly
\endhtmlonly L'exclusion mutuelle par masquage et la gestion de files de threads sont suffisantes pour reconstruire des opérateurs de synchronisation plus souples et evitant de masquer les interruption trop longtemps. C'est le cas du sémaphore de Dijkstra. On peut ensuite traiter un problème classique de synchronisation comme les N philosophes. \htmlonly
\endhtmlonly \subsection Ssema Un exemple d'outil de synchronisation : le sémaphore \htmlonly
\endhtmlonly Un sémaphore est un simple sac où l'on prend ou remet des jetons un à un. Un thread demandant un jeton doit attendre qu'il y en ait un de disponible. Tout thread déposant un jeton doit réveiller le plus ancien thread en attente de jeton s'il en existe. Le sémaphore doit être déclaré de type FredSemaphore, alloué en variable globale ou sur le tas. Dans tousles cas, il doit être initialisé à une valeur >=0 via FredSemaphoreInit(). \code FredSemaphore GlobalS ; /* sémaphore global */ struct S1 { /* struct globale */ Semaphore member; /* avec sémaphore membre */ ... } void exemple() { S1 * s; FredSemaphore LocalS ; /* sémaphore local */ FredSemaphore* DynaS ; /* sémaphore dynamique */ ..... FredSemaphoreInit(&LocalS,0); /* initialisation */ .... DynaS = FredNEW(FredSemaphore); /* allocation */ FredSemaphoreInit(DynaS,3); /* initialisation */ ..... FredSemaphoreInit(&GlobalS,25); /* initialisation */ ..... s= FredNEW(S1); /* allocation d'une structure */ FredSemaphoreInit(&(s->member),33); /* initialisation du membre*/ ..... } \endcode Il peut ensuite être utilisé pour prendre FredSemaphoreP() ou déposer un jeton FredSemaphoreV() L'implantation nécessite un compteur et une queue de threads en attente (cf. ::FredSemaphore). Le compteur représente le nombre de jetons si le compteur est >=0 et le nombre de threads en attente s'il est <0. Le test du compteur, sa modification et la manipulation de la file d'attente doivent être atomiques et se font donc masqués. (cf. \ref Gsema) \htmlonly
\endhtmlonly \subsection Sphilo Les N philosophes \htmlonly
\endhtmlonly Le programme principal initie : - les sémaphores représentant les fourchettes - les philosophes \code #define NBphilo 5 FredThread philosophe[NBphilo]; FredSemaphore fourchette[NBphilo]; int main ( int argc, char** argv) { FredInitialize(); for( i=0;i \endhtmlonly \section ISimpl Implantation de FRED \htmlonly
\endhtmlonly Le principe d'implantation d'un noyau de gestion de thread sur un monoprocesseur est d'entrelacer l'exécution des procédures associées à des threads au gré de synchronisation explicite ( sémaphore, verrou,..) ou des interruptions ( temps partagé, E/S). Il faut donc être capable de trier entre les threads \b suspendus en attente d'un signal externe (E/S, fin de quantum,..) ou interne ( sémaphore passant, réveil par un autre thread,..), \b ceux prêts à tourner et celui qui s'exécute sur le processeur : le thread \b actif ou \b courant. La technique classique est l'utilisation de listes que l'on appelle \b queues ou \b files d'attente (\ref ISlist ). L'autre mécanisme nécessaire est le mécanisme qui permet de passer d'une exécution de procédure à une autre puis faire l'opération inverse. Pour ce faire, il suffit de sauvegarder l'état du processeur (registres) d'un thread et de relancer un autre thread depuis un état sauvé. Il faut de plus savoir créer un nouvel état d'exécution lors de la création d'un nouveau thread. A tout thread sera associée une zone de sauvegarde pour ses registres (compteur ordinal, stack pointeur, registres généraux etc...). La taille et l'organisation de cette zone dépend donc du processeur et de la façon de sauvegarder les registres. Pour passer d'un thread à un autre, il suffira de sauver les registres dans la zone du thread en cours d'exécution puis de recharger les registres à partir de la zone de sauvegarde d'un autre thread qui reprend alors sont exécution( \ref ISctx ). Enfin,il faut disposer d'un mécanisme permettant de faire ces commutation entre threads de façon atomique. Comme sur un monoprocesseur, la seule possibilité pour empêcher qu'une suite d'instructions de s'exécuter complètement est une interruption, l'atomicité est simplement réalisée en inhibant les interruptions au début de cette séquence( \ref ISmask ) FRED est un noyau où les threads sont gérés en temps partagé. Il faut donc d'une part récupérer des interruptions( \ref ISit ) et gérer un mécanisme de décomptage du temps pour provoquer une interruption périodique permettant de changer le thread actif( \ref IStimer ) La réalisation et mise en oeuvre du noyau de threads utilise des fonctions d'allocation mémoire et d'E/S. Les fonctions offertes par la bibliothèque standard C n'ont pas toujours été prévues pour être partagées entre des threads. Il est prudent de les rendre compatibles ( \b thread \b safe )avec les threads ( \ref ISsys ). L'implantation d'un noyau de thread est alors l'utilisation coordonnée de toutes ces fonctions( \ref ISthread ). \htmlonly
\endhtmlonly \subsection ISlist Gestion de listes \htmlonly
\endhtmlonly La gestion de liste proposée est une liste \b circulaire \b doublement \b chainée. Ceci veut dire que toute cellule (de type ::FredCell ) de liste contient un double chainage c'est à dire un pointeur sur la cellule d'avant et celle d'après. La première cellule et la dernière cellule sont reliées. La liste (de type ::FredList ) pointe sur un élément qui est considéré comme le dernier élément de la liste. L'élément qui le suit est donc le premier élément de la liste. L'élément qui le précède est donc l'avant-dernier élément de la liste. Pour plus de détails, voir \ref Glist \htmlonly
\endhtmlonly \subsection ISctx Commutation de contexte \htmlonly
\endhtmlonly Du point de vue de l'entrelacement des exécutions sur le processeur des différentes procédures que sont les threads, un descripteur de thread doit inclure une zone de sauvegardes pour ses registres (compteur ordinal, stack pointeur, registres généraux etc...). La taille et l'organisation de cette zone dépend donc du processeur et de la façon de sauvegarder les registres. Pour passer d'une exécution de thread à un autre, il suffira de sauver les registres dans la zone du thread en cours d'exécution puis de recharger les registres à partir de la zone de sauvegarde d'un autre thread qui reprend alors son exécution. En plus des opération de sauvegarde restauration, il faut pouvoir initialiser une zone de sauvegarde de façon qu'un thread puisse démarrer correctement sa procédure initiale avec sa pile. La gestion de Contexte est proposée en plusieurs variantes pour des systèmes UNIX . La première est basée sur les procédures \c setjmp()-\c lonjmp() qui sont fournies avec la librairie standard C et qui réalisent une forme de sauvegarde/restauration de contexte permettant d'écrire une commutation contexte de faççn portable. Par contre, l'initialisation d'un contexte reste à faire en fonction de la machine hôte et de l'implantation de la structure "jmpbuf" utilisée par la lib C. On trouvera une variation à destination de Linux IA32 ( \ref Gctxlinuxia32 ) et l'autre d'un Sparc32 sous Solaris ( \ref Gctxsolarissparc32 ) La seconde façon consiste à écrire directement la commutation de contexte en code natif pour une machine donnée.L'exemple ( \ref Gctxia32 ) proposé est à destination de Linux et l'architecture IA32 d'Intel (386/486/Pentium etc.et clones..). La méthode proposée sauve tous les registres dans la pile d'un thread et sauve donc le seul pointeur de pile dans le contexte. Ces procédures dépendent de la machine hôte mais aussi des conventions de passage de paramètres entre procédures C, des prologues et épilogues des procédures générés par le compilateur C. Pour les connaitre, il faut analyser le code assembleur engendré par le compilateur ( option -S ). La version proposé est conforme au code généré par le compilateur gcc 3.2 Le choix de l'implantation à utiliser pour FRED est fixé par une macro variable du préprocesseur C qui permet d'inclure l'implantation choisi. (cf le fichier makefile et le début du fichier contextswitch.h ) \htmlonly
\endhtmlonly \subsection ISit Traitement des interruptions \htmlonly
\endhtmlonly La gestion des autorisations/inhibitions des interruptions est nécessaire pour plusieurs raisons : - l'utilisation des interruptions d'une horloge permet d'implanter une allocation du processeur en temps partagé et une gestion d'échéancier etc.. - l'utilisation des interruptions permet de réagir aux évènements extérieurs (E/S) - le masquage des interruptions est le seul moyen INITIAL d'assurer l'atomicité d'exécution d'une séquence de code Les interruptions issues de l'environnement sont transmises par le système aux processus lourds utilisateurs selon un modèle voisin du mécanisme matériel. L'occurrence d'un signal répéré par son numéro provoque l'exécution d'une routine de traitement qui lui a été associée(attachée). La sauvegarde des registres et le masquage contre le signal est fait préalablement à l'appel de la routine de traitement. La restauration de l'état antérieure est fait au retour de cette routine de traitement. Une procédure de traitement de signal est une procédure C sans résultat à paramètre numéro de signal. On associe un traitant d'interruption à une interruption par FredSignalAttach(). Par défaut à l'initialisation( FredSignalsInit() ), on installe pour les interruptions un traitant par défaut : defaultFredSignalHandler() . Pour voir la liste des signaux UNIX : "man -S 7 signal" \htmlonly
\endhtmlonly \subsection ISmask Inhibition des interruptions \htmlonly
\endhtmlonly Sur un monoprocesseur interruptible par des évènements internes ou externes, le mécanisme de base pour assurer l'exclusion mutuelle est d'inhiber temporairement les interruptions. Dans FRED, on gardera cependant la possibilite de tuer le process UNIX (par Ctrl_C ) même en cas de programme en boucle infinie en exclusion mutuelle donc masquée. FRED n'offre qu'un simple mécanisme de masquage/démasquage des interruptions utilisable via les 2 procédures FredMaskOn() et FredMaskOff(). Les masques utilisés sont initialisés par FredMasksInit(). \warning Il est incorrect d'imbriquer les sections critiques. \code /* code incorrect */ ....FredMaskOn();....; FredMaskOn();....FredMaskOff();....FredMaskOff();.... \endcode \htmlonly
\endhtmlonly \subsection IStimer Gestion du décompteur \htmlonly
\endhtmlonly Le plus petit mécanisme de base pour construire une gestion en temps partagé, un échéancier ou la gestion de la date est le compteur à rebours ou décompteur. Un compteur initialement chargé avec une valeur positive est décrémenté périodiquement. Au passage à zéro, une interruption est engendrée et le décomptage s'arrête. FRED offre un décompteur unique pour la gestion du temps partagé. Il est basé sur l'utilisation d'un des compteurs UNIX : le timer virtuel (ITIMER_VIRTUAL) qui ne décompte que durant l'exécution du processus qui l'utilise. Il est utilisé en décompteur. L'interruption qu'il engrendre est le signal SIGVTALRM. Il est alors possible de lancer un décomptage en microsecondes( FredCountDownStart() ) ou de l'arrêter ( FredCountDownStop() ). Dans les deux cas, on récupère la valeur résiduelle du décompte en cours au moment de ces invocations. On peut directement lire la valeur résiduelle par FredCountDownGet() . Par FredCountDownAttach() , on peut attacher un traitant d'interruption à la fin décomptage. \htmlonly
\endhtmlonly \subsection ISsys E/S et gestion de mémoire "thread-safe" \htmlonly
\endhtmlonly Pour rendre \b thread \b safe , les procédures de la bibliothèque standard C, il suffit de les mettre en exclusion mutuelle. Dans le cadre de FRED, cela est fait par inhibition des interruptions( cf. \ref Gmem et \ref Gio ) \htmlonly
\endhtmlonly \subsection ISthread Gestion des threads \htmlonly
\endhtmlonly Une fois la commutation de contexte établie, la gestion des threads est relativement simple. Le cycle de vie d'un thread est le suivant : - A sa création par FredThreadSpawn() , il lui est alloué un descripteur FredThreadDesc, de la mémoire pour sa pile et une description de l'appel de procédure initiale. Après initialisation de son contexte, il est \b prêt à s'exécuter. On le met dans la file d'attente des threads prêts #readyQ . - Lorsque c'est son tour d'avoir le processeur selon la règle du tourniquet (temps partagé équitable), il devient actif. C'est le thread \b courant. Il le reste pour une durée bornée (\b quantum de temps). On démarre donc le décompteur lorsqu'il est activé. - Le thread reste actif soit jusqu'à l'interruption de fin de décomptage ( exécution de QuantumHandler() ) qui le recycle en fin de file des prêts pour relancer un autre thread. Si le thread se bloque avant la fin de quantum dans une file d'attente de synchronisation ( FredThreadSuspend() ), on arrête le décomptage et on active un autre thread prêt ( en relançant le décomptage). - Lorsque le thread est réveillé par un autre thread FredThreadWakeUp() , il est remis en queue de file des prêts. - Un thread actif peut aussi se terminer normalement à la fin de sa procédure initiale ou par FredThreadExit(). La terminaison du thread implique de libérer sa pile. Un autre thread prêt est alors activé. L'initialisation de la pile du thread et de son contexte nécessite de déterminer la taille minimum nécessaire et le sens de croissance de la pile c'est à dire vers les adresses faibles ou l'inverse. C'est le rôle du programme inspect_stack.c de déterminer cela et de générer le fichier Cstack.h qui contiendra ces informations nécessaires à la création d'un thread. La libération de la mémoire contenant pile par une procédure s'exécutant dans cette pile engendre des erreurs. Il est nécessaire d'exécuter ce code de libération dans une pile différente. FRED utilise pour cela un thread spécialisé dans le nettoyage de la mémoire #leon qui est créé au départ du programme concurrent. Les threads qui se terminent s'inscrivent dans une file de décédés #deadQ et réveillent leon. Le moteur du noyau de thread est donc le mécanisme qui relance systématiquement le thread en tête de la file des prêts #readyQ. Si la file est vide, le moteur est en panne. Pour éviter cette situation, on introduit un thread qui ne se termine jamais et ne fait que passer la main aux autres threads. Ce chien de garde #watchdog est démarré au lancement de FRED ( FredInitialize()) Le programme FRED démarre comme un programme C standard. FRED est disponible après son initialisation FredInitialize(). Pour que l'on puisse utiliser toute l'API FRED dans la procédure main(), il est nécessaire de lui adjoindre un thread #mainThread que l'on pourra interrompre et synchroniser comme tout autre thread. Enfin, la gestion de la terminaison nécessite la mise en place d'un protocole entre #leon qui détruit effectivement les threads et FredWaitTerminate() qui permet au thread principal d'attendre la fin des autres threads (sauf #leon et #watcdog ). Pour plus de détails, voir \ref Gthread et \ref IGthread */ // DON'T REMOVE /*----------------------------- END OF MANUAL ---------------------------- ------------------------------------------------------------------------ ------------------------------------------------------------------------*/ // Group definition /*---------------------------------------------------------------------*/ // Description of modules. /*---------------------------------------------------------------------*/ /** \defgroup Gfred Interface FRED \brief Ce module définit l'interface applicative de FRED */ /** \defgroup Ginit Interface d'initialisation & terminaison \ingroup Gfred \brief Ce module définit comment initier et terminer un programme concurrent FRED. Pour savoir comment l'utiliser, voir \ref Sinit et \ref Sphilo */ /** \defgroup Gthread Interface de gestion de threads \ingroup Gfred \brief Ce module définit comment créer des threads, terminer un thread ou un programme concurrent et connaitre le thread courant. Pour savoir comment l'utiliser, voir \ref Sthread et \ref Sphilo */ /** \defgroup Gmutex Interface d'exclusion mutuelle \ingroup Gfred \brief Ce module définit comment exécuter une séquence de code en exclusion mutuelle Cela se fait par simple masquage et démasquage d'interruptions. Pour savoir comment l'utiliser, voir \ref Smutex , \ref Ssema et \ref Sphilo */ /** \defgroup Gcond Interface de gestion de file d'attente \ingroup Gfred \brief Ce module définit comment bloquer ou réveiller un thread dans une file d'attente. Pour savoir comment l'utiliser, voir \ref Scond , \ref Ssema et \ref Sphilo */ /** \defgroup Gmem Interface de gestion mémoire \ingroup Gfred \brief Ce module définit comment allouer ou libérer de la mémoire atomiquement Il suffit d'exécuter les procédure de la bibliothèque C en exclusion mutuelle. */ /** \defgroup Gio Interface d'entrée/Sortie \ingroup Gfred \brief Ce module définit comment faire des E/S atomiquement Il suffit d'exécuter les procédure de la bibliothèque C en exclusion mutuelle. */ /** \defgroup Gsema Interface sémaphore \brief Une implémentation classique du classique sémaphore de Dijkstra. */ /** \defgroup Gimpl Implémentation de FRED \brief Ce module regroupe les sous modules nécessaires à la mise en oeuvre de FRED. */ /** \defgroup Glist Interface de gestion de listes \ingroup Gimpl \brief Ce module est une gestion de liste FIFO Elle est implantée comme une liste circulaire doublement chaînée avec insertion en queue et retrait en tête. */ /** \defgroup Gctx Interface de gestion de contexte \ingroup Gimpl \brief Ce module regroupe plusieurs implantations de la commutation de contexte processeur */ /** \defgroup Gctxlinuxia32 Gestion de contexte pour Linux IA32 par setjmp/longjmp \ingroup Gctx */ /** \defgroup Gctxia32 Gestion de contexte pour Linux IA32 (code natif) \ingroup Gctx */ /** \defgroup Gctxsolarissparc32 Gestion de contexte pour Solaris Sparc32 par setjmp/longjmp \ingroup Gctx */ /** \defgroup Gmsk Interface de masquage/démasquage des interruptions \ingroup Gimpl */ /** \defgroup Git Interface de traitement des interruptions \ingroup Gimpl */ /** \defgroup Gtimer Interface de gestion du décompteur \ingroup Gimpl */ /** \defgroup IGthread Implantation des threads \ingroup Gimpl */