setcontext - setcontext

SetContext ist eines aus einer Familie von C Bibliotheksfunktionen (die andere sind getContext , makecontext und swapcontext ) zur verwendeten Kontextsteuerung. Die Familie ermöglicht die Implementierung in C fortschrittlicher Steuerfluß Muster wie Iteratoren , Fasern und Koroutinen . Sie können als erweiterte Version von setjmp / longjmp angesehen werden . Während letzteres nur einen einzigen nicht lokalen Sprung in den Stapel erlaubt , können mehrere kooperative Kontrollthreads mit jeweils einem eigenen Stapel erstellt werden. setcontext setcontext

Spezifikation

setcontext wurde in POSIX .1-2001 und der Single Unix Specification , Version 2, angegeben, aber nicht alle Unix-ähnlichen Betriebssysteme bieten sie an. POSIX .1-2004 hat diese Funktionen überholt und in POSIX .1-2008 wurden sie entfernt, wobei POSIX-Threads als möglicher Ersatz angegeben wurden. Unter Berufung auf IEEE Std 1003.1, Ausgabe 2004:

Mit der Aufnahme der Norm ISO / IEC 9899: 1999 in diese Spezifikation wurde festgestellt, dass die Norm ISO C (Unterabschnitt 6.11.6) festlegt, dass die Verwendung von Funktionsdeklaratoren mit leeren Klammern ein veraltetes Merkmal ist. Verwenden Sie daher den Funktionsprototyp:

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

nutzt eine veraltete Funktion des ISO C-Standards. Daher kann eine streng konforme POSIX-Anwendung dieses Formular nicht verwenden. Daher ist die Verwendung von getcontext (), makecontext () und swapcontext () als veraltet markiert.

In der ISO C-Norm gibt es keine Möglichkeit, einen nicht veralteten Funktionsprototyp anzugeben, der angibt, dass eine Funktion mit einer beliebigen Anzahl (einschließlich Null) von Argumenten beliebigen Typs (einschließlich Ganzzahlen, Zeigern auf Daten, Zeigern auf Funktionen und) aufgerufen wird zusammengesetzte Typen).

Definitionen

Die Funktionen und die zugehörigen Typen sind in der definierten ucontext.h System - Header - Datei . Dies schließt den ucontext_t Typ ein, mit dem alle vier Funktionen arbeiten:

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

uc_link verweist auf den Kontext, der beim Beenden des aktuellen Kontexts fortgesetzt wird, wenn der Kontext mit makecontext (einem sekundären Kontext) erstellt wurde. uc_sigmask wird verwendet, um den Satz von Signalen zu speichern, die im Kontext blockiert sind, und uc_stack ist der Stapel, der vom Kontext verwendet wird. uc_mcontext speichert Ausführungszustand , einschließlich aller Register und CPU - Flags , dem Befehlszeiger und der Stapelzeiger ; ist ein undurchsichtiger Typ . mcontext_t

Die Funktionen sind:

  • int setcontext(const ucontext_t *ucp)
    Diese Funktion überträgt die Kontrolle auf den Kontext in ucp . Die Ausführung wird ab dem Punkt fortgesetzt, an dem der Kontext gespeichert wurde ucp . setcontext kommt nicht zurück.
  • int getcontext(ucontext_t *ucp)
    Speichert den aktuellen Kontext in ucp . Diese Funktion kehrt in zwei möglichen Fällen zurück: nach dem ersten Aufruf oder wenn ein Thread in ucp via setcontext oder in den Kontext wechselt swapcontext . Die getcontext Funktion bietet keinen Rückgabewert zur Unterscheidung der Fälle (ihr Rückgabewert wird ausschließlich zur Signalisierung von Fehlern verwendet). Daher muss der Programmierer eine explizite Flagvariable verwenden, die keine Registervariable sein darf und als flüchtig deklariert werden muss, um eine konstante Ausbreitung zu vermeiden oder andere Compiler-Optimierungen .
  • void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
    Die makecontext Funktion richtet einen alternativen Steuerungs-Thread in ein ucp , der zuvor mit initialisiert wurde getcontext . Das ucp.uc_stack Element sollte auf einen Stapel geeigneter Größe gerichtet sein. Die Konstante SIGSTKSZ wird üblicherweise verwendet. Wenn ucp zur Verwendung von setcontext oder gesprungen swapcontext wird, beginnt die Ausführung am Einstiegspunkt der Funktion, auf die von verwiesen wird func , mit den angegebenen argc Argumenten. Nach func Beendigung wird die Steuerung an zurückgegeben ucp.uc_link .
  • int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
    Überträgt die Steuerung an ucp und speichert den aktuellen Ausführungsstatus in oucp .

Beispiel

Das folgende Beispiel zeigt die Verwendung eines Iterators 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;
}

HINWEIS: Dieses Beispiel ist nicht korrekt, funktioniert jedoch in einigen Fällen wie vorgesehen. Für die Funktion makecontext müssen zusätzliche Parameter eingegeben werden int , das Beispiel übergibt jedoch Zeiger. So kann das beispielsweise auf 64-Bit - Maschinen versagen (insbesondere LP64 -architectures, wo ). Dieses Problem kann umgangen werden, indem 64-Bit-Werte aufgelöst und rekonstruiert werden. Dies führt jedoch zu einer Leistungsbeeinträchtigung. sizeof(void*) > sizeof(int)

Auf Architekturen, bei denen int- und Zeigertypen dieselbe Größe haben (z. B. x86-32, bei denen beide Typen 32 Bit groß sind), können Sie möglicherweise Zeiger als Argumente an makecontext () nach argc übergeben. Es ist jedoch nicht garantiert, dass dies portabel ist, es ist gemäß den Standards undefiniert und funktioniert nicht auf Architekturen, bei denen Zeiger größer als Ints sind. Ab Version 2.8 nimmt glibc jedoch einige Änderungen vor

makecontext(3) , um dies auf einigen 64-Bit-Architekturen (z. B. x86-64) zu ermöglichen.

Zum Abrufen und Festlegen des Kontexts kann ein kleinerer Kontext nützlich sein:

#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;
}

Dies macht eine Endlosschleife, da der Kontext den Programmzähler enthält.

Verweise

  1. ^ a b Die Open Group-Basisspezifikationen Ausgabe 6 IEEE Std 1003.1, Ausgabe 2004 [1]

Externe Links