Superkkt Blog

Signal 다루기

2007/05/06 11:00

아래는 시그널을 다룰때 조심해야할 세가지 사항이다.

1. POSIX 함수가 시그널에 의해 인터럽트 되었을때, 재실행 해야하는지?
2. 시그널 핸들러에서 재진입(re-entrant) 할 수 없는 함수를 호출해서 생기는 문제
3. 시그널 핸들러에서 errno를 셋팅하는 함수를 호출했을때 errno가 overwrite되는 문제

프로세스가 라이브러리 함수를 호출하고 있는 도중에 시그널이 발생하면 어떻게 될까? 이것은 전적으로 실행되는 함수의 종류에 따라 달라진다. 함수의 종류는 read()와 write()처럼 블럭되는 함수들과 getpid()처럼 블럭되지 않는 함수로 크게 분류 할 수 있겠다. 블럭되지 않는 함수들은 시그널에 의해 인터럽트되지 않는다. 그러나 블럭되는 함수들은 시그널에 의해 인터럽트 될 수 있다. 사용하려는 함수가 시그널의 영향을 받는지 확인하려면 해당 함수의 메뉴얼의 에러 섹션에 EINTR 에러가 셋팅 될 수 있는지 확인해보면 된다.

블럭되는 함수가 시그널에 의해 인터럽트 되는것은 사용자가 시그널을 이용해서 블럭된 함수를 취소 할 수 있도록 하기위함이였다고 한다. 예를들어 read() 호출 전에 alarm(5)를 설정하면 5초 동안 read()가 블럭될 경우 EINTR과 함께 리턴한다. 그러나 이런 방식은 프로그램에 불필요한 복잡성을 추가하는 결과를 낳게 되었고, 이로 인해  POSIX 위원회는 새로운 함수는 절대로 errno에 EINTR을 설정하지 못하도록 결정했다고 한다. 그 예로 pthread_join()이나 pthread_cond_wait() 처럼 블럭되는 함수의 메뉴얼을 봐도 에러 섹션에 EINTR에 대한 내용이 없다.

시그널은 비동기적으로 발생한다. 즉, 이 말은 시그널 핸들러는 언제 실행될지 모른다는 것이다. 만약 프로그램이 재진입이 불가능한 함수(예를들어 strtok())를 호출하고 있는데 시그널을 받았다고 가정해보자. 그래서 실행된 시그널 핸들러에서 또 strtok()를 호출하게 되면 어떻게 될까? 시그널 핸들러가 리턴되고 원래 프로그램이 실행되는 위치로 되도록아가서 처음의 strtok()가 다시 실행되는 경우 정상 작동을 하지 않을것이다.

errno는 전역변수이다. 만약 프로그램에서 에러가 발생해서 에러 메세지를 출력하기 바로 직전에 시그널을 받아서 시그널 핸들러가 실행되었다고 가정해보자. 시그널 핸들러 안에서 에러 발생시 errno를 셋팅하는 함수를 호출했는데 여기서도 에러가 발생한 경우, 원래 프로그램의 errno를 overwrite하게 된다. 따라서 시그널 핸들러가 리턴하고 원래 프로그램이 에러 메세지를 출력하면 원래 발생한 에러와는 다른 엉뚱한 메세지가 찍힐것이다. 이 문제를 해결하기 위해 시그널 핸들러는 루틴 처음에 errno를 임시 변수에 저장하고, 리턴하기 전에 다시 errno를 복구해주도록 해야 한다.

시그널에 안전한, 재진입이 가능하거나 시그널에 의해 인터럽트 되지 않는, 함수 리스트는 아래 링크를 참조하도록 한다. 단, 아래 리스트에 있은 함수 목록에 read()와 write()가 있는걸 주의하자. 즉, 시그널에 의해 인터럽트가 되는 함수라고해서 무조건 시그널에 안전하지 않은것은 아니다. read()와 write()의 메뉴얼을 보면 시그널에 인터럽트 될때(EINTR errno로 리턴할때)는 전송되는 데이터는 없다고 명시되어 있다. 이 말은 이 함수들은 재진입이 가능하다는 의미이다.

List of functions that will be reentrant or non-interruptible by signal
2007/05/06 11:00 2007/05/06 11:00

trackbacks

trackbacks rss

이 글에는 트랙백을 보낼 수 없습니다

Leave a Comment