개요
리눅스·유닉스에서 **시그널(Signal)**은 프로세스에 비동기 이벤트를 전달하는 메커니즘이다. 커널·다른 프로세스·키보드 인터럽트 등에서 발생한 시그널을 프로세스가 받으면, 기본 동작(종료·코어 덤프·무시 등)이 수행되거나, 개발자가 등록한 Custom Signal Handler가 호출된다. 이 글에서는 **sigaction(2)**을 사용해 시그널 핸들러를 등록하는 방법, 시그널의 종류와 기본 동작, async-signal-safe 함수 사용의 중요성, 그리고 실전 예제를 다룬다.
이 글을 통해 얻을 수 있는 것
- 시그널의 종류와 기본 동작(terminate, core, ignore, stop, continue) 이해
sigaction구조체와SA_SIGINFO플래그를 이용한 확장 정보 수신- 시그널 핸들러 내에서 호출해도 안전한 함수만 사용해야 하는 이유와 목록 참조 방법
- 실전에서 바로 쓸 수 있는 C 예제 코드
시그널의 종류
리눅스는 **표준 시그널(Standard signals)**과 **실시간 시그널(Real-time signals, SIGRTMIN~SIGRTMAX)**을 지원한다. 표준 시그널 중 자주 쓰이는 것만 정리하면 다음과 같다.
| 시그널 | 기본 동작 | 설명 |
|---|---|---|
| SIGINT | Term | 키보드 인터럽트 (Ctrl+C) |
| SIGQUIT | Core | 키보드 Quit (Ctrl+\) |
| SIGILL | Core | 잘못된 명령어 |
| SIGABRT | Core | abort(3) 호출 |
| SIGFPE | Core | 산술 예외 (0으로 나누기 등) |
| SIGSEGV | Core | 잘못된 메모리 참조 (Segmentation fault) |
| SIGPIPE | Term | 읽는 쪽이 없는 파이프에 쓰기 |
| SIGALRM | Term | alarm(2) 타이머 만료 |
| SIGTERM | Term | 종료 요청 (기본 kill) |
| SIGCHLD | Ign | 자식 프로세스 상태 변경 |
| SIGUSR1, SIGUSR2 | Term | 사용자 정의 (의미 없음, 앱에서 정의) |
| SIGKILL | Term | 강제 종료 (캐치·블록·무시 불가) |
| SIGSTOP | Stop | 일시 정지 (캐치·블록·무시 불가) |
기본 동작: Term(프로세스 종료), Core(종료 + 코어 덤프), Ign(무시), Stop(정지), Cont(정지 해제).
시그널 처리 흐름
시그널이 발생하면 커널이 해당 프로세스(또는 스레드)에 전달하고, 등록된 disposition에 따라 기본 동작을 수행하거나 사용자 핸들러를 호출한다. 흐름을 단순화하면 아래와 같다.
flowchart LR
subgraph sourceGroup["발생 원인"]
kernelNode["커널/예외"]
otherProc["다른 프로세스"]
selfRaise["raise / 자기 자신"]
end
subgraph processGroup["프로세스"]
maskCheck["시그널 마스크확인"]
dispCheck["disposition확인"]
defaultAction["기본 동작수행"]
handlerCall["CustomHandler 호출"]
end
kernelNode --> maskCheck
otherProc --> maskCheck
selfRaise --> maskCheck
maskCheck -->|"차단되지 않음"| dispCheck
dispCheck -->|"SIG_DFL"| defaultAction
dispCheck -->|"핸들러 등록됨"| handlerCall
handlerCall -->|"return"| trampolineNode["sigreturn트램펄린"]
핸들러는 비동기로 호출될 수 있으므로, 실행 중이던 코드(시스템 콜·라이브러리 함수)가 언제든 중단된 상태에서 핸들러가 실행된다. 이 점이 async-signal-safe 제약의 이유다.
sigaction을 사용하여 Custom Signal Handler 등록하기
signal(2)보다 이식성과 제어력이 좋은 **sigaction(2)**으로 핸들러를 등록한다.
구조체와 플래그
- sa_handler / sa_sigaction: 핸들러 함수 포인터.
SA_SIGINFO를 쓰면sa_sigaction이 사용되며, 시그널 번호·siginfo_t·ucontext_t를 인자로 받는다. - sa_mask: 핸들러 실행 동안 블록할 시그널 집합.
- sa_flags: 동작 수정. 자주 쓰는 것:
- SA_SIGINFO: 핸들러에
(int signo, siginfo_t *info, void *ucontext)전달. - SA_RESTART: 일부 시스템 콜이 시그널 후 자동 재시작.
- SA_NODEFER: 핸들러 실행 중 같은 시그널이 다시 전달될 수 있게 함(재진입 시 유의).
- SA_SIGINFO: 핸들러에
등록 절차
struct sigaction을 0으로 초기화.sa_sigaction(또는sa_handler)에 핸들러 함수 지정.sa_flags에SA_SIGINFO등 필요한 플래그 설정.sigaction(signum, &act, NULL)(또는 기존 동작 저장용으로oldact사용) 호출.
아래 예제는 SIGSEGV에 대해 SA_SIGINFO | SA_UNSUPPORTED | SA_EXPOSE_TAGBITS로 등록한 뒤, 핸들러 안에서 커널이 해당 플래그를 지원하는지 확인하는 패턴이다. (Linux 5.11+ 플래그 탐지용)
async-signal-safe 함수를 사용해야 한다
시그널 핸들러는 메인 프로그램 실행의 임의 지점에서 비동기로 호출된다. 이때 일반 라이브러리 함수(예: printf, malloc, pthread_mutex_lock)를 호출하면, 내부 상태가 일관되지 않은 상황에서 재진입할 수 있어 미정의 동작이 된다.
- stdio:
printf등은 정적 버퍼와 인덱스를 쓰므로, 메인에서printf실행 중에 핸들러가 같은printf를 호출하면 버퍼가 꼬인다. - malloc: 힙 메타데이터가 손상될 수 있다.
따라서 시그널 핸들러 안에서는 async-signal-safe로 보장된 함수만 호출해야 한다. POSIX가 보장하는 목록은 **signal-safety(7)**에 있으며, _exit, write, sigaction, raise 등 제한된 집합만 안전하다. printf, malloc, free 등은 핸들러 내에서 호출하면 안 된다.
| 사용 가능(예) | 사용 금지(예) |
|---|---|
_exit, _Exit | exit, printf, malloc |
write(2) | printf, fprintf, puts |
sigaction, raise | pthread_mutex_lock, malloc |
자세한 목록과 주의사항은 signal-safety(7)를 참고한다.
실전 예제: SIGSEGV 핸들러와 플래그 지원 탐지
아래 코드는 SIGSEGV에 Custom Handler를 등록한 뒤 raise(SIGSEGV)로 발생시키고, 핸들러 안에서 sigaction(SIGSEGV, NULL, &oldact)으로 커널이 반환한 oldact.sa_flags를 확인해 SA_UNSUPPORTED·SA_EXPOSE_TAGBITS 지원 여부를 판별한다. 핸들러 내에서는 async-signal-safe인 sigaction, _exit만 사용한다.
| |
- 핸들러 안:
sigaction,_exit만 사용 → async-signal-safe 준수. - main:
perror,exit는 핸들러가 아닌 일반 흐름에서만 호출되므로 사용 가능.
정리
- 시그널은 비동기 이벤트 전달 메커니즘이며, 표준 시그널과 실시간 시그널이 있다.
- Custom Handler는 **sigaction(2)**으로 등록하고,
SA_SIGINFO로siginfo_t등을 받을 수 있다. - 핸들러 내부에서는 async-signal-safe 함수만 써야 하며, 목록은 **signal-safety(7)**을 참고한다.
- 실무에서는 핸들러를 짧게 유지하고, 가능하면 플래그·파일 디스크립터·원자적 변수 등으로 메인 루프에 알린 뒤, 메인에서 안전한 함수로 처리하는 패턴을 권장한다.
참고 문헌
- signal(7) - Linux manual page: 시그널 개요, 표준/실시간 시그널, disposition, 시그널 마스크.
- sigaction(2) - Linux manual page: 시그널 동작 설정·조회,
struct sigaction,sa_flags,siginfo_t. - signal-safety(7) - Linux manual page: 시그널 핸들러에서 호출 가능한 async-signal-safe 함수 목록과 주의사항.
![Featured image of post [Linux] Custom Signal Handler 만들기: sigaction과 안전한 시그널 처리](/post/2021-11-11-custom-signal-launcher/wordcloud_hu_2d0716e58ca3e597.webp)
![[Rust] Comprehensive Rust 무료 강의 정리 및 코스 구조](/post/2022-12-30-comprehensive-rust/wordcloud_hu_d1420ff38434cdb6.webp)
![[Hardware] LattePanda Alpha에 Ubuntu 16.04 LTS 설치 가이드](/post/2018-12-06-install-ubuntu-16.04-on-lattepanda/wordcloud_hu_fc536f8de2cbd4bf.webp)
![[RPM] Spec 파일에서 주석과 매크로 동시 사용 시 주의사항](/post/2021-11-24-rpm-spec-comments/wordcloud_hu_6d09ac09623081c7.webp)
![[.NET] 런타임별 Finalizer 호출 차이와 IDisposable 권장](/post/2021-04-27-distructor-called-by-runtime/wordcloud_hu_7b7dd809a5d4bdc3.webp)
![[Tutorial] Learn Prompting - 프롬프트 엔지니어링 무료 가이드 정리](/post/2022-12-30-learn-prompting/wordcloud_hu_6a9d105de4834753.webp)