[Linux] 시그널

김영준·2023년 12월 4일

https://g.co/kgs/R7BWpf
Part 03 - 프로세스 간 통신
Chapter 08 - 시그널
07 - 알람 시그널
08 - 기타 시그널 처리 함수

07 - 알람 시그널

일정 시간이 지난 후에 자동으로 시그널이 발생하게 하려면 알람 시그널(alarm signal)을 사용한다.
알람 시그널은 일정 시간 후에 한 번 발생시킬 수도 있고 일정한 간격을 두고 주기적으로 발생시킬 수도 있다.

7.1 - 알람 시그널 함수

알람 시그널 생성 : alarm(2)

#include <unistd.h>

unsigned int alarm(unsigned int seconds);
// seconds - 알람을 발생시킬 때까지 남은 시간(초 단위)

alarm() 함수가 인자로 지정한 시간이 지나면 SIGALRM 시그널이 생성되어 프로세서에 전달된다. 만약 시그널을 생성하기 전에 다시 alarm() 함수를 호출하면 이전 설정은 없어지고 재설정된다.

ex)

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

void sig_handler(int signo) {
    psignal(signo, "Received Signal");
}

int main() {
    signal(SIGALRM, sig_handler); // SIGALRM 시그널 처리를 위한 시그널 핸들러를 지정한다.

    alarm(2); // alarm() 함수의 인자로 2초를 지정해 함수를 호출한다.
    printf("Wait...\n");
    sleep(3); // 3초 동안 sleep하고 있는 중에 2초가 지나면 SIGALRM 시그널이 발생해
							// 시그널 핸들러가 호출된다.
}

// Wait...
// Received Signal: Alarm Clock

7.2 인터벌 타이머

타이머의 종류

  • ITIMER_REAL - 실제 시간을 사용한다. 이 타이머가 만료되면 SIGALRM 시그널이 생성된다.
  • ITIMER_VIRTUAL - 프로세스가 사용하는 사용자 모드 CPU 시간을 사용한다. 이 시간은 프로세서가 동작 중을 때만 작동하며, 만료되면 SIGVTALRM 시그널이 생성된다.
  • ITIMER_ PROF - 프로세스가 사용하는 시스템 모드와 사용자 모드 CPU 시간을 합해 사용한다. 이 타이머가 만료되면 SIGPROF 시그널이 생성된다. ITIMER_VIRTUAL과 함께 사용하면 프로세스 사용한 사용자 모드 CPU 시간과 시스템 모드 CPU 시간을 알 수 있다.

타이머 정보 검색 : getitimer(2)

#include <sys/time.h>

int getitimer(int which, struct itimerval * curr_value);
  • which - 검색할 타이머의 종류
  • curr_value - 타이머의 정보를 저장할 구조체 포인터

getitimer() 함수는 타이머 정보를 검색하는 함수이다.

타이머 설정 : setitimer(2)

#include <sys/time.h>

int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
  • which - 설정할 타이머의 종류
  • value - 설정할 타이머의 정보를 저장한 구조체 포인터
  • ovalue - 이전 타이머의 정보를 저장할 구조체 포인터

setitimer()는 타이머를 설정하는 함수이다.

ex)

#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

void sig_handler() {
    printf("Timer Invoked..\n");
}

int main() {
    struct itimerval it;

    signal(SIGALRM, sig_handler); // SIGALRM 시그널 핸들러를 지정한다.
    // 타이머의 간격을 2초로 설정하고 타이머에 현재 남은 시간을 3초로 설정한다.
		// 즉, 최초의 시그널은 3초 후에 발생하고 이후 2초 간격으로 타이머가 동작하게 설정하는 것이다.
		it.it_value.tv_sec = 3;
    it.it_value.tv_usec = 0;
    it.it_interval.tv_sec = 2;
    it.it_interval.tv_usec = 0;

    if (setitimer(ITIMER_REAL, &it, (struct itimerval *)NULL) == -1) {
        perror("setitimer");
        exit(1);
    }

    while (1) {
				// 반복문으로 1초 동안 sleep했다가 getitimer() 함수를 호출해 남은 시간 정보를 출력하게 한다.

        if (getitimer(ITIMER_REAL, &it) == -1) {
            perror("getitimer");
            exit(1);
        }
        printf("%d sec, %d msec.\n", (int)it.it_value.tv_sec,
            (int)it.it_value.tv_usec);
        sleep(1);
    }
}

// 처음 남은 시간이 3초였으므로 while문을 세번 반복한 후에 타이머가 만료되어
// SIGALRM 시그널을 생성하고 이후에는 2초 간격으로 타이머가 작동함을 알 수 있다.
// 무한반복문이므로 강제종료 해야한다.

08 - 기타 시그널 처리 함수

리눅스에서는 시그널 정보를 출력하거나, 시그널이 올 때까지 기다리거나, 시그널을 보내는 등의 시그널 처리를 위한 기타 함수를 제공한다.

8.1 - 시그널 정보 출력

시그널 정보 출력 - psignal(3)

#include <signal.h>

void psignal(int sig, const char *s);
  • sig - 정보를 출력할 시그널
  • s - 출력할 문자열

psignal() 함수는 두 번째 인자인 s로 지정한 문자열을 출력한 수 첫 번째 인자인 sig로 지정한 시그널을 가리키는 이름을 붙여 표준 오류로 출력한다.

시그널 정보 출력 - strsignal(3)

#include <string.h>

char *strsignal(int sig);
  • sig - 정보를 출력할 시그널

strsignal() 함수는 인자로 받은 시그널을 가리키는 이름을 문자열로 리턴한다. 인자로 받은 시그널이 없으면 NULL을 리턴한다.

8.2 - 시그널 블로킹과 해제

시그널 블로킹과 해제 - sighold(3), sigrelse(3)

#include <signal.h>

int sighold(int sig);
int sigrelse(int sig);
  • sig - 블로킹하거나 해제할 시그널

sighold() 함수는 인자로 받은 시그널을 프로세스의 시그널 마스크에 추가한다. 시그널 마스크에 추가된 시그널은 블로킹되어 해당 시그널을 받지 않는다.

ex)

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

void sig_handler(int signo) {
    char *s;

    s = strsignal(signo);
    printf("Received Signal : %s\n", s);
}

int main() {
    if (signal(SIGINT, sig_handler) == SIG_ERR) {
        perror("signal");
        exit(1);
    }

    sighold(SIGINT); // sighold() 함수를 사용해 SIGINT 시그널을 블로킹한다. Ctrl + /로 강제 종료한다.

    pause();
}

시그널 집합 블로킹과 해제 : sigprocmask(2)

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • how - 시그널을 블로킹할 것인지 해제할 것인지 여부
  • set - 블로킹하거나 해제할 시그널 집합의 주소
  • oldset - NULL 또는 이전 설정값을 저장할 시그널 집합의 주소

sigprocmask() 함수는 set에 지정한 시그널 집합을 블로킹할 것인지 해제할 것인지를 how에 지정해 호출한다. 첫 번째 인자인 how에 올 수 있는 값은 다음과 같다.

  • SIG_BLOCK - set에 지정한 시그널 집합을 시그널 마스크에 추가한다.
  • SIG_UNBLOCK - set에 지정한 시그널 집합을 시그널 마스크에서 제거한다.
  • SIG_SETMASK - set에 지정한 시그널 집합으로 협재 시그널 마스크를 대체한다.

두 번째 인자인 set은 블로킹하거나 해제할 시그널 집합을 가리키고, 세 번째 인자인 oldset에는 NULL 아니면 이전 설정값이 저장된다.

ex)

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

int main() {
    sigset_t new;

    sigemptyset(&new); // 시그널 집합에 SIGINT와 SIGQUIT 시그널을 설정한다.
    sigaddset(&new, SIGINT);
    sigaddset(&new, SIGQUIT);
    sigprocmask(SIG_BLOCK, &new, (sigset_t *)NULL);
		// sigprocmask() 함수를 사용해 SIGINT 시그널과 SIGQUIT 시그널을 블로킹한다.

    printf("Blocking Signals : SIGINT, SIGQUIT\n");
    printf("Send SIGQUIT\n");
    kill(getpid(), SIGQUIT); // SIGQUIT 시그널을 보낸다.

    printf("UnBlocking Signals\n");
    sigprocmask(SIG_UNBLOCK, &new, (sigset_t *)NULL);
		// 블로킹된 SIGQUIT 시그널을 해제한다.
}

8.3 - 시그널 기다리기

시그널 대기 : pause(2)

#include <unistd.h>

int pause(void);

pause() 함수는 프로세스가 종료하거나 시그널 잡기 함수를 호출하는 시그널을 받을 때까지 프로세스를 대기시킨다. pause() 함수는 시그널 잡기 함수로, 시그널을 잡았을 때만 -1을 리턴한다.

시그널 대기 : sigpause(3)

#include <signal.h>

int sigpause(int sigmask);
int sigpause(int sig);
  • sigmask - 블록을 해제하고 대기할 시그널을 지정한 시그널 마스크
  • sig - 시그널이 올 때까지 대기할 시그널

sigpause() 함수는 인자로 지정한 시그널을 프로세스의 시그널 마스크에서 제거하고 프로세스가 시그널을 받을 때까지 기다린다.

시그널 기다리기 : sigsuspend(2)

#include <signal.h>

itn sigsuspend(const sigset_t *mask);
  • mask - 기다리려는 시그널을 지정한 시그널 집합의 주소

sigsuspend() 함수는 인자로 지정한 시그널 집합에 설정된 시그널들로 프로세스의 시그널 마스크를 교체하고, 블로킹되지 않은 시그널이 도착할 때까지 프로세스의 수행을 멈추고 기다린다.

ex)

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

void sig_handler(int signo) {
    psignal(signo, "Received Signal:");
}

int main() {
    sigset_t set;

    signal(SIGALRM, sig_handler); // 알람 시그널을 받으면 시그널 핸들러를 호출하도록 설정한다.

    sigfillset(&set); // 시그널 마스크에서 모든 시그널을 블로킹한 후 SIGALRM만 해제한다.
    sigdelset(&set, SIGALRM);

    alarm(3); // 3초 후에 알람 시그널이 발생하도록 설정한다.

    printf("Wait...\n");

    sigsuspend(&set); // sigsuspend() 함수를 사용해 시그널이 도착하기를 기다린다.
}

8.4 - 기타 시그널 함수

시그널 보내기 : sigsend(2)

#include <signal.h>

int sigsend(idtype, id_t id, int sig);
  • idtype - id에 지정한 값의 종류
  • id - 시그널을 받을 프로세스나 프로세스 그룹
  • sig - 보내려는 시그널

sigsend() 함수는 sig로 지정한 시그널을 id에 지정한 프로세스나 프로세스 그룹에 보낸다.

시그널 무시 처리 : sigignore(3)

#include <signal.h>

int sigignore(int sig);
  • sig - 무시할 시그널

sigignore() 함수는 인자로 지정한 시그널의 처리 방법을 SIG_IGN으로 설정함으로써 시그널을 무시하도록 한다.

profile
Seize the day

0개의 댓글