Egloos | Log-in
F/OSS study
F/OSS study
[C] nonlocal goto (setjmp/longjmp)
arch: x86
glibc: 2.10.1


setjmp/longjmp는 프로그램의 흐름을 비정상적으로 제어하는 것으로 이른바 nonlocal goto라고 불린다.
nonlocal이라고 하는 것은 일반 goto와 달리 함수 밖의 위치로 이동할 수 있기 때문이다.
하지만 아무런 제한없이 아무 함수로나 움직일 수 있는 것은 아니며
이동할 위치의 stack frame이 보존된 상태여야 한다.
(즉, 해당 함수가 아직 실행되고 있는 상태에야 한다.)
일반적으로 중첩된 함수 호출내에서 예외 상황이 발생한 경우
상위의 예외 처리 루틴으로 바로 제어를 넘겨서 실행할 수 있도록 구현할 수 있다.

실행을 시작할 위치는 setjmp()로 지정할 수 있다.
인자로 주어진 jmp_buf 타입은 opaque type으로 해당 위치의 실행 환경 정보를 저장한다.
이는 각 아키텍처에 따라 다를 수 있으며 glibc에는 다음과 같이 정의되어 있다.

/* Calling environment, plus possibly a saved signal mask.  */
struct __jmp_buf_tag
  {
    /* NOTE: The machine-dependent definitions of `__sigsetjmp'
       assume that a `jmp_buf' begins with a `__jmp_buf' and that
       `__mask_was_saved' follows it.  Do not move these members
       or add others before it.  */
    __jmp_buf __jmpbuf;        /* Calling environment.  */
    int __mask_was_saved;    /* Saved the signal mask?  */
    __sigset_t __saved_mask;    /* Saved signal mask.  */
  };

typedef struct __jmp_buf_tag jmp_buf[1];

역사적인 이유로 인해 jmp_buf 타입은 배열로 정의되며
따라서 jmp_buf 타입은 포인터가 아닌 타입 자체를 다루도록 API에 명시된다.

이 중 __jmp_buf 타입이 실제 아키텍처 별 정보를 담는 부분으로
x86의 경우는 단지 레지스터 정보를 담기 위한 6개의 정수(int) 배열로 구성된다.

typedef int __jmp_buf[6];

#define JB_BX    0
#define JB_SI    1
#define JB_DI    2
#define JB_BP    3
#define JB_SP    4
#define JB_PC    5

#define JB_SIZE 24

위에서 보듯이 ebx, esi, edi 레지스터와 현재의 pc 값 및 스택 정보 (esp, ebp)가 저장된다.
이를 제외한 다른 레지스터의 값들은 longjmp() 호출 후에도 그대로 보존되며
스택 포인터는 setjmp() 당시의 값으로 복원되고 실행도 setjmp()에서 반환된 것처럼 다시 시작한다.
주의할 점은 스택의 내용 자체가 setjmp 당시의 상황으로 복원되는 것은 아니라는 것이다.
longjmp() 후 setjmp() 위치로 복원된 상황에서도 지역 변수는 그 사이 변경된 값을 보존하고 있다.

setjmp()는 최초 호출 시 (즉, longjmp()를 통한 이동 위치를 설정하는 경우)에는 0을 반환하지만
longjmp()를 통해 반환되는 경우에는 longjmp()의 두번째 인자로 주어진 값을 반환한다.
따라서 longjmp()의 두번째 인자는 반드시 0 이외의 값으로 설정해야 하며 (보통은 이를 통해 예외 상황을 구분한다)
강제로 0을 쓰는 경우에는 시스템이 자동으로 1로 바꾸어 버린다.

간단한 예제를 통해 setjmp/longjmp 사용법을 살펴보기로 하자.
여기서는 매우 단순한 (그리고 안전하지 않은) 예외 처리를 구현해 보기로 한다.
(보다 자세한 내용은 아래의 참고 문헌을 살펴보기 바란다.)

먼저 다음과 같이 구현된 함수들이 있다고 가정한다.

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <unistd.h>

#define FILE_ERROR  1
#define MEM_ERROR   2

jmp_buf jmpbuf;


FILE * find_file(const char *fname)
{
  FILE *f;
 
  if (access(fname, F_OK))
    longjmp(jmpbuf, FILE_ERROR);

  f = fopen(fname, "r");
  if (f == NULL)
    longjmp(jmpbuf, FILE_ERROR);

  return f;
}

void read_file(FILE *f, int size)
{
  char *buf;
 
  if (size < 0)
    longjmp(jmpbuf, MEM_ERROR);

  buf = malloc(size);
  if (buf == NULL)
    longjmp(jmpbuf, MEM_ERROR);

  fread(buf, size, 1, f);

  /* do something */
 
  free(buf);
}

위에서 보았듯이 함수들은 에러가 발생하는 상황에서 longjmp() 호출하게 되어 있다.
아래의 main() 함수에서는 setjmp()를 호출하여 이들이 도달할 위치를 설정한다.

int main(int argc, char *argv[])
{
  int res;
  int step;
  FILE *fp;

  /* Usage: ./a.out <filename> <bufsize> */

  step = -1;
  if ((res = setjmp(jmpbuf)) == 0) {
    step = 0;
    fp = find_file(argv[1]);
    step = 1;
    read_file(fp, strtol(argv[2], NULL, 10));
    step = 2;
    fclose(fp);
  }
  else if (res == FILE_ERROR) {
    printf("file exception: %s\n", argv[1]);
  }
  else if (res == MEM_ERROR) {
    printf("memory exception: %s\n", argv[2]);
    fclose(fp);
  }

  printf("last step = %d\n", step);
  return res;
}

setjmp()가 0을 리턴하는 첫번째 경우가 try 블록에 해당한다.
나머지 else if 들은 각각 해당 에러를 처리하는 catch 블록과 동일하다.
다음은 위 예제 프로그램의 실행 결과를 보여준다.

$ ./a.out a.out 100
last step = 2
$ ./a.out a.out -10
memory exception: -10
last step = 1
$ ./a.out b.out 100
file exception: b.out
last step = 0


=== 참고 문헌 ===

by namhyung | 2010/03/22 00:52 | System | 트랙백 | 덧글(3)
트랙백 주소 : http://studyfoss.egloos.com/tb/5275830
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by yoshiman at 2011/12/12 16:17
좋은글 잘보고 갑니다. PL 시험공부중이었는 데 도움 많이 되었습니다
Commented by namhyung at 2011/12/24 19:30
도움이 되셨다니 다행입니다.
Commented by 나그네 at 2014/03/13 18:43
정리 잘되있네요~ 잘보고갑니다

:         :

:

비공개 덧글

◀ 이전 페이지 다음 페이지 ▶

카테고리
General
Application
System
Kernel
Book
Tips
태그
x86 compiler memory algorithm CARM SMP scm glibc blktrace bash C documentation linux CAaQA3 awk build synchronization git computer-architecture patch emacs block-layer vcs binutils elf kernel perf script sed gcc
전체보기
이글루 파인더

최근 등록된 덧글
informsi yang bagus dan b..
by agen qnc at 06/22
informasi yang bagus dan b..
by agen qnc at 06/22
informasi yang bagus dan b..
by agen qnc at 06/22
최근 등록된 트랙백
Tod's Ferrari Homme
by Tods Pas Cher,Kodak did ..
Mocassin Femme
by Mocassins Homme, I got so..
natural garcinia cambogia
by
rss

skin by jiinny


X