NAME
signal-safety - 비동기 시그널 안전 함수들
DESCRIPTION
비동기 시그널 안전(async-signal-safe) 함수는 시그널 핸들러 안에서 안전하게 호출할 수 있는 함수이다. 많은 함수들은 비동기 시그널 안전 함수가 아니다. 특히 재진입 가능하지 않은 함수들은 일반적으로 시그널 핸들러 안에서 호출하는 게 안전하지 않다.
모든 함수들이 비동기 시그널 안전이 아닌 stdio
라이브러리 구현을 생각해 보면 함수를 안전하지 않게 만드는 종류의 문제들을 금방 이해할 수 있다.
파일에 대해 버퍼링 I/O를 수행할 때 stdio
함수들에서는 정적 할당 데이터 버퍼와 함께 버퍼 내 데이터 양과 현재 위치를 기록하는 연계 카운터 및 인덱스를 (또는 포인터들을) 유지해야 한다. 주 프로그램이 printf(3) 같은 stdio
함수 호출 중에 있고 그 안에서 버퍼와 연관 변수들을 부분적으로 갱신했다고 하자. 만약 그 순간에 시그널 핸들러가 프로그램을 중단시키고 핸들러에서 다시 printf(3)를 호출하면 그 두 번째 printf(3) 호출은 일관적이지 않은 데이터로 동작하게 되며 결과가 예측 불가능하다.
안전하지 않은 함수의 문제를 피하려면 두 가지 선택지가 있다.
-
(a) 시그널 핸들러에서 비동기 시그널 안전 함수만 호출하고 (b) 시그널 핸들러 자체가 주 프로그램의 전역 변수들에 대해 재진입 가능이게 한다.
-
주 프로그램에서 안전하지 않은 함수를 호출하거나 시그널 핸들러에서도 접근하는 전역 데이터를 다룰 때 시그널 전달을 막는다.
일반적으로 두 번째 선택지는 여하한 복잡도의 프로그램에서도 어렵기 때문에 첫 번째 선택지를 취한다.
POSIX.1에서는 구현에서 비동기 시그널 안전으로 만들어야 하는 함수들을 명세한다. (어떤 구현에서는 더 많은 함수들에 안전한 구현을 제공할 수도 있다. 하지만 표준에서 요구하는 것이 아니므로 다른 구현에서는 같은 보장을 하지 않을 수도 있다.)
일반적으로 함수가 비동기 시그널 안전 건 재진입 가능하거나 시그널에 대해 원자적(즉 시그널 핸들러로 실행을 중단시킬 수 없음)이어서이다.
POSIX.1에서 비동기 시그널 안전이기를 요구하는 함수들이 아래 표에 있다. 별다른 언급이 없는 함수는 POSIX.1-2001에서 비동기 시그널 안전을 요구한 것이며, 후속 표준에서 바뀐 내용이 적혀 있다.
함수 | 참고 |
---|---|
abort(3) | POSIX.1-2001 TC1에서 추가 |
accept(2) |
|
access(2) | |
aio_error(3) | |
aio_return(3) | |
aio_suspend(3) | 아래 참고 |
alarm(2) | |
bind(2) |
|
cfgetispeed(3) | |
cfgetospeed(3) | |
cfsetispeed(3) | |
cfsetospeed(3) | |
chdir(2) | |
chmod(2) | |
chown(2) | |
clock_gettime(2) | |
close(2) | |
connect(2) |
|
creat(2) | |
dup(2) | |
dup2(2) | |
execl(3) | POSIX.1-2008에서 추가, 아래 참고 |
execle(3) | 아래 참고 |
execv(3) | POSIX.1-2008에서 추가 |
execve(2) | |
_exit(2) | |
_Exit(2) | |
faccessat(2) | POSIX.1-2008에서 추가 |
fchdir(2) | POSIX.1-2008 TC1에서 추가 |
fchmod(2) | |
fchmodat(2) | POSIX.1-2008에서 추가 |
fchown(2) | |
fchownat(2) | POSIX.1-2008에서 추가 |
fcntl(2) | |
fdatasync(2) | |
fexecve(3) | POSIX.1-2008에서 추가 |
ffs(3) | POSIX.1-2008 TC2에서 추가 |
fork(2) | 아래 참고 |
fstat(2) | |
fstatat(2) | POSIX.1-2008에서 추가 |
fsync(2) | |
ftruncate(2) | |
futimens(3) | POSIX.1-2008에서 추가 |
getegid(2) | |
geteuid(2) | |
getgid(2) | |
getgroups(2) | |
getpeername(2) | |
getpgrp(2) | |
getpid(2) | |
getppid(2) | |
getsockname(2) | |
getsockopt(2) |
|
getuid(2) | |
htonl(3) | POSIX.1-2008 TC2에서 추가 |
htons(3) | POSIX.1-2008 TC2에서 추가 |
kill(2) | |
link(2) | |
linkat(2) | POSIX.1-2008에서 추가 |
listen(2) |
|
longjmp(3) | POSIX.1-2008 TC2에서 추가, 아래 참고 |
lseek(2) | |
lstat(2) | |
memccpy(3) | POSIX.1-2008 TC2에서 추가 |
memchr(3) | POSIX.1-2008 TC2에서 추가 |
memcmp(3) | POSIX.1-2008 TC2에서 추가 |
memcpy(3) | POSIX.1-2008 TC2에서 추가 |
memmove(3) | POSIX.1-2008 TC2에서 추가 |
memset(3) | POSIX.1-2008 TC2에서 추가 |
mkdir(2) | |
mkdirat(2) | POSIX.1-2008에서 추가 |
mkfifo(3) | |
mkfifoat(3) | POSIX.1-2008에서 추가 |
mknod(2) | POSIX.1-2008에서 추가 |
mknodat(2) | POSIX.1-2008에서 추가 |
ntohl(3) | POSIX.1-2008 TC2에서 추가 |
ntohs(3) | POSIX.1-2008 TC2에서 추가 |
open(2) | |
openat(2) | POSIX.1-2008에서 추가 |
pause(2) | |
pipe(2) | |
poll(2) | |
posix_trace_event(3) |
|
pselect(2) | |
pthread_kill(3) | POSIX.1-2008 TC1에서 추가 |
pthread_self(3) | POSIX.1-2008 TC1에서 추가 |
pthread_sigmask(3) | POSIX.1-2008 TC1에서 추가 |
raise(3) | |
read(2) | |
readlink(2) | |
readlinkat(2) | POSIX.1-2008에서 추가 |
recv(2) | |
recvfrom(2) | |
recvmsg(2) | |
rename(2) | |
renameat(2) | POSIX.1-2008에서 추가 |
rmdir(2) | |
select(2) | |
sem_post(3) | |
send(2) | |
sendmsg(2) | |
sendto(2) | |
setgid(2) | |
setpgid(2) | |
setsid(2) | |
setsockopt(2) |
|
setuid(2) | |
shutdown(2) | |
sigaction(2) | |
sigaddset(3) | |
sigdelset(3) | |
sigemptyset(3) | |
sigfillset(3) | |
sigismember(3) | |
siglongjmp(3) | POSIX.1-2008 TC2에서 추가, 아래 참고 |
signal(2) | |
sigpause(3) | |
sigpending(2) | |
sigprocmask(2) | |
sigqueue(2) | |
sigset(3) | |
sigsuspend(2) | |
sleep(3) | |
sockatmark(3) | POSIX.1-2001 TC2에서 추가 |
socket(2) | |
socketpair(2) | |
stat(2) | |
stpcpy(3) | POSIX.1-2008 TC2에서 추가 |
stpncpy(3) | POSIX.1-2008 TC2에서 추가 |
strcat(3) | POSIX.1-2008 TC2에서 추가 |
strchr(3) | POSIX.1-2008 TC2에서 추가 |
strcmp(3) | POSIX.1-2008 TC2에서 추가 |
strcpy(3) | POSIX.1-2008 TC2에서 추가 |
strcspn(3) | POSIX.1-2008 TC2에서 추가 |
strlen(3) | POSIX.1-2008 TC2에서 추가 |
strncat(3) | POSIX.1-2008 TC2에서 추가 |
strncmp(3) | POSIX.1-2008 TC2에서 추가 |
strncpy(3) | POSIX.1-2008 TC2에서 추가 |
strnlen(3) | POSIX.1-2008 TC2에서 추가 |
strpbrk(3) | POSIX.1-2008 TC2에서 추가 |
strrchr(3) | POSIX.1-2008 TC2에서 추가 |
strspn(3) | POSIX.1-2008 TC2에서 추가 |
strstr(3) | POSIX.1-2008 TC2에서 추가 |
strtok_r(3) | POSIX.1-2008 TC2에서 추가 |
symlink(2) | |
symlinkat(2) | POSIX.1-2008에서 추가 |
tcdrain(3) | |
tcflow(3) | |
tcflush(3) | |
tcgetattr(3) | |
tcgetpgrp(3) | |
tcsendbreak(3) | |
tcsetattr(3) | |
tcsetpgrp(3) | |
time(2) | |
timer_getoverrun(2) | |
timer_gettime(2) | |
timer_settime(2) | |
times(2) | |
umask(2) | |
uname(2) | |
unlink(2) | |
unlinkat(2) | POSIX.1-2008에서 추가 |
utime(2) | |
utimensat(2) | POSIX.1-2008에서 추가 |
utimes(2) | POSIX.1-2008에서 추가 |
wait(2) | |
waitpid(2) | |
wcpcpy(3) |
POSIX.1-2008 TC2에서 추가 |
wcpncpy(3) |
POSIX.1-2008 TC2에서 추가 |
wcscat(3) |
POSIX.1-2008 TC2에서 추가 |
wcscat(3) |
POSIX.1-2008 TC2에서 추가 |
wcschr(3) |
POSIX.1-2008 TC2에서 추가 |
wcscmp(3) |
POSIX.1-2008 TC2에서 추가 |
wcscpy(3) |
POSIX.1-2008 TC2에서 추가 |
wcscspn(3) |
POSIX.1-2008 TC2에서 추가 |
wcslen(3) |
POSIX.1-2008 TC2에서 추가 |
wcsncat(3) |
POSIX.1-2008 TC2에서 추가 |
wcsncmp(3) |
POSIX.1-2008 TC2에서 추가 |
wcsncpy(3) |
POSIX.1-2008 TC2에서 추가 |
wcsnlen(3) |
POSIX.1-2008 TC2에서 추가 |
wcspbrk(3) |
POSIX.1-2008 TC2에서 추가 |
wcsrchr(3) |
POSIX.1-2008 TC2에서 추가 |
wcsspn(3) |
POSIX.1-2008 TC2에서 추가 |
wcsstr(3) |
POSIX.1-2008 TC2에서 추가 |
wcstok(3) |
POSIX.1-2008 TC2에서 추가 |
wmemchr(3) |
POSIX.1-2008 TC2에서 추가 |
wmemcmp(3) |
POSIX.1-2008 TC2에서 추가 |
wmemcpy(3) |
POSIX.1-2008 TC2에서 추가 |
wmemmove(3) |
POSIX.1-2008 TC2에서 추가 |
wmemset(3) |
POSIX.1-2008 TC2에서 추가 |
write(2) |
참고:
-
POSIX.1-2001과 POSIX.1-2001 TC2에서는 fpathconf(3), pathconf(3), sysconf(3)가 비동기 시그널 안전이기를 요구했지만 POSIX.1-2008에서 이 요구가 제거되었다.
-
안전하지 않은 함수의 실행이 시그널 핸들러로 중단되고, 그 핸들러가 longjmp(3)나 siglongjmp(3) 호출을 통해 끝나고, 이후 프로그램에서 안전하지 않은 함수를 호출하는 경우에 프로그램의 동작 방식이 규정되어 있지 않다.
-
POSIX.1-2001 TC1에서는 응용이 시그널 핸들러에서 fork(2)를 호출하고 pthread_atfork(3) 호출로 등록한 포크 핸들러에서 비동기 시그널 안전이 아닌 함수를 호출하는 경우 동작 방식이 규정되어 있지 않음을 분명히 하였다. 그 표준의 향후 리비전에서 fork(2)를 비동기 시그널 안전 함수 목록에서 제거할 가능성이 높다.
-
취소점인 함수를 호출하며 취소 연기 구간 상에 있는 비동기 시그널 핸들러가 취소를 유발할 수 있다. 비동기 취소가 발생한 것처럼 동작하게 되며 그로 인해 응용 상태의 무결성이 깨질 수 있다.
errno
시그널 핸들러에서 진입 시 errno
를 저장하고 반환 전 그 값을 복원시키면 errno
값을 가져오고 설정하는 것이 비동기 시그널 안전이다.
GNU C 라이브러리의 일탈
GNU C 라이브러리에 다음과 같은 표준 일탈이 있다.
-
glibc 2.24 전에서 execl(3)과 execle(3)이 내부적으로 realloc(3)을 이용했고 그로 인해 비동기 시그널 안전이 아니었다. glibc 2.24에서 수정되었다.
-
glibc의 aio_suspend(3) 구현이 내부적으로 pthread_mutex_lock(3)을 사용하기 때문에 비동기 시그널 안전이 아니다.
SEE ALSO
sigaction(2), signal(7), standards(7)
2021-03-22