[Linux] 고급 시그널(sigaction) 정리

mommers·2026년 2월 3일

Linux

목록 보기
34/59


고급시그널 처리-sigaction

signal() 함수의 불안정함(시스템별 동작 차이)을 해결하고, 핸들러 실행 중 '다른 시그널 차단' 등 정교한 제어가 가능한 표준 함수


1. sigaction을 써야 하는 이유

  • 표준화 : 모든 유닉스/리눅스 시스템에서 동일하게 동작함 (POSIX 표준)
  • 블록(Block) 기능 : 핸들러가 실행되는 동안 또 다른 특정 시그널이 끼어들지 못하게 막을 수 있음 (sa_mask)
  • 정보 제공 : 시그널을 누가 보냈는지(PID), 왜 보냈는지 등 상세 정보 확인 가능 (SA_SIGINFO)

2. 구조체 및 원형

C

#include <signal.h>

struct sigaction {
    void (*sa_handler)(int);           // 핸들러 함수 포인터
    void (*sa_sigaction)(int, siginfo_t *, void *); // 고급 핸들러
    sigset_t sa_mask;                  // 핸들러 실행 중 블록할 시그널들
    int sa_flags;                      // 동작 옵션
    void (*sa_restorer)(void);         // 내부용 (사용 안 함)
};

void advanced_handler(int signo, siginfo_t *info, void *context) {
    printf("시그널: %d\n", signo);
    printf("보낸 프로세스 PID: %d\n", info->si_pid);
    printf("보낸 UID: %d\n", info->si_uid);
}

sa.sa_sigaction = advanced_handler;
sa.sa_flags = SA_SIGINFO;  // 이 플래그 필수

3. 예제: "안전한 핸들러"

이 예제는 SIGINT(Ctrl+C)를 처리하는 도중에 SIGQUIT(Ctrl+\)가 들어와도 무시(블록)하고, 하던 일을 마친 뒤에 처리하는 안정성을 보여줍니다.

C

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void my_handler(int signo) {
    printf("\n[Handler] 시그널 %d번 처리 시작 (3초 소요)...\n", signo);
    printf("[Handler] 이 동안에는 Ctrl+\\ (SIGQUIT)을 눌러도 대기합니다.\n");
    
    for (int i = 0; i < 3; i++) {
        printf("... 처리 중 %d\n", i + 1);
        sleep(1);
    }
    printf("[Handler] 처리 완료.\n");
}

int main() {
    struct sigaction act;

    // 1. 핸들러 함수 지정
    act.sa_handler = my_handler;

    // 2. 마스크 설정 (핸들러 실행 중 막을 시그널)
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGQUIT); // SIGINT 처리 중엔 SIGQUIT을 블록함!

    // 3. 플래그 설정
    // SA_RESTART: 시그널 처리 후 중단된 시스템 콜(read, write 등)을 자동 재시작
    act.sa_flags = SA_RESTART; 

    // 4. 등록
    if (sigaction(SIGINT, &act, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    printf("PID: %d\n", getpid());
    printf("Ctrl+C를 눌러보세요. 핸들러 실행 중에 Ctrl+\\도 눌러보세요.\n");

    while (1) {
        printf("Main Loop...\n");
        sleep(1);
    }
    return 0;
}

4. 실행 및 결과

  1. Ctrl+C 입력: my_handler가 실행되어 "처리 중 1, 2, 3"을 출력하기 시작함.
  2. 도중에 Ctrl+\ 입력:
    • signal() 함수였다면: 핸들러가 멈추고 즉시 Quit (core dumped) 되며 죽음.
    • sigaction() 결과: 아무 반응 없음 (블록됨). my_handler가 "처리 완료" 메시지를 띄우고 리턴된 직후에 Quit 시그널이 처리되어 종료됨.

5. 주요 Flags (sa_flags)

  • 0: 기본 동작.
  • SA_RESTART: 시그널 때문에 끊긴 read(), write() 등을 에러(EINTR) 내지 않고 자동으로 재시작해줌. (매우 유용).
  • SA_SIGINFO: 핸들러 함수 파라미터를 확장하여, 송신자 PID나 데이터를 받을 수 있게 함. (sa_handler 대신 sa_sigaction 사용).


reference : https://reakwon.tistory.com/215#google_vignette

profile
임베디드 개발자가 되기 위해 공부중입니다!

0개의 댓글