2013년 8월 8일 목요일

fiber 예제

쓰레드는 OS가 실행 단위들을 스케쥴링을 해주는 것이며 fiber는 user level에서 특정 API르 이용하여 실행 단위를 user level에서 직접 스케쥴링을 하는 기법이다. 사용용도를 간단히 설명하면 비동기식  I/O를 사용하면서 짬짬이 다른 문맥을 수행하거나 단일 프로세스 구조에서 thread없이 단순히 문맥만 분리 시킴으로 queue구조를 운영할수도 있다.

아래 예제에서 사용된 API들은 glibc에서 기본으로 제공되는 함수이며 swapcontext()의 내부 로직은 user level에서 직접 프로세스 실행에 대한 레지스터값을 저장하고 복원하는 방법을 상요하여 문맥을 전환 하고 있다.

그러나 문맥전환이 일어하는 과정에서 추가적으로 signal 정보를 저장, 복원 하는 작업도 발생하게 되는데 이로 인하여 system call overhead가 걸려서 문맥전환이 생각보다 느리게 수행되는 단점도 존재한다.

막약 signal을 다룰필요가 없는 프로그램을 작성한다면 glibc의 swapcontext() 함수 내부에서 signal 정보를 저장, 복원 하는 부분만 삭제하게 될 경우 커널의 스케쥴링으로는 도저히 볼수없는 정말 환상적인 문맥전환 성능을 만날수있다.

#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <ucontext.h>

static ucontext_t main_ctx;

static ucontext_t test_ctxA;
static ucontext_t test_ctxB;

static uint64_t cnt = 0;

static uint64_t cnt_old = 0;

static void fiber_funcA(void)

{
    for (;;) {
        ++cnt;
        // Conext B 로 문맥전환
        swapcontext(&test_ctxA, &test_ctxB);
    }
}

static void fiber_funcB(void)

{
    for (;;) {
        ++cnt;
        // Conext A 로 문맥전환
        swapcontext(&test_ctxB, &test_ctxA);
    }
}

static void sighandler(int signal)

{
    printf("%lu \n", cnt - cnt_old);
    cnt_old = cnt;
    alarm(1);
}

int main(void)

{
    char stackA[2048];
    char stackB[2048];

    // 문맥전환 횟수 모니터링용 헨들러

    signal(SIGALRM, sighandler);
    alarm(1);

    // Conext A fiber 생성

    if (getcontext(&test_ctxA) < 0)
    {
        fprintf(stderr, "ERROR!!\n");
        return 1;
    }
    test_ctxA.uc_stack.ss_sp = stackA;
    test_ctxA.uc_stack.ss_size = sizeof(stackA);
    test_ctxA.uc_link = &main_ctx;
    makecontext(&test_ctxA, (void (*)(void))fiber_funcA, 0);

    // Conext B fiber 생성

    if (getcontext(&test_ctxB) < 0)
    {
        fprintf(stderr, "ERROR!!\n");
        return 1;
    }
    test_ctxB.uc_stack.ss_sp = stackB;
    test_ctxB.uc_stack.ss_size = sizeof(stackB);
    test_ctxB.uc_link = &main_ctx;
    makecontext(&test_ctxB, (void (*)(void))fiber_funcB, 0);

    // Conext Main 에서 Conext A로 문맥 전환

    swapcontext(&main_ctx, &test_ctxA);

    return 0;

}