tonykang22 / study

0 stars 0 forks source link

[리눅스 개발환경] 13. 시그널 #84

Open tonykang22 opened 2 years ago

tonykang22 commented 2 years ago

13. 시그널

시그널 개요

시그널(Signal)이란?



시그널 전송


시그널 처리


시그널 경쟁

image



시그널 핸들러

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signo, sighandler_t handler);


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

int loop = 1;

static void sigint_handler(int signo)
{
    printf("SIGINT received!\n");
    loop = 0;
}

int main()
{
    if (signal(SIGINT, sigint_handler) == SIG_ERR) {
        printf("register SIGINT handler fail!\n");
        return -1;
    }

    if (signal(SIGTSTP, SIG_IGN) == SIG_ERR) {
        printf("register SIGTSTP handler fail!\n");
        return -1;
    }

    while (loop)
        sleep(1);

    printf("loop exit.\n");

    return 0;
}



시그널 전송

#include <sys/types.h>
#incldue <signal.h>

int kill (pid_t pid, int signo);


#include <signal.h>

int raise (int signo);


int main(int argc, char *argv[]) { if (argc < 3) { printf("Usage: %s \n", argv[0]); return -1; }

if (kill(atoi(argv[2]), atoi(argv[1])) == -1) {
    perror("kill error: ");
    return -1;
}

return 0;

}


<br>

- 수행 결과

<img width="566" alt="image" src="https://user-images.githubusercontent.com/86760744/181876315-302a8771-e183-4134-895c-eb4f02203674.png">

<br><br>

## 시그널 차단
- 프로세스가 코드 어딘가에서 실행 중인 상태에서, 시그널을 수신하게 되면 실행 중인 부분에서 중단되고, 등록된 시그널 핸들러 함수가 수행되게 된다.
- 핸들러 수행이 완료된 후 중단된 지점으로 돌아와 코드를 계속 수행한다.
- 그런데 수행 중인 코드가 수정 중인 데이터를 핸들러에서 참조하거나 수정하게 되면 문제가 발생할 수 있다.

<br>

- 주기적으로 설정 파일에서 읽어들인 데이터를 이용해 어떤 연산을 수행하는 프로세스가 있다고 가정해보자.
- 이 프로세스는 SIGHUP 시그널을 수신 받으면 설정 파일에서 데이터를 다시 읽어오는 핸들러가 등록되어 있다.
- 그런데 10개의 설정 중 3개의 데이터를 읽어와 연산을 수행했는데, 이 시점에 SIGHUP 시그널을 수신 받게 되면,
- 시그널 핸들러가 다시 설정 파일을 읽어오게 되는데, 중단된 시점으로 복귀하게 되면,
- 앞에 읽어온 3개의 시점과, 핸들러 복귀 이후 나머지 7개의 데이터 시점이 달라지게 된다.
- 시그널 핸들러의 복귀 시에, 이와 같이 의도하지 않은 문제가 발생할 수 있다.

<br>

- 이를 위해 10개의 데어티가 다 처리되는 동안은 시그널을 수신 받지 못하게 차단하여 문제를 해결할 수 있다.
- 즉, 프로그램에서 시그널 핸들러와 프로그램의 다른 부분이 데이터를 공유할 필요가 있을 때,
- 공유되는 부분을 크리티컬 섹션이라 하고, 일시적으로 시그널을 보류하여 이 영역을 보호할 수 있다.

<br>

``` c
#include <signal.h>

int sigprocmask (int how, const sigset_t *set, sigset_t *oldset);



int wakeup = 0;

static void sig_handler(int signo) { if (signo == SIGINT) printf("signal SIGINT received!\n"); else if (signo == SIGTSTP) printf("signal SIGTSTP received!\n"); else if (signo == SIGQUIT) printf("signal SIGQUIT received!\n"); else printf("signal %d received!\n");

    wakeup = 1;

}

static void wait_for_signal(void) { printf("sleep 10 sec\n"); sleep(10); printf("wakeup!\n"); }

int main() { sigset_t set, oldset;

    if (signal(SIGINT, sig_handler) == SIG_ERR) {
            printf("signal handler(SIGINT) error!\n");
            return -1;
    }

    if (signal(SIGTSTP, sig_handler) == SIG_ERR) {
            printf("signal handler(SIGTSTP) error!\n");
            return -1;
    }

    if (signal(SIGQUIT, sig_handler) == SIG_ERR) {
            printf("signal handler(SIGQUIT) error!\n");
            return -1;
    }

    sigemptyset(&set);
    sigemptyset(&oldset);

    sigaddset(&set, SIGINT);
    sigaddset(&set, SIGTSTP);

    sigprocmask(SIG_BLOCK, &set, NULL);
    printf("Block SIGINT and SIGTSTP\n");

    wait_for_signal();

    sigdelset(&set, SIGINT);

    sigprocmask(SIG_UNBLOCK, &set, &oldset);
    printf("Block only SIGINT\n");

    wait_for_signal();

    sigprocmask(SIG_SETMASK, &oldset, NULL);
    printf("Block SIGINT and SIGTSTP\n");

    wait_for_signal();

    return 0;

}


<br>

- 수행 결과
<img width="377" alt="image" src="https://user-images.githubusercontent.com/86760744/181876379-9c78d575-724e-4a62-b8f5-9b376da145be.png">

<br><br>

## 시그널 고급 관리
- signal() 함수의 대안으로 POSIX는 훨씬 더 강력한 시그널 관리 능력을 제공하는 sigaction() 시스템 콜을 표준화했다.

``` c 
#include <signal.h>

int sigaction (int signo, const struct sigaction *act, struct sigaction *oldact);


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 sa_sigaction (int signo, siginfo_t *si, void *ucontext);

#include <sys/signal.h>

typedef struct siginfo_t {
    int si_signo; /* 시그널 번호 */
    int si_errno; /* errno 값 */
    int si_code; /* 시그널을 일으킨 원인을 알려준다.(ex. SI_KERNEL 커널에서 보낸 시그널) */ 
    pid_t si_pid; /* 보내는 프로세스 pid */
    uid_t si_uid; /* 보내는 프로세스 uid */
    int si_status; /* 종료된 값이나 시그널 */
    clock_t si_utime; /* 소비된 사용자 시간 */
    clock_t si_stime; /* 소비된 시스템 시간 */
    sigval_t si_value; /* si_int와 si_ptr의 유니온 */
    int si_int;/* sigqueue() /* 페이로드 정수 타입 */
    void *si_ptr; /* sigqueue() 페이로드 void 포인터 타입 */ 
    void *si_addr; /* 장애가 발생한 메모리 위치 */
    int si_band;
    int si_fd; /* 파일 디스크립터 */
};
#inclue <signal.h>

int sigqueue (pid_t pid, int signo, const union sigval value);

union sigval {
    int sival_int;
    void *sival_ptr;
};