System Prgramming : signal handler

전창우·2024년 11월 30일

system programming

목록 보기
8/9

signal handler란?

signal handler란 프로세스가 실행되고 있는 중에 signal이 발생했을 시, '프로세스가 해당 signal에 어떻게 대응할 것인가?'를 선언해놓은 것이다.

old style signal handler(signal func)

우선 기본적인 signal handler 함수인 signal func에 대해 알아보자.
signal func의 함수 원형은 다음과 같다.

int signal(SIGNAL, sig_handler)

여기서 SIGNAL은 signal을 나타내는 상수값이고,
sig_handler는 SIGNAL이 발생했을 때, 호출할 함수이다.

sinal func의 문제점

1.하나의 signal을 처리하는 동안, 다른 signal이 도착할 경우 처리하지 못함.

  • signal handler에서 signal을 처리하는 동안, 다른 signal을 안전하게 차단할 수 없음

2.signal에 대한 자세한 정보를 알지 못함

  • 단순히, sinal handler에게 어떤 signal이 발생하면 호출하는 역할만 함

sigaction

signal func의 단점을 보완한 new style signal handler로
1. 어떤 signal을 처리할 것인지
2. 해당 signal을 어떻게 처리할 것인지
3. signal을 처리하는 도중 다른 signal이 발생하면 어떻게 처리할 것인지
설정할 수 있다.

sigaction func의 함수원형

#include <signal.h>
int res = sigaction(int signum,
					coust struct sigaction* action,
                    struct sigaction* prevaction);

ARGS:
1. signum: SIGNAL 상수, 처리할 Signal 정의(ex: SIGINT, SIGTERM, SIGKILL 등)
2. action: sigaction struct type. signum 발생 시 처리 동작 정의
3. prev: 이전에 등록된 신호 처리 동작 정보를 저장할 포인터(NULL로 처리 가능)

결국에 action이라는 argument가 해당 함수를 지배하는데,,
sigaction struct 원형

struct sigaction
{
	void (*sa_handler)(int);// Old style
    void (*sa_sigaction)(int, siginfo_t*, void*);// New style
    sigset_t sa_mask;
    int sa_flags;   
}

Old style을 사용할 거면 그냥, signal func를 사용하면 된다. 굳이 sigaction을 사용할 필요가 없다.
sigaction 함수를 사용하는 이유는 해당 signal이 발생했을 때, 세세한 처리가 가능하기 때문이다. 이런 '세세한 처리'는 sigaction struct에 의해서 정의된다.

sigaction의 멤버를 뜯어보자.

void (*sa_handler)(int) : 이거 쓸거면 나가라

void (sa_sigaction)(int, siginfo_t, void*):
이 자리에 함수가 들어가야 한다.

해당 함수는
1. return값이 void여야 하며,
2. args는 아래 형식을 따라야 한다

int : signum
siginfo_t : 신호와 관련된 상세 정보를 담고있는 구조체
void
: 그냥 원하는 거 넣으면 된다. 출력한 txt라던가..

sa_mask: 신호를 처리하는 동안 차단(sa_flags에 따라 동작 결정)할 신호들의 집합
sigemptyset(): 신호 집합 초기화.
sigaddset(): 신호 추가.

sa_flags: 신호 처리 도중, 발생한 Signal에 대한 처리를 정의

SA_RESTART: 미뤘던 signal, handler 수행 이후 재시작
SA_SIGINFO: sa_sigaction 활성화.
SA_NODEFER: 해당 signal을 처리하는 중에 다시 같은 signal이 발생하면, kernel이 자동으로 blocking 하지 못하도록 설정
SA_RESETHAND: signal을 수신하고 handler를 호출한 이후, 해당 signal을 SIG_DFL로 설정

siginfo_t 구조체 원형
signal handler에 전달되는 siginfo에는 다음과 같은 정보가 담겨져 있다.

typedef struct {
    int     si_signo; // Signal number
    int     si_errno;
    int     si_code; // signal이 발생한 원인
    pid_t   si_pid;
    uid_t   si_uid;
    void   *si_addr;
	int		si_status;
	union	sigval si_value;
} siginfo_t;

sigaction() 매커니즘

sigaction의 args : int signum, sigaction struct* handler, prev
sigaction struct 멤버: sa_handler, sa_mask, sa_flags

예제

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

#define INPUTLEN 100

char *uid_to_name(uid_t uid)
{
    struct passwd *pw_ptr;
    static char numstr[10];
    if((pw_ptr = getpwuid(uid))==NULL)
    {
        sprintf(numstr, "%d", uid);
        return numstr;
    }else{
        return pw_ptr->pw_name;
    }
}
void int_handler(int sig, siginfo_t *siginfo, void *context)
{
    printf("error value: %d, signal code: %d\n", siginfo->si_errno,siginfo->si_code);

    printf("sending UID %-8s\n",uid_to_name(siginfo->si_uid));
    printf("Called with signal %d\n",sig);
    
    sleep(sig);
    printf("done handling signal %d\n", sig);
}
int main()
{
    struct sigaction new_handler;
    sigset_t blockSet;
    char x[INPUTLEN];

    new_handler.sa_sigaction = int_handler;
    new_handler.sa_flags = SA_RESETHAND | SA_SIGINFO | SA_RESTART;

    sigemptyset(&blockSet);
    sigaddset(&blockSet, SIGQUIT);
    new_handler.sa_mask = blockSet;

    if(sigaction(SIGINT, &new_handler, NULL) ==-1)
    {
        perror("sigaction err");
        exit(EXIT_FAILURE);
    }else{
        while(1)
        {
            fgets(x, INPUTLEN, stdin);
            printf("input: %s", x);
        }
    }
    return 0;
}
  1. SIGINT 발생시 new_handler의 sa_sigaction에 저장된 int_handler func 호출

  2. int_handler 실행 도중, SIGQUIT signal 발생시, block 되었다가 int_handler가 종료되면 SIGQUIT 수행.

  3. int_handler는 siginfo를 통해 signal에 대한 정보를 가지고, 이를 활용하여 다양한 task(uid, signal 정보, 등등) 수행 가능

sigprocmask()

critical section에 대해서 Signal을 수신하지 않도록 제어하기 위한 함수

sigprocmask 함수 원형

#include <signal.h>
int res = sigprocmask(int how, const sigset_t *sigs, sigset_t *prev);

ARGS
how:

  • SIG_BLOCK : 매개변수 sigs에 등록된 Signal들을 blocking
  • SIG_UNBLOCK : 매개변수 sigs에 등록된 signal들을 unblocking
  • SIG_SETMASK: 매개변수 sigs에 등록된 signal들을 blocking 시키고,
    sigs에 등록되지 않은 signal들은 unblocking

sigs: signal들의 집합(data type: sigset_t)
prev: 안써도됨 걍 NULL

About sigset_t struct
sigset_t struct를 처음 선언한 이후,
sigemptyset(sigset_t* sigset)으로 초기화를 해준 뒤,
sigaddset(sigset_t *setp, int signum)으로 집합에 signal들을 추가해주면 된다.

sigprocmask() 매커니즘

예제

int main(void)
{
    sigset_t sig_set;
    int count;

    sigemptyset(&sig_set);
    sigaddset(&sig_set, SIGINT);
    sigprocmask(SIG_BLOCK, &sig_set, NULL);

    for(count =3; count>0; count --)
    {
        printf("count %d\n", count);
        sleep(1);
    }
    printf("Unblocking SIGINT\n");
    sigprocmask(SIG_UNBLOCK, &sig_set, NULL);
    printf("Press Ctrl-C while count, this sentence will not be printed.\n");
    while(1);
    exit(0);
}

0개의 댓글