sigaction은 리눅스와 유닉스 기반 시스템에서 신호 처리를 설정하기 위한 함수입니다. 프로세스가 특정 신호를 받았을 때 해당 신호를 처리하는 방법을 정의할 수 있도록 도와줍니다. sigaction을 사용하면 signal 함수보다 더 많은 옵션과 제어가 가능하여 더 유연한 신호 처리가 가능합니다
특징 : sigaction은 함수이고, 동시에 구조체도 사용합니다. sigaction 함수는 신호를 처리할 때 사용할 설정을 지정하기 위해 struct sigaction 구조체를 인수로 받습니다.
signal 시스템의 문제점signal이 발생된 원인을 모름
– signal handler에게 어떤 signal이 발생되었는지는 통보(signum)
– 해당 signal이 발생된 원인에 대해서는 알 수 없음
signal handler에서 signal을 처리하는 동안 다른 signal을 안전하게 차단할 수 없음
signal에서는 신호 핸들러가 실행되는 동안 다른 신호를 일시적으로 막는 기능이 부족했습니다. 이로 인해 동시에 여러 신호가 발생할 경우 핸들러가 중첩되어 실행되는 등의 문제가 발생할 수 있었습니다. 즉, 하나의 시그널을 처리하는것은 효과적이나 여러 시그널이 도착하면 문제가 생긴다는 겁니다.
그러나 sigaction에서는 sa_mask 필드를 통해 특정 신호가 처리되는 동안 블록할 신호를 지정할 수 있습니다. 이를 통해 신호 간의 충돌을 방지하고 더욱 안전하게 신호를 처리할 수 있습니다.

그럼 이 좋은거 후두다닥 배워보러 갑시다

sigaction은 함수이고, 동시에 구조체도 사용합니다. sigaction 함수는 신호를 처리할 때 사용할 설정을 지정하기 위해 struct sigaction 구조체를 인수로 받습니다.
struct sigaction {
void (*sa_handler)(int); // 신호 핸들러 함수
void (*sa_sigaction)(int, siginfo_t *, void *); // 대체 신호 핸들러
sigset_t sa_mask; // 핸들러가 실행되는 동안 블록할 신호 집합
int sa_flags; // 동작 플래그 (SA_RESTART, SA_SIGINFO 등)
};

struct sigaction에는 sa_handler와 sa_sigaction 이라는 두 가지 신호 핸들러를 설정할 수 있습니다
이 두 핸들러는 신호가 발생했을 때 호출될 함수를 지정하는 방식이지만, 그 용도와 기능에는 차이가 있습니다.
sa_handler는 기존 스타일의 신호 핸들러입니다.
신호 번호 int signo 하나만을 매개변수로 받는 단순한 핸들러 함수를 설정할 때 사용됩니다.
이 핸들러는 발생한 신호의 번호만 전달되므로 신호에 대한 추가 정보를 얻을 수 없습니다.
예를 들어, 다음과 같이 단순히 신호를 받았을 때 실행될 함수를 지정합니다:
void handler(int signo) {
printf("Received signal %d\n", signo);
}
sa_sigaction은 보다 많은 정보를 제공하는 새로운 스타일의 핸들러로, 신호 발생 시 추가 정보 (siginfo_t)를 받을 수 있습니다.
이 핸들러는 세 개의 매개변수를 받습니다:
int signo: 발생한 신호의 번호
siginfo_t *info: 신호에 대한 추가 정보를 담고 있는 포인터 (si_pid, si_uid 등)
void *context: CPU 컨텍스트에 대한 포인터 (보통 잘 사용되지 않음)
sa_sigaction을 사용하려면 struct sigaction의 sa_flags에 SA_SIGINFO 플래그를 설정해야 합니다.
그렇지 않으면 sa_handler가 기본 핸들러로 사용됩니다.
예를 들어, 다음과 같이 신호를 보내온 프로세스의 PID와 같은 추가 정보를 얻을 수 있습니다:
void siginfo_handler(int signo, siginfo_t *info, void *context) {
printf("Received signal %d\n", signo);
printf("Sent by process with PID: %d\n", info->si_pid);
}

int main() {
struct sigaction sa;
// 1. 기존 스타일 핸들러 설정
sa.sa_handler = old_handler; // sa_handler 사용
sa.sa_flags = 0; // 기본 플래그
sigaction(SIGINT, &sa, NULL); // SIGINT에 대해 old_handler 사용
// 2. 새로운 스타일 핸들러 설정
sa.sa_sigaction = new_handler; // sa_sigaction 사용
sa.sa_flags = SA_SIGINFO; // SA_SIGINFO 설정으로 sa_sigaction 활성화
sigaction(SIGTERM, &sa, NULL); // SIGTERM에 대해 new_handler 사용
while (1) {
printf("Waiting for SIGINT or SIGTERM...\n");
sleep(2);
}
return 0;
}
SA_RESETHAND: signal을 수신하고 handler를 호출한 이후, 해당 signal을 SIG_DFL로 설정
SA_NODEFER : 해당 signal을 처리하는 중에 다시 같은 signal이 발생하면 kernel이 자동으로 blocking 하지 못하도록 설정
SA_RESTART : signal handler에 의해 중지된 시스템 호출을 handler 처리 이후 자동으로 재시작
SA_SIGINFO : 해당 값을 설정하고 signal을 수신하면 sa_sigaction을 수행
해당 값을 설정하지 않으면, sa_handler를 수행
int main() {
struct sigaction sa;
sa.sa_sigaction = sigint_handler; // 새로운 스타일 핸들러 설정
sa.sa_flags = SA_SIGINFO | SA_RESTART; // 플래그 설정 (여러 플래그 사용시 |(or) 사용)
sigemptyset(&sa.sa_mask); // sa_mask 초기화
sigaction(SIGINT, &sa, NULL); // SIGINT에 대해 핸들러
return 0;
}
여러가지 플래그를 사용해서 핸들러에 속성을 부여한다! 라는 느낌입니다

sa_mask란?
• Handler에서 발생된 signal을 처리하는 동안, 다른 signal을 차단할지 설정하는 필드입니다
• 차단할 signal들의 집합을 설정하는 방식을 사용합니다
이 설정은 핸들러가 처리 중일 때 다른 신호로 인해 방해받는 것을 방지하기 위해 사용됩니다!
설정 방법
sigemptyset(&sa.sa_mask): sa_mask를 초기화하여 비워둡니다.sigaddset(&sa.sa_mask, SIGNAL_이름): sa_mask에 특정 신호를 추가합니다.