시그널 핸들러
POSIX 시스템은 다른 구식 유닉스 체제에서 보다 약간 더 많은 시그널에 대해서 checking을 수행하며
다음과 같은 경우, 리눅스는 시그널 핸들러들(signal handler)를 호출시킴
* 타이머가 째깍댈 때마다 비동기적으로
* 모든 시스템 호출 반환 시에
* 다음과 같은 시스템 호출 동안에도 그러함.
select(), pause(), connect(), accept(), 터미널 상에서의 read(),
소켓/파이프나 라인 프린터, FIFO에 의한 open(), PTY나 시그널 라인,
터미널에 대한 ioctl(), F_SETLKW 명령을 내리는 fcntl(), wait4(), syslo(),
모든 TCP 또는 NFS 작업
* 위 외에도 다음에 시스템 콜들이 해당.
create(), getmsg(), putmsg(), msgrcv(), msgsnd(), recv(), send().
wait(), waitpid(), wait3(), tcdrain(), sigpause(), semop()
민약 시그널(프로그램에서 핸들러를 인스톨한 경우)이 시스템 호출 중에 발생한다면, 그에 대한
핸들러가 호출됨. 그리고 핸들러가 반환되면 (시스템 호출로), 시스템 호출은 중간에 가로채기를
당했는 지 살펴보고, 즉시 -1 값을 가지고 반환됨.
그리고 errno를 EINTR로 셋팅함.
다음 2 가지 해결책 중에 하나를 고르면 됨.
(1) 설치한 모든 시그널 핸들러에 대하여 SA_RESTART를 sigaction 플래그에 첨가
signal(sig_nr, my_signal_handler)
{
struct sigaction sa;
sigaction(sig_nr, (strucgt sigaction*)0, &sa);
#ifdef SA_RESTART
sa.sa_flag |= SA_RESTART;
#endif
#ifdef SA_INTERUPT
sa.sa_flag |= SA_INTERUPT;
#endif
sigaction(sig_nr, &sa, (struct sigaction*)0);
}
이 방법이 대부분의 시스템 호출에 적용되기는 하지만, read(), write(), ioctl(), select()
pause(), connect() 에 대해서는 직접 EINTR를 체크하여야 함.
(2) 직접 EINTR를 체크 (read()의 경우)
(예제 1)
(원래 코드)
int result;
while(len > 0)
{
result = read(fd, buffer, len);
if(result < 0) break;
buffer += result;
len -= result;
}
(변경된 코드)
int result;
while(len > 0)
{
reult = read(fd, buffer, len);
if(result < 0)
{
if(errno != EINTR) break;
}
else
{
buffer += result;
len -= result;
}
}
(예제 2)
(원래 코드)
int result;
result = ioctl(fd, cmd, addr);
(변경된 코드)
int result;
do
{
result = ioctl(fd, cmd, addr);
} while((result == 01) && (errno == EINTR));
BSD 유닉스의 몇몇 버전에서는 시스템 호출을 재개하는 것이 기본 행동으로 되어 있는 경우도 있으므로 주의
시스템 호출이 가로채기를 허용하기 위해서는 SV_INTERUPT 또는 SA_INTERUPT 플래그를 사용하도록 함.
위에서 언급한 내용을 쉽게 정리하면...
"시스템 콜 호출 실패 시 그리고, errno가 EINTR일 경우 (시스템 콜 처리 중간에 시그널이 발생한 경우)
에는 호출한 해당 시스템 콜을 errno가 EINTR가 아닐 때 까지 다시 호출해야 함"
URL : http://blog.daum.net/pss_notepad/31