Setcontext

A setcontext a POSIX szabványos könyvtári függvényei közé tartozik ( egyébként a getcontext , a makecontext és a swapcontext ), amelyeket a kontextus kezelésére használnak. A család setcontextlehetővé teszi a C számára, hogy áramlásszabályozási mintákat, például iterátorokat , szálakat és korutinokat valósítson meg . A családot bővített változatnak tekinthetjük ; míg az utóbbi csak egy nem helyi verem-ugrást tesz lehetővé, több együttműködő vezérlési folyam létrehozását teszi lehetővé saját veremekkel . setjmp/longjmpsetcontext

Specifikáció

setcontexta POSIX .1-2001-ben és a Single UNIX Specification második verziójában definiált , de nem érhető el minden UNIX-szerű operációs rendszeren . A függvények és a hozzájuk tartozó típusok a fejlécfájlban vannak meghatározva ucontext.h. Ide tartozik az a típus ucontext_t, amellyel mind a négy funkció együttműködik:

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

uc_linkarra a kontextusra mutat, amely az aktuális kontextusból való kilépéskor vissza lesz állítva, ha a kontextus ezzel lett létrehozva makecontext(másodlagos kontextus). a kontextusba zárt jelekuc_sigmask tárolására szolgál, és ez a kontextus által használt verem . a végrehajtási állapot tárolására szolgál, beleértve az összes CPU - regisztert , a programszámlálót és a veremmutatót; egy átlátszatlan mutató. uc_stackuc_mcontext mcontext_t

A következő funkciókat is meghatározták:

  • int setcontext(const ucontext_t *ucp)
Ez a funkció átadja a vezérlést a kontextusnak ucp. A végrehajtás attól a ponttól folytatódik, ahol a kontextust tárolták ucp. Sikeres esetben a visszatérés innen setcontextnem történik meg.
  • int getcontext(ucontext_t *ucp)
Tárolja az aktuális kontextust a következőben ucp: Ez a függvény két esetben tér vissza: a kezdeti hívás után, vagy amikor a szál a vagy gombbal a kontextusba ucpvált . A függvény nem ad visszatérési értéket ezeknek az eseteknek az elkülönítésére (csak hibajelentésre szolgál), ezért a fejlesztőnek kifejezetten a regisztermódosító nélkül és a volatilis módosítóval deklarált jelzőváltozót kell használnia, hogy elkerülje az állandó kifejezéshajtogatást és a fordító egyéb optimalizálását . .setcontextswapcontextgetcontext
  • void makecontext(ucontext_t *ucp, void *func(), int argc, ...)
A funkció makecontextegy alternatív vezérlési folyamatot állít be ucp, amelyet korábban a -val inicializáltak getcontext. A mezőnek ucp.uc_stacka kívánt méretű köteg helyére kell mutatnia; állandót szoktak használni SIGSTKSZ. ucpHa a setcontextvagy gombbal ugrik , a végrehajtás az argumentumok számával rendelkező függvény belépési pontjánswapcontext kezdődik . Ha elkészült , a vezérlés átkerül a következőre .funcargcfuncucp.uc_link
  • int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
Átadja a vezérlést ucpés eltárolja az aktuális végrehajtási állapotot a oucp.

Példa

Az alábbi példa egy iterátort mutat be, amely a következővel van megvalósítva setcontext. Az ilyen kód meglehetősen ritka; ahelyett, hogy setcontextkooperatív többfeladatos munkavégzés megvalósítására használnák, gyakran különféle burkolókönyvtárakat használnak , például a GNU Portable Threads -t .

#include <stdio.h> #include <stdlib.h> #include <ucontext.h> /* Iterátor funkció. A * swapcontext meghívásakor először kerül megadásra, majd 0-tól 9-ig vált. Minden érték tárolódik * i_from_iterator, majd a swapcontext segítségével visszatér a fő ciklusba. * A fő hurok kiírja az értéket, és meghívja a swapcontextet, hogy visszatérjen a * függvényhez. Amikor elérjük a ciklus végét, a végrehajtás átvált a main_context1*/ kontextusra. üres hurok ( ucontext_t * loop_context , ucontext_t * other_context , int * i_from_iterator ) { int i ; for ( i = 0 ; i < 10 ; ++ i ) { /* Írja be a ciklusszámlálót az iterátor visszatérési helyére. */ * i_from_iterator = i ; /* Tárolja a hurokkörnyezetet a "loop_context"-ben, és váltson másik kontextusra. */ swapcontext ( loop_context , other_context ); } } int main ( érvénytelen ) { /* Három kontextus: * (1) main_context1 : a főre mutat, hogy visszatérjen a ciklusból. * (2) main_context2 : a kontextuskapcsoló helyére mutat a főben * (3) loop_context : arra a helyre mutat a ciklusban, ahol a vezérlés * ugrik a mainről. */ ucontext_t main_context1 , main_context2 , loop_context ; /* Verem az iterátor funkcióhoz. */ char iterator_stack [ SIGSTKSZ ]; /* Az iterátor befejezését jelző jelző. */ volatile int iterator_finished ; /* Az iterátor visszatérési értéke. */ volatile int i_from_iterator ; /* Inicializálja az iterátor környezetet. Az uc_link a main_context1-re mutat, * a visszatérési pont az iterátor végén. */ loop_context . uc_link = & main_context1 ; loop_context . uc_stack . ss_sp = iterator_stack ; loop_context . uc_stack . ss_size = sizeof ( iterator_stack ); getcontext ( & loop_context ); /* A loop_context feltöltése, lehetővé téve a swapcontext számára a ciklus elindítását. * A (void (*)(void))-re való átalakítás azért szükséges, hogy elkerüljük a * fordítói figyelmeztetést, és nincs hatással a függvény viselkedésére. */ makecontext ( & loop_context , ( void ( * )( void )) loop , 3 , & loop_context , & main_context2 , & i_from_iterator ); /* Törölje a befejezés jelzőjét. */ iterator_befejezett = 0 ; /* Tárolja az aktuális környezetet a main_context1-ben. Amikor a * ciklus véget ér, a vezérlés visszatér erre a pontra. */ getcontext ( & main_context1 ); if ( ! iterator_finished ) { /* Állítsa be az iterator_finished jelzőt az iterátor újraindításának letiltásához. */ iterator_befejezett = 1 ; míg ( 1 ) { /* Tárolja ezt a pontot a main_context2-ben, és váltson át iterátorra. * Az első hívás elindítja a ciklust, a továbbiak a swapkontextuson keresztül * átváltanak a ciklusba. */ swapcontext ( & main_context2 , & loop_context ); printf ( "%d \n " , i_from_iterator ); } } return 0 ; }

Megjegyzés: Ez a példa nem egyezik a specifikáció hivatkozási oldalával [1] . A függvény makecontexttovábbi paramétereket igényel int, és a példában a mutatók átadásra kerülnek. Ez hibához vezethet 64 bites platformokon (különösen az LP64 architektúrákon , ahol sizeof(void*) > sizeof(int)). Elméletileg ezek a problémák megoldhatók, de ezek a megoldások sem hordozhatóak.

Jegyzetek

  1. Az Open Group Base Specifications 6. kiadás IEEE Std 1003.1, 2004-es kiadás . Hozzáférés dátuma: 2010. július 30. Az eredetiből archiválva : 2010. december 9.

Linkek