NAME

makecontext, swapcontext - 사용자 문맥 조작하기

SYNOPSIS

#include <ucontext.h>

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
int swapcontext(ucontext_t *restrict oucp,
                const ucontext_t *restrict ucp);

DESCRIPTION

시스템 V 계열 환경에는 (<ucontext.h>에 정의돼 있고 getcontext(3)에서 설명하는) ucontext_t라는 타입이 있으며 네 가지 함수 getcontext(3), setcontext(3), makecontext(), swapcontext()를 통해 한 프로세스 내의 여러 제어 스레드들 사이에서 사용자 수준 문맥 전환이 가능하다.

makecontext() 함수는 (getcontext(3) 호출에서 얻은) ucp가 가리키는 문맥을 변경한다. makecontext()를 호출하기 전에 호출자는 이 문맥을 위한 새 스택을 할당하여 그 주소를 ucp->uc_stack에 지정해야 하며 후속 문맥을 정의하여 그 주소를 ucp->uc_link에 지정해야 한다.

이후에 (setcontext(3)swapcontext()를 이용해) 이 문맥을 활성화할 때 함수 func가 호출되면서 argc 다음에 오는 일련의 정수(int) 인자들이 전달된다. 호출자는 이 인자들의 개수를 argc에 지정해야 한다. 이 함수가 반환하고 나면 후속 문맥이 활성화된다. 후속 문맥 포인터가 NULL이면 스레드가 끝난다.

swapcontext() 함수는 oucp가 가리키는 구조체에 현재 문맥을 저장한 다음 ucp가 가리키는 문맥을 활성화한다.

RETURN VALUE

성공 시 swapcontext()는 반환하지 않는다. (하지만 나중에 oucp가 활성화될 때 돌아올 수도 있으며, 그 경우 swapcontext()가 0을 반환하는 것처럼 보인다.) 오류 시 swapcontext()는 -1을 반환하며 오류를 나타내도록 errno를 설정한다.

ERRORS

ENOMEM
남은 스택 공간이 충분하지 않음.

VERSIONS

glibc에서 버전 2.1부터 makecontext()swapcontext()를 제공한다.

ATTRIBUTES

이 절에서 사용하는 용어들에 대한 설명은 attributes(7)를 보라.

인터페이스 속성
makecontext() 스레드 안전성 MT-Safe race:ucp
swapcontext() 스레드 안전성 MT-Safe race:oucp race:ucp

CONFORMING TO

SUSv2, POSIX.1-2001, POSIX.1-2008에서 이식성 문제를 이유로 makecontext()swapcontext() 명세를 제거하였으며 대신 POSIX 스레드를 사용하게 응용을 재작성하기를 권고하고 있다.

NOTES

ucp->uc_stack의 해석은 sigaltstack(2)에서와 마찬가지이다. 즉, 스택의 성장 방향과 무관하게 이 구조체에는 스택으로 사용할 메모리 영역의 시작점과 길이를 담는다. 따라서 사용자 프로그램이 그 방향에 대해 신경쓸 필요가 없다.

int와 포인터 타입이 같은 크기인 아키텍처에서는 (가령 x86-32에서는 두 타입 모두 32비트임) makecontext()에서 argc 다음에 오는 인자들로 포인터를 전달하는 것이 어찌어찌 가능할 수도 있다. 하지만 이식성이 보장되지 않고 표준에 따르면 규정되어 있지 않으며 포인터가 int보다 큰 아키텍처에서 동작하지 않게 된다. 그럼에도 불구하고 glibc에서 버전 2.8부터 makecontext()를 조금 변경해서 일부 64비트 아키텍처들(가령 x86-64)에서 그렇게 하는 것을 허용한다.

EXAMPLES

아래 예시 프로그램은 getcontext(3), makecontext(), swapcontext() 사용 방식을 보여 준다. 프로그램을 실행하면 다음 결과가 나온다.

$ ./a.out
main: swapcontext(&uctx_main, &uctx_func2)
func2: started
func2: swapcontext(&uctx_func2, &uctx_func1)
func1: started
func1: swapcontext(&uctx_func1, &uctx_func2)
func2: returning
func1: returning
main: exiting

프로그램 소스

#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>

static ucontext_t uctx_main, uctx_func1, uctx_func2;

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

static void
func1(void)
{
    printf("func1: started\n");
    printf("func1: swapcontext(&uctx_func1, &uctx_func2)\n");
    if (swapcontext(&uctx_func1, &uctx_func2) == -1)
        handle_error("swapcontext");
    printf("func1: returning\n");
}

static void
func2(void)
{
    printf("func2: started\n");
    printf("func2: swapcontext(&uctx_func2, &uctx_func1)\n");
    if (swapcontext(&uctx_func2, &uctx_func1) == -1)
        handle_error("swapcontext");
    printf("func2: returning\n");
}

int
main(int argc, char *argv[])
{
    char func1_stack[16384];
    char func2_stack[16384];

    if (getcontext(&uctx_func1) == -1)
        handle_error("getcontext");
    uctx_func1.uc_stack.ss_sp = func1_stack;
    uctx_func1.uc_stack.ss_size = sizeof(func1_stack);
    uctx_func1.uc_link = &uctx_main;
    makecontext(&uctx_func1, func1, 0);

    if (getcontext(&uctx_func2) == -1)
        handle_error("getcontext");
    uctx_func2.uc_stack.ss_sp = func2_stack;
    uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
    /* argc > 1 아니면 후속 문맥이 func1() */
    uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1);
    makecontext(&uctx_func2, func2, 0);

    printf("main: swapcontext(&uctx_main, &uctx_func2)\n");
    if (swapcontext(&uctx_main, &uctx_func2) == -1)
        handle_error("swapcontext");

    printf("main: exiting\n");
    exit(EXIT_SUCCESS);
}

SEE ALSO

sigaction(2), sigaltstack(2), sigprocmask(2), getcontext(3), sigsetjmp(3)


2021-03-22