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