콘텍스트 설정
setcontext setcontext는 컨텍스트 제어에 사용되는 Clibrary 함수 패밀리(getcontext, makecontext 및 swapcontext) 중 하나입니다.그setcontext
family를 사용하면 반복기, 파이버, Coroutine 등의 고급 제어 흐름 패턴을 C에 구현할 수 있습니다.setjmp/longjmp의 확장 버전으로 볼 수 있지만, setjmp/longjmp에서는 로컬이 아닌1개의 점프만 허용됩니다.setcontext
를 사용하면 각각 자체 스택을 가진 여러 개의 공동 스레드 제어가 가능합니다.
사양
setcontext
는 POSIX.1-2001 및 Single Unix Specification, version 2 에 지정되어 있습니다만, 모든 Unix 계열의 operating system이 이러한 기능을 제공하는 것은 아닙니다.POSIX.1-2004 에서는 이러한 기능이 폐지되어 POSIX.1-2008에서는 POSIX 스레드가 대체 가능한 것으로서 삭제되었습니다.IEEE 규격 1003.1, 2004년판 인용:[1]
ISO/IEC 9899:1999 표준을 이 규격에 통합함에 따라 ISO C 표준(하위 절 6.11.6)은 빈 괄호가 있는 기능 선언자의 사용을 청소년기의 특징으로 규정하는 것으로 밝혀졌다.따라서 기능 프로토타입을 사용하여 다음을 수행합니다.
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
ISO C 표준의 사춘기적 특징을 이용하고 있습니다.따라서 엄밀하게 준거한 POSIX 어플리케이션에서는 이 폼을 사용할 수 없습니다.따라서 getcontext(), makecontext() 및 swapcontext()의 사용은 objective로 표시됩니다.
ISO C 표준에서는 함수를 임의의 타입(정수, 데이터에 대한 포인터, 함수에 대한 포인터 및 복합 타입 포함)의 인수로 임의의 수(0 포함)로 호출하는 것을 나타내는 비 사춘기 함수 프로토타입을 지정할 수 없습니다.
정의들
기능 및 관련 유형은 에 정의되어 있습니다.ucontext.h
시스템 헤더 파일여기에는ucontext_t
4가지 기능이 모두 작동하는 유형:
유형화된 구조 { 콘텍스트_t *uc_link; sigset_t uc_masksmask; 스택_t uc_stack; 콘텍스트_t uc_mcontext; ... } 콘텍스트_t;
uc_link
현재 콘텍스트가 종료되었을 때 재개되는 콘텍스트를 가리킵니다.makecontext
(세컨더리 컨텍스트). uc_sigmask
는 컨텍스트에서 차단된 신호 세트를 저장하기 위해 사용됩니다.uc_stack
는 컨텍스트에서 사용되는 스택입니다. uc_mcontext
는 모든 레지스터와 CPU 플래그, 명령 포인터 및 스택포인터를 포함한 실행 스테이트를 저장합니다.mcontext_t
불투명한 타입입니다.
기능은 다음과 같습니다.
int setcontext(const ucontext_t *ucp)
- 이 함수는 의 컨텍스트에 제어를 전송합니다.
ucp
. 컨텍스트가 저장된 시점부터 실행이 계속됩니다.ucp
.setcontext
는 돌아오지 않습니다.
- 이 함수는 의 컨텍스트에 제어를 전송합니다.
int getcontext(ucontext_t *ucp)
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
- 그
makecontext
함수는 대체 제어 스레드를 설정한다.ucp
를 사용하여 이미 초기화되어 있습니다.getcontext
.그ucp.uc_stack
구성원은 적절한 크기의 스택을 가리켜야 합니다.상수SIGSTKSZ
는 일반적으로 사용됩니다.언제ucp
바로 사용할 수 있습니다.setcontext
또는swapcontext
에 의해 지시된 함수의 엔트리 포인트에서 실행이 시작됩니다.func
,와 함께argc
지정된 대로 인수를 지정합니다.언제func
종료, 제어가 에 반환됩니다.ucp.uc_link
.
- 그
int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
- 제어의 이전처
ucp
현재 실행 상태를 저장합니다.oucp
.
- 제어의 이전처
예
다음 예시는 반복기에서 다음을 사용하는 방법을 보여 줍니다.setcontext
.
#실패하다 <stdio.h> #실패하다 <stdlib.h> #실패하다 <콘텍스트>h> #실패하다 <고객명>님.h> /* 3가지 컨텍스트: * (1) main_context1 : 루프가 돌아오는 메인 포인트. * (2) main_context2 : 루프로부터의 제어가 이루어지는 메인 포인트 * 콘텍스트 전환에 의한 흐름. * (3) loop_context : 메인으로부터의 제어가 루프의 포인트 * 콘텍스트 전환에 의한 흐름.*/ 콘텍스트_t main_main1, main_param2, loop_module; /* 반복기 반환값입니다.*/ 휘발성의 인트 i_from_iterator; /* 이것은 반복기 기능입니다.첫 번째 호출 시 입력됩니다. * swapcontext 및 루프 범위는 0 ~9 입니다.각 값은 i_from_iterator에 저장됩니다. * 다음으로 메인루프로 돌아가기 위해 swapcontext를 사용합니다.메인 루프가 인쇄됩니다. * 값을 호출하여 swapcontext를 호출하여 함수로 되돌립니다.끝날 때 루프의 *에 도달하여 함수가 종료되고 실행이 * main_context1이 가리키는 컨텍스트.*/ 무효 고리( 콘텍스트_t *loop_module, 콘텍스트_t *other_module(기타_module), 인트 *i_from_iterator) { 인트 i; 위해서 (i=0; i < > 10; ++i) { /* 루프 카운터를 반복기 반환 위치에 씁니다.*/ *i_from_iterator = i; /* 루프 컨텍스트(코드의 이 포인트)를 "loop_context"에 저장합니다. * 및 other_switch로 전환합니다.*/ 콘텍스트를 교환하다(loop_module, other_module(기타_module)); } /* 함수는 발신 컨텍스트에 암묵적으로 접속되어 있습니다. * "setcontext(&loop_context->uc_link)";" */ } 인트 주된(무효) { /* 반복기 기능의 스택.*/ 차 리터레이터_스택[신호]; /* 반복이 완료되었음을 나타내는 플래그.*/ 휘발성의 인트 리터레이터_개요; 콘텍스트를 취득하다(&loop_module); /* 반복기 컨텍스트를 초기화합니다.uc_link는 main_link1, * 반복기 종료 시점으로 돌아갑니다.*/ loop_module.uc_link = &main_main1; loop_module.uc_stack.ss_sp = 리터레이터_스택; loop_module.uc_stack.ss_size = 크기(리터레이터_스택); /* swapcontext start loop이 되도록 loop_context를 입력합니다.그 * ( ( ( ( ( ( ) ) typecast )는 컴파일러의 경고를 피하기 위한 것입니다만, 이것은 * 기능의 동작과는 관련이 없습니다.*/ 콘텍스트를 작성하다(&loop_module, (무효 (*)(무효)) 고리, 3, &loop_module, &main_param2, &i_from_iterator); /* 완료된 플래그를 지웁니다.*/ 리터레이터_개요 = 0; /* 현재 콘텍스트를 main_context1에 저장합니다.루프가 끝나면 * 제어 흐름은 이 지점으로 돌아갑니다.*/ 콘텍스트를 취득하다(&main_main1); 한다면 (!리터레이터_개요) { /* 이전 getcontext가 다음과 같이 되도록 iterator_finished를 설정합니다. * uc_link 경유로 에 반환됩니다.상기의 조건이 false일 경우, * 반복기는 재시작되지 않습니다.*/ 리터레이터_개요 = 1; 하는 동안에 (1) { /* 이 포인트를 main_context2에 저장하고 반복기로 전환합니다. * 첫 번째 콜은 루프를 시작합니다.후속 콜은 로 전환됩니다. * 스왑 컨텍스트가 루프 상태입니다.*/ 콘텍스트를 교환하다(&main_param2, &loop_module); 인쇄물(%d\n", i_from_iterator); } } 돌아가다 0; }
메모: 이 예는 [1]올바르지 않지만 경우에 따라서는 올바르게 동작할 수 있습니다.함수makecontext
유형을 지정할 추가 매개 변수가 필요합니다.int
단, 이 예에서는 포인터를 전달합니다.따라서 64비트 머신(특히 LP64 아키텍처)에서는 이 경우)에서 실패할 수 있습니다.sizeof(void*) > sizeof(int)
이 문제는 64비트 값을 분할하여 재구성함으로써 해결할 수 있지만 이로 인해 성능 저하가 발생합니다.
int 타입과 포인터 타입이 같은 크기(예를 들어 x86-32)인 아키텍처에서는 argc 뒤에 makecontext()를 인수로서 건네주는 포인터를 회피할 수 있습니다.그러나, 이것을 하는 것은, 이식 가능한 것을 보증하는 것은 아니고, 표준에 따라서 정의되어 있지 않으며, 포인터가 ints보다 큰 아키텍처에서는 동작하지 않습니다.단, 버전 2.8 이후 glibc는 일부 64비트아키텍처(x86-64 등)에서 이를 허용하기 위해 에 몇 가지 변경을 가했습니다.
get 콘텍스트 및 set 콘텍스트의 경우 작은 콘텍스트가 편리합니다.
#실패하다 <stdio.h> #실패하다 <콘텍스트>h> #실패하다 <리스트 없음.h> 인트 주된(인트 argc, 컨스턴트 차 *argv[]){ 콘텍스트_t 맥락; 콘텍스트를 취득하다(&맥락); 놓다('헬로 월드'); 수면.(1); 콘텍스트 설정(&맥락); 돌아가다 0; }
컨텍스트가 프로그램카운터를 유지하기 때문에 무한 루프가 됩니다.