8.6 Nonlocal Jumps
- C언어는 nonlocal jump라고 불리는 user level exceptional control flow를 제공한다.
- 이 nonlocal jump는 일반적인 call and return sequence를 거치지 않고 한 함수에서 다른 실행중인 함수로 직접 control을 옮겨주는 것이다.
- nonlocal jump는 setjmp, longjmp 함수에 의해 제공된다.
- setjmp 함수는 현재 호출했을 때의 상태를 env 버퍼에 저장하고 0을 반환한다.
- 이 상태는 나중에 longjmp 함수에 의해 사용된다.
- 호출했을 때의 상태는 PC, stack pointer, general-purpose 레지스터를 말한다.
- setjmp가 반환하는 값은 변수에 저장하면 안된다.
- 하지만 switch나 조건문에서 조건 테스트에는 사용할 수 있다.
- longjmp 함수는 env 버퍼에서 호출했을 때의 상태를 복구한다.
- 그리고 가장 최근에 호출된 setjmp를 return하게 한다.
- 그러면 setjmp는 0이 아닌 return 값 retval과 함께 반환된다.
- setjmp 함수는 한번 호출되지만 여러번 반환된다.
- 언제 반환되는가 하면
- setjmp가 처음 호출되고 env 버퍼에 호출되었을 때 상태를 저장할 때 0 반환
- longjmp 함수가 setjmp를 return하게 해서 retval 반환
- 반면에 longjmp 함수는 한번 호출되고 반환되지 않는다.
- nonlocal jump가 유용하게 쓰일 때는 깊게 중첩된 함수에서 error condition을 발견해서 즉시 return되는 것이다.
- 깊게 중첩된 함수에서 error condition이 발견되면 우리는 nonlocal jump를 이용해서 localized error handler로 바로 오는 것이다. 어렵게 호출 stack을 돌아오는 것이 아니라.
- 아래 그림이 nonlocal jump가 어떻게 작동하는지 보여준다.
- 그림 8.43 nonlocal jump 예제.
- 이 예제는 전체 스택을 풀지 않고도 깊이 중첩된 함수의 error condition에서 복구하기 위해 nonlocal jump를 사용하는 것을 보여준다.
- 메인 함수는 먼저 setjmp를 호출해서 현재 호출했을 때 상태를 저장하고 함수 foo를 호출한다.
- 그리고 foo 함수는 bar 함수를 호출한다.
- 만약에 foo나 bar 함수에서 error를 만나면 longjmp 호출로 인해서 바로 setjmp에서 반환된다.
- setjmp 반환 값이 0이 아니면 이것은 error type을 의미한다.
- 이것은 코드 어떤 부분에서 (handler 의미하는듯?) decode되고 handle된다.
- 모든 호출들을 뛰어넘는 이런 longjmp의 특징은 의도하지 않은 결과를 초래할 수 있다.
- 예를 들어 어떤 자료구조가 함수 호출 중간에 할당되어서 함수 마지막에 반환하려고 했다.
- 그러면 반환하는 코드가 skip되고 이것은 메모리 누수로 이어진다.
- nonlocal jump가 유용하게 사용되는 다른 예시는 signal이 도착해서 interrupt된 명령어로 돌아가는 것 대신 signal handler에서 특정 코드 위치로 가는 것이다.
- 아래 그림은 이런 기본 방법을 보여주는 코드다.
- 그림 8.44 사용자가 Ctrl+C를 입력하면 자신을 다시 시작하기 위해 nonlocal jump를 사용하는 프로그램.
- 이 프로그램은 signal과 nonlocal jump를 이용해서 user가 키보드로 ctrl+c 누를 때마다 soft restart 하는 것이다.
- sigsetjmp, siglongjmp 함수는 signal handler가 사용할 수 있는 setjmp, longjmp이다.
- 코드를 보자.
- sigsetjmp 함수가 처음 불린 것은 프로그램이 처음 시작할 때의 상태와 signal context (pending, blocked signal vector를 포함하는 것)을 저장하는 것이다.
- 그리고 메인 함수는 무한 루프에 들어가게 된다.
- 그리고 사용자가 ctrl+c 누르게 되면 kernel은 SIGINT signal을 프로세스에게 보내게 된다.
- 프로세스는 그 signal을 받고 interrupt된 프로세스에게 control을 다시 주는 signal handler에서 return하는 대신, signal handler는 메인 함수의 시작 부분으로 nonlocal jump를 한다.
- 프로그램을 실행하면 아래와 같이 되는 것이다.
- race condition을 피하기 위해서 sigsetjmp를 호출한 후에 handler를 설정해야 한다.
- 이렇게 하지 않으면 sigsetjmp의 처음 호출이 siglongjmp를 위해 호출했을 때의 상태를 설정하기 전에 handler가 실행되는 위험이 있을 수 있다.
- 왜 그런가 하면 siglongjmp가 임의의 코드로 jump할 수 있기 때문에 siglongjmp로 갈 수 있는 코드에서는 safe한 함수만 호출해야 한다.
- 이 예제에서는 안전한 sio_puts, sleep 함수를 호출한다.
- unsafe한 exit 함수는 siglongjmp로 갈 수 없다.sigsetjmp, siglongjmp가 async signal safe 함수 목록인 아래에 없다는 것을 알 수 있다.
- Software exceptions in C++ and Java
- C++ 및 Java에서 제공하는 예외 메커니즘은 C의 setjmp 및 longjmp 함수의 더 높은 수준이고 더 구조화된 버전이다.
- try 문 내부의 catch 절은 setjmp 함수와 유사하다고 생각하면 된다.
- 마찬가지로 throw 문은 longjmp 함수와 비슷하다.