Setcontext

setcontext este una dintre funcțiile standard ale bibliotecii POSIX (altele includ getcontext , makecontext și swapcontext ) utilizate pentru a gestiona contextul. Familia setcontextpermite lui C să implementeze modele de proiectare de control al fluxului, cum ar fi iteratoare , fibre și coroutine . Familia poate fi gândită ca o versiune extinsă ; în timp ce acesta din urmă permite doar un singur stack hop non-local, permite crearea de fluxuri de control multiple cooperante cu propriile stive . setjmp/longjmpsetcontext

Specificație

setcontextdefinit în POSIX .1-2001 și în a doua versiune a specificației UNIX unice , dar nu este disponibil pe toate sistemele de operare asemănătoare UNIX . Funcțiile și tipurile lor asociate sunt definite în fișierul antet ucontext.h . Acestea includ tipul ucontext_tcu care interacționează toate cele patru funcții:

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

uc_linkindică contextul care va fi restabilit la ieșirea din contextul curent dacă contextul a fost creat cu makecontext(context secundar). uc_sigmaskeste folosit pentru a stoca semnalele blocate în context și uc_stackeste stiva utilizată de context. uc_mcontextfolosit pentru a stoca starea de execuție, inclusiv toate registrele CPU , contorul de programe și indicatorul de stivă; mcontext_teste un indicator opac.

De asemenea, sunt definite următoarele funcții:

  • int setcontext(const ucontext_t *ucp)
Această funcție transferă controlul în context în ucp. Execuția continuă din punctul în care contextul a fost stocat în ucp. Dacă reușește, întoarcerea de la setcontextnu este efectuată.
  • int getcontext(ucontext_t *ucp)
Stochează contextul curent în ucp. Această funcție revine în două cazuri: după apelul inițial sau când firul de execuție comută în context ucpcu setcontextsau swapcontext. Funcția getcontextnu furnizează o valoare de returnare pentru a separa aceste cazuri (servește doar pentru a raporta o eroare), astfel încât dezvoltatorul trebuie să folosească în mod explicit o variabilă de tip flag declarată fără modificatorul de registru și cu modificatorul volatil pentru a evita plierea constantă a expresiei și alte optimizări ale compilatorului .
  • void makecontext(ucontext_t *ucp, void *func(), int argc, ...)
Funcția makecontextsetează un flux alternativ de control la ucp, inițializat anterior cu getcontext. Câmpul ucp.uc_stacktrebuie să indice un loc pentru un teanc de dimensiunea necesară; de obicei se folosește o constantă SIGSTKSZ. Când săriți ucpcu setcontextsau swapcontext, execuția începe la punctul de intrare al funcției funccu numărul de argumente argc. Când este finalizat func, controlul este transferat către ucp.uc_link.
  • int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
Transferă controlul ucpși stochează starea curentă de execuție în oucp.

Exemplu

Exemplul de mai jos arată un iterator implementat cu setcontext. Un astfel de cod este destul de rar; în loc să fie folosite setcontextpentru a implementa multitasking cooperativ, sunt adesea folosite diverse biblioteci de wrapper , de exemplu, GNU Portable Threads .

#include <stdio.h> #include <stdlib.h> #include <ucontext.h> /* Funcția de iterator. Se introduce prima dată când * swapcontext este apelat, apoi se ciclează de la 0 la 9. Fiecare valoare este stocată * i_from_iterator, după care revine la bucla principală folosind swapcontext. * Bucla principală tipărește valoarea și apelează swapcontext pentru a reveni * înapoi la funcție. Când se ajunge la sfârșitul buclei, execuția trece la contextul main_context1*/ buclă goală ( ucontext_t * loop_context , ucontext_t * alt_context , int * i_from_iterator ) { int i ; pentru ( i = 0 ; i < 10 ; ++ i ) { /* Scrieți contorul de bucle în locația de întoarcere a iteratorului. */ * i_from_iterator = i ; /* Stocați contextul buclei în ''loop_context'' și comutați la alt context. */ swapcontext ( bucla_context , alt_context ); } } int main ( void ) { /* Trei contexte: * (1) main_context1 : indică principalul pentru a reveni din buclă. * (2) main_context2 : indică locația comutatorului de context în main * (3) loop_context : indică locul din buclă unde controlul * va sări de la main. */ ucontext_t main_context1 , main_context2 , loop_context ; /* Stivă pentru funcția de iterator. */ char iterator_stack [ SIGSTKSZ ]; /* Flag care indică finalizarea iteratorului. */ volatile int iterator_finished ; /* Valoarea returnată a iteratorului. */ volatile int i_from_iterator ; /* Inițializați contextul iteratorului. uc_link indică spre main_context1, * punctul de întoarcere la sfârșitul iteratorului. */ 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 ); /* Populați loop_context, permițând swapcontext să pornească bucla. * Conversia în (void (*)(void)) este necesară pentru a evita avertismentul * compilatorului și nu afectează comportamentul funcției. */ makecontext ( & loop_context , ( void ( * )( void )) buclă , 3 , & loop_context , & main_context2 , & i_from_iterator ); /* Șterge indicatorul de finalizare. */ iterator_terminat = 0 ; /* Stochează contextul curent în main_context1. Când bucla * se termină, controlul va reveni în acel punct. */ getcontext ( & main_context1 ); dacă ( ! iterator_terminat ) { /* Setați indicatorul iterator_finished pentru a dezactiva repornirea iteratorului. */ iterator_terminat = 1 ; în timp ce ( 1 ) { /* Stocați acest punct în main_context2 și comutați la un iterator. * Primul apel începe bucla, cele ulterioare comută * prin contextul de swap în buclă. */ swapcontext ( & main_context2 , & loop_context ); printf ( "%d \n " , i_from_iterator ); } } returnează 0 ; }

Notă: Acest exemplu nu se potrivește cu pagina de referință a specificațiilor [1] . Funcția makecontextnecesită parametri suplimentari să fie de tip int, iar în exemplu, pointerii sunt transferați. Acest lucru poate duce la o eroare pe platformele pe 64 de biți (în special, pe arhitecturile LP64 , unde sizeof(void*) > sizeof(int)). Teoretic, aceste probleme pot fi rezolvate, dar nici aceste soluții nu sunt portabile.

Note

  1. Specificațiile de bază ale grupului deschis, numărul 6 IEEE Std 1003.1, ediția 2004 . Data accesului: 30 iulie 2010. Arhivat din original la 9 decembrie 2010.

Link -uri