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.setcontextne revient pas.
- Cette fonction transfère le contrôle au contexte dans
-
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 contexteucpviasetcontextouswapcontext. Lagetcontextfonction 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 .
- Enregistre le contexte actuel dans
-
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)- La
makecontextfonction met en place un autre thread de contrôle dansucp, qui a été précédemment initialisé à l'aide degetcontext. Leucp.uc_stackmembre doit être dirigé vers une pile de taille appropriée; la constanteSIGSTKSZest couramment utilisée. Quanducpest passé à l'utilisation desetcontextouswapcontext, l'exécution commencera au point d'entrée de la fonction pointée parfunc, avec lesargcarguments spécifiés. Lorsque sefunctermine, le contrôle est retourné àucp.uc_link.
- La
-
int swapcontext(ucontext_t *oucp, ucontext_t *ucp)- Transfère le contrôle vers
ucpet enregistre l'état d'exécution actuel dansoucp.
- Transfère le contrôle vers
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 à
, 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
Liens externes
- Contextes System V - Le manuel de la bibliothèque GNU C
- : obtenir / définir le contexte utilisateur actuel - Manuel du programmeur Linux - Fonctions de la bibliothèque
- setcontext - obtenir / définir le contexte actuel de l'utilisateur Page de manuel FreeBSD.