setcontext - setcontext

setcontext fait partie d'une famille de fonctions de bibliothèque C (les autres étant getcontext , makecontext et swapcontext ) utilisées pour le contrôle de contexte . La famille permet la mise en œuvre en C de modèles de flux de contrôle avancés tels que les itérateurs , les fibres et les coroutines . Ils peuvent être considérés comme une version avancée de setjmp / longjmp ; alors que ce dernier ne permet qu'un seul saut non local dans la pile , permet la création de plusieurs threads de contrôle coopératifs , chacun avec sa propre pile. setcontext setcontext

spécification

setcontext a été spécifié dans POSIX .1-2001 et la spécification Unix unique , version 2, mais tous les systèmes d'exploitation de type Unix ne les fournissent pas. POSIX .1-2004 a rendu obsolète ces fonctions, et dans POSIX .1-2008, elles ont été supprimées, avec les threads POSIX indiqués comme un remplacement possible. Citant IEEE Std 1003.1, édition 2004:

Avec l'incorporation de la norme ISO / CEI 9899: 1999 dans cette spécification, il a été constaté que la norme ISO C (paragraphe 6.11.6) spécifie que l'utilisation de déclarateurs de fonction avec des parenthèses vides est une caractéristique obsolète. Par conséquent, en utilisant le prototype de fonction:

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);

utilise une fonction obsolète de la norme ISO C. Par conséquent, une application POSIX strictement conforme ne peut pas utiliser ce formulaire. Par conséquent, l'utilisation de getcontext (), makecontext () et swapcontext () est marquée comme obsolescente.

Il n'y a aucun moyen dans la norme ISO C de spécifier un prototype de fonction non obsolescent indiquant qu'une fonction sera appelée avec un nombre arbitraire (y compris zéro) d'arguments de types arbitraires (y compris des entiers, des pointeurs vers des données, des pointeurs vers des fonctions, et types composites).

Définitions

Les fonctions et les types associés sont définis dans le fichier d'en-têteucontext.h système . Cela inclut le type avec lequel les quatre fonctions fonctionnent: ucontext_t

typedef struct {
	ucontext_t *uc_link;
	sigset_t    uc_sigmask;
	stack_t     uc_stack;
	mcontext_t  uc_mcontext;
	...
} ucontext_t;

uc_link pointe vers le contexte qui sera repris à la sortie du contexte courant, si le contexte a été créé avec makecontext (un contexte secondaire). uc_sigmask est utilisé pour stocker l'ensemble des signaux bloqués dans le contexte, et uc_stack est la pile utilisée par le contexte. uc_mcontext stocke l' état d' exécution , y compris tous les registres et drapeaux CPU , le pointeur d'instruction et le pointeur de pile ; est un type opaque . mcontext_t

Les fonctions sont:

  • int setcontext(const ucontext_t *ucp)
    Cette fonction transfère le contrôle au contexte dans ucp . L'exécution se poursuit à partir du moment où le contexte a été stocké ucp . setcontext ne revient pas.
  • int getcontext(ucontext_t *ucp)
    Enregistre le contexte actuel dans ucp . Cette fonction retourne dans deux cas possibles: après l'appel initial, ou lorsqu'un thread bascule vers le contexte ucp via setcontext ou swapcontext . La getcontext fonction ne fournit pas de valeur de retour pour distinguer les cas (sa valeur de retour est utilisée uniquement pour signaler une erreur), le programmeur doit donc utiliser une variable de drapeau explicite, qui ne doit pas être une variable de registre et doit être déclarée volatile pour éviter une propagation constante ou d'autres optimisations du compilateur .
  • void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
    La makecontext fonction met en place un autre thread de contrôle dans ucp , qui a été précédemment initialisé à l'aide de getcontext . Le ucp.uc_stack membre doit être dirigé vers une pile de taille appropriée; la constante SIGSTKSZ est couramment utilisée. Quand ucp est passé à l'utilisation de setcontext ou swapcontext , l'exécution commencera au point d'entrée de la fonction pointée par func , avec les argc arguments spécifiés. Lorsque se func termine, le contrôle est retourné à ucp.uc_link .
  • int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
    Transfère le contrôle vers ucp et enregistre l'état d'exécution actuel dans oucp .

Exemple

L'exemple ci-dessous montre un itérateur utilisant setcontext .

#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>

/* The three contexts:
 *    (1) main_context1 : The point in main to which loop will return.
 *    (2) main_context2 : The point in main to which control from loop will
 *                        flow by switching contexts.
 *    (3) loop_context  : The point in loop to which control from main will
 *                        flow by switching contexts. */
ucontext_t main_context1, main_context2, loop_context;

/* The iterator return value. */
volatile int i_from_iterator;

/* This is the iterator function. It is entered on the first call to
 * swapcontext, and loops from 0 to 9. Each value is saved in i_from_iterator,
 * and then swapcontext used to return to the main loop.  The main loop prints
 * the value and calls swapcontext to swap back into the function. When the end
 * of the loop is reached, the function exits, and execution switches to the
 * context pointed to by main_context1. */
void loop(
    ucontext_t *loop_context,
    ucontext_t *other_context,
    int *i_from_iterator)
{
    int i;
    
    for (i=0; i < 10; ++i) {
        /* Write the loop counter into the iterator return location. */
        *i_from_iterator = i;
        
        /* Save the loop context (this point in the code) into ''loop_context'',
         * and switch to other_context. */
        swapcontext(loop_context, other_context);
    }
    
    /* The function falls through to the calling context with an implicit
     * ''setcontext(&loop_context->uc_link);'' */
} 
 
int main(void)
{
    /* The stack for the iterator function. */
    char iterator_stack[SIGSTKSZ];

    /* Flag indicating that the iterator has completed. */
    volatile int iterator_finished;

    getcontext(&loop_context);
    /* Initialise the iterator context. uc_link points to main_context1, the
     * point to return to when the iterator finishes. */
    loop_context.uc_link          = &main_context1;
    loop_context.uc_stack.ss_sp   = iterator_stack;
    loop_context.uc_stack.ss_size = sizeof(iterator_stack);

    /* Fill in loop_context so that it makes swapcontext start loop. The
     * (void (*)(void)) typecast is to avoid a compiler warning but it is
     * not relevant to the behaviour of the function. */
    makecontext(&loop_context, (void (*)(void)) loop,
        3, &loop_context, &main_context2, &i_from_iterator);
   
    /* Clear the finished flag. */      
    iterator_finished = 0;

    /* Save the current context into main_context1. When loop is finished,
     * control flow will return to this point. */
    getcontext(&main_context1);
  
    if (!iterator_finished) {
        /* Set iterator_finished so that when the previous getcontext is
         * returned to via uc_link, the above if condition is false and the
         * iterator is not restarted. */
        iterator_finished = 1;
       
        while (1) {
            /* Save this point into main_context2 and switch into the iterator.
             * The first call will begin loop.  Subsequent calls will switch to
             * the swapcontext in loop. */
            swapcontext(&main_context2, &loop_context);
            printf("%d\n", i_from_iterator);
        }
    }
    
    return 0;
}

REMARQUE: cet exemple n'est pas correct, mais peut fonctionner comme prévu dans certains cas. La fonction makecontext nécessite des paramètres supplémentaires pour être type int , mais l'exemple passe des pointeurs. Ainsi, l'exemple peut échouer sur les machines 64 bits (en particulier les architectures LP64 , où ). Ce problème peut être contourné en fractionnant et en reconstruisant les valeurs 64 bits, mais cela entraîne une baisse des performances. sizeof(void*) > sizeof(int)

Sur les architectures où les types int et pointeur ont la même taille (par exemple, x86-32, où les deux types sont 32 bits), vous pourrez peut-être passer des pointeurs comme arguments à makecontext () après argc. Cependant, cela n'est pas garanti pour être portable, n'est pas défini selon les normes et ne fonctionnera pas sur les architectures où les pointeurs sont plus grands que les ints. Néanmoins, à partir de la version 2.8, la glibc apporte quelques modifications à

makecontext(3) , pour permettre cela sur certaines architectures 64 bits (par exemple, x86-64).

Pour obtenir et définir le contexte, un contexte plus petit peut être pratique:

#include <stdio.h>
#include <ucontext.h>
#include <unistd.h>

int main(int argc, const char *argv[]){
	ucontext_t context;
	
	getcontext(&context);
	puts("Hello world");
	sleep(1);
	setcontext(&context);
	return 0;
}

Cela crée une boucle infinie car le contexte contient le compteur du programme.

Références

  1. ^ a b Les spécifications de base du groupe ouvert Numéro 6 IEEE Std 1003.1, édition 2004 [1]

Liens externes