Un contexte d’exécution est donc principalement une pile d’exécution, celle qui est manipulée par le programme compilé via les registres %esp et %ebp.
Cette pile n’est, en elle-même qu’une zone mémoire dont on connaît l’adresse de base. Pour pouvoir restaurer un contexte d’exécution il faut aussi connaître la valeur des registres %esp et %ebp qui identifient une frame dans cette pile.
De plus lorsqu’un programme se termine, son contexte d’exécution ne doit plus pouvoir être utilisé.
Enfin, un contexte doit pouvoir être initialisé avec un pointeur de fonction et un pointeur pour les arguments de la fonction. Cette fonction sera celle qui sera appelée lors de la première activation du contexte. On suppose que le pointeur d’arguments est du type void *. La fonction appelée aura tout loisir pour effectuer une coercition de la structure pointée dans le type attendu.
Segment mémoire
La gestion de la pile d’exécution est associée à un segment de mémoire virtuelle pointé par le registre %ss (stack segment). Les noyaux Linux utilisent le même segment pour la pile d’appel et pour le stockage des données usuelles (variables globales et tas d’allocation du C). C’est pourquoi il nous est possible d’allouer un espace mémoire et de l’utiliser pour y placer une pile d’exécution... Sur d’autres systèmes d’exploitation, dissociant ces différents segments, les programmes que nous proposons seraient incorrects.
Exercice 5
- Déclarez un nouveau type func_t, utilisé par le contexte pour connaître le point d’entrée de la fonction (elle ne retourne rien et prend en paramètre le pointeur d’arguments).
- Étendez la structure de donnée struct ctx_s qui décrit un tel contexte.
Exercice 6 Proposez une procédurequi initialise le contexte ctx avec une pile d’exécution de stack_size octets allouée dynamiquement (voir l’encart à propos de cette allocation). Lors de sa première activation ce contexte appellera la fonction f avec le paramètre args.int init_ctx(struct ctx_s *ctx, int stack_size, func_t f, void *args);