시스템 소프트웨어

시그널 (Signal)

masimelo 2023. 3. 7. 15:21

시그널

  • 프로세스가 실행되는 도중에 다양한 예외상황이 발생할 수 있고, 이것을 적절하게 처리해야함
    • 예외 상황에 발생하는 이벤트 또는 신호를 시그널이라고 함
    • 정보와 의미가 일대일로 매칭되기 때문에, 빠르게 전달되고 빠르게 처리할 수 있음
  • 대부분의 운영체제에는 시그널이 정의되어있음
    • UNIX와 Linux 계열의 운영체제는 kill -l명령어를 통해 그 종류를 확인할 수 있음
  • 시그널은 다음 두가지 용도에 특히 잘 사용될 수 있음
    • 비동기적인 이벤트의 발생을 통지하기 위한 용도
      • 프로세스 종료
    • 프로세스간의 통신이나 동기화 하기 위한 용도
      • 알림
  • 시그널은 비동기적, 동기적으로 처리될 수 있음
    • 비동기적
      • 시그널이 특정 시간에 발생하는 것을 기다리지 않음
    • 동기적
      • 동작을 중단하고 시그널이 발생하는 것을 기다림
  • 시그널은 각 기본 동작이 정의되어 있음
    • Abort
      • 코어 덤프를 생성하고 프로세스를 종료
    • Exit
      • 코어 덤프를 생성하지 않고 프로세스를 종료
    • Abnormal termination
      • 프로세스를 비정상 종료함
    • Stop
      • 프로세스를 정지함
    • Continue
      • 프로세스를 재게함
    • ignore
      • 시그널을 무시함
    • Implementation dependent
      • 구현에 의존함

POSIX 시그널

    •  

시그널의 생성과 처리

시그널 핸들러

  • 시그널은 각 기본 동작이 정의되어 있음
  • 시그널의 동작을 다른 동작으로 변경할 수 있음
  • 시그널 핸들러
    • 프로세스가 시그널을 수신했을 때, 기본 동작 대신에 특정 동작을 수행하기를 원할때 사용하는 사용자 정의 함수
    • 내부적으로 디폴트 액션 또는 커널 정의 시그널 핸들러는 사용자 정의 시그널 핸드러로 덮어짐
  • 하지만 SIGKILL과 SIGSTOP 시그널 동작은 재정의할 수 없음

시그널 마스크

  • 블록(Block)
    • 프로세스가 시그널을 블록하면 프로세스가 해당 시그널의 블록을 해제할 때까지 그 시그널은 peding됨
  • 무시(Ignore)
    • 프로세스가 시그널을 무시하면, 그 시그널은 전달되지만 프로세스가 해당 시그널을 버리도록 처리함

시그널 함수

kill 명령어

  • 프로세스에 시그널을 전송함
  • kill [옵션] [PID]
    • 옵션
      • -[num] : [num] 번호 시그널을 전송함

kill() 함수

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

int kill(pid_t pid, int signum);
  • 설명
    • 프로세스에 시그널을 전송함
  • 인자
    • pid : 프로세스의 pid
    • signum : 전송할 시그널의 번호
  • 반환
    • 성공시 0
    • 오류시 -1

signal() 함수

#include <signal.h>

typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  • 설명
    • 프로세스에 전송된 시그널의 기본동작을 다른 동작으로 재정의 함
  • 인자
    • signum : 시그널의 번호
    • handler : 시그널을 수신했을 때의 동작을 정의한 함수의 포인터
      • SIG_IGN : 시그널을 무시함
      • SIG_DFL : 시그널의 기본동작으로 되돌림
  • 반환
    • 성공시 이전 시그널 핸들러의 포인터
    • 오류시 SIG_ERR

signal() 함수 예시

#include <dirent.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

void mySigHandler(int signum);

int main(int argc, char const *argv[]) {
    if (signal(SIGINT, mySigHandler) == SIG_ERR) {
        perror("signal() error");
        exit(-1);
    }

    if (signal(SIGUSR1, mySigHandler) == SIG_ERR) {
        perror("signal() error");
        exit(-1);
    }
    printf("Hello my PID is %d\\n", getpid());
    while (1) {
        puts("Pause...");
        pause();
    }
    return 0;
}

void mySigHandler(int signum) {
    if (signum == SIGINT) {
        printf("Hello SIGINT\\n");
    } else if (signum = SIGUSR1) {
        printf("Hello SIGUSR1\\n");
    }
}

alarm() 함수

#include <unistd.h>

unsigned int alarm(unsigned int seconds);
  • 설명
    • 프로세스 자신에 SIGALRM 시그널을 전송함
  • 인자
    • seconds : 몇 초 후에 시그널을 전송할 것인지 지정
  • 반환
    • 성공시 시그널 전송할 때 까지 남은 시간
    • 오류시 -1

alarm() 함수 예시

#include <dirent.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

void myAlrHandler(int signum);

int main(int argc, char const *argv[]) {
    if (signal(SIGALRM, myAlrHandler) == SIG_ERR) {
        perror("signal() eroor");
        exit(-1);
    }
    alarm(5);
    puts("Sleep ...");
    sleep(100);
    return 0;
}

void myAlrHandler(int signum) { puts("Hello! I'm wake!"); }

sighold() 함수

#include <signal.h>

int sighold(int signum);
  • 설명
    • 시그널을 블록 상태로 변경
  • 인자
    • signum : 블록 하고자하는 시그널 번호
  • 반환
    • 성공시 0
    • 오류시 -1

sigrelse() 함수

#include <signal.h>

int sigrelse(int signum);
  • 설명
    • 시그널의 블록상태를 해제함
  • 인자
    • signum : 해제하고자 하는 시그널의 번호
  • 반환
    • 성공시 0
    • 오류시 -1

시그널 집합

시그널 집합

  • 시그널은 보통 개별적으로 처리됨
  • POSIX에서 복수의 시그널을 처리하기 위해 도입한 개념
    • 시그널 집합은 시그널을 비트 마스크로 표현
    • 비트 하나가 시그널 하나를 가리킴
      • 각비트가 특정 시그널과 일대일로 연결되어 있음
      • 비트값이 0인 경우에, 해당 시그널이 설정되지 않은 상태
      • 비트값이 1인 경우에, 해당 시그널이 설정된 상태
  • UNIX 계열의 운영체제에서 시그널 집합은 sigset_t자료형으로 정의되어 있음
    • 정수형 또는 구조체의 자료형으로, 운영체제에 따라 다름

시그널 집합 함수

sigemptyset() 함수

#include <signa;.h>

int sigemptyset(sigset_t * set);
  • 설명
    • 시그널 집합의 모든 비트를 0dmfh tjfwjdgka
  • 인자
    • set : 시그널 집합의 포인터
  • 반환
    • 성공시 0
    • 오류시 -1

sigfillset() 함수

#include <signal.h>

int sigfillset(sigset_t * set);
  • 설명
    • 시그널 집합의 모든 비트를 1로 설정
  • 인자
    • set : 시그널 집합의 포인터
  • 반환
    • 성공시 0
    • 오류시 -1

sigaddset() 함수

#include <signal.h>

int sigaddset(sigset_t * set,int signum);
  • 설명
    • 시그널 집합에서 지정한 시그널 비트를 1로 설정함
  • 인자
    • set : 시그널 집합의 포인터
    • signum : 시그널의 번호
  • 반환
    • 성공시 0
    • 오류시 -1

sigdelset() 함수

#include <signal.h>

int sigdelset(sigset_t * set, int signum);
  • 설명
    • 시그널 집합에서 지정한 시그널비트를 0으로 설정
  • 인자
    • set : 시그널 집합의 포인터
    • signum : 시그널의 번호
  • 반환
    • 성공시 0
    • 오류시 -1

sigismember() 함수

#include <signal.h>

int sigismember(sigset_t * set, int signum);
  • 설명
    • 시그널 집합에서 지정한 시그널의 설정 유무를 확인
  • 인자
    • set : 시그널 집합의 포인터
    • signum : 시그널의 번호
  • 반환
    • 성공시 0
    • 오류시 -1

sigset_t 사용 예시

#include <dirent.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char const *argv[]) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);

    printf("SIGUSR1 : %d\\n", sigismember(&set, SIGUSR1));
    printf("SIGUSR2 : %d\\n", sigismember(&set, SIGUSR2));

    return 0;
}

sigprocmask() 함수

#include <signal.h>

int sigprocmask(int how, const sigset_t * set,sigset_t * oldset);
  • 설명
    • 시그널 집합을 이용하여 시그널을 블록 상태로 설정하거나 해제할 수 있음
  • 인자
    • how : 시그널 상태 처리에 대한 플래그
      • SIG_BLOCK : set인자에 지정한 시그널 집합을 시그널 마스크에 추가함
      • SIG_UNBLOCK : set인자에 지정한 시그널 집합을 시그널 마스크에서 헤제함
      • SIG_SETMASK : set인자에 지정한 시그널 집합으로 시그널 마스크를 대체함
    • set : 시그널 집합의 포인터
    • oldset : 변경 전의 시그널 집합을 저장할 공간에 대한 포인터
  • 반환
    • 성공시 0
    • 오류시 -1

시그널 집합 함수

  • 시그널 집합 생성
    • sigemptyset(&set);
    • sigemptyset(&oldset);

  • 시그널 집합에 시그널 추가
    • sigaddset(&set, SIGINT);
    • sigaddset(&set,SIGUSR1);

  • 시그널 집합을 이용하여 블록 상태 목록에 추가
    • sigprocmask(SIG_BLOCK,&set,&oldset);

  • 시그널 집합에서 시그널 삭제
    • sigdelset(&set,SIGINT);

  • 시그널 집합을 이용하여 블록상태 목록에서 해제
    • sigprocmask(SIG_UNBLOCK, &set,&oldset);

  • 시그널 집합을 이용하여 블록 상태 목록 대체
    • sigprocmask(SIG_SETMASK, &set,&oldset);

sigprocmask() 함수 예시

#include <dirent.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

void mySigHandler(int signum) {
    if (signum == SIGUSR1) {
        puts("Hello!\\n");
    }
}

int main(int argc, char const *argv[]) {
    int cnt = 10;

    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);
    sigprocmask(SIG_BLOCK, &set, NULL);

    signal(SIGUSR1, mySigHandler);
    printf("My PID Is %d\\n", getpid());
    while (cnt > 0) {
        printf("%d\\n", cnt);
        fflush(stdout);
        cnt -= 1;
        sleep(1);
    }
    return 0;
}

struct sigaction 구조체

#include <signal.h>

struct sigaction{
	int sa_flags; //시그널 전달 방법에 대한 플래그
	void (*sa_handler)(int); // 시그널을 처리할 동작 지정
	void (*sa_sigaction)(int, siginfo_t *, void *); //시그널을 처리할 동작 지정
	sigset_t sa_mask; // 시그널 핸들러가 동작중일, 블록할 시그널 집합
};
  • sa_handler : 시그널을 처리할 동작에 대한 지정(SA_SIGINFO가 설정되어 있지 않으면, sa_handler에 지정)
  • sig_action : 시그널을 처리할 동작에 대한 지정 (SA_SIGINFO가 설정되어 있으면, sa_sigaction에 지정)
  • sa_flags
    • SA_RESETHAND : 시그널의 기본 처리방법은 SIG_DFL로 재설정 되고, 시그널이 처리되는 동안 시그널을 블록하지 않음
    • SA_NODEFER : 시그널이 처리되는 동안 유니스 커널에서 해당 시그널을 자동으로 블록하지 못함
    • SA_SIGINFO
      • 시그널 번호 외에 추가 인자 두 개가 시그널 핸들러로 전달됨
    • SA_RESTART
      • 시스템은 시그널 핸들러에 의해 중지된 기능을 재시작 하게 함
    • SA_NOCLDWAIT
      • 시그널이 SIGCHILD면 시스템은 자식프로세스가 종료될때 좀비 프로세스를 만들지 않음
    • SA_NOCLDSTOP
      • 시그널이 SIGCHILD면 자식프로세스가 중지 또는 재시작 할때, 부모 프로세스에 SIGCHILD시그널을 전달하지 않음

sigaction() 함수

#include <signal.h>

int sigaction(int signum, const struct sigaction *act,struct sugaction *oldact);
  • 설명
    • 시그널 핸들러와 시그널을 처리하는 과정을 제어함
  • 인자
    • signum : 시그널의 번호
    • act : 시그널 처리에 대한 정보를 담은 메모리 공간에 대한 포인터
    • oldact : 변경 전의 정보를 저장할 메모리 공간에 대한 포인터
  • 반환
    • 성공시 0
    • 오류시 -1

sigaction() 함수 예시

 

#include <dirent.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

void mySigHandler(int signum) {
    int i = 0;

    if (signum == SIGUSR1) {
        puts("SIGUSR1 handling start\\n");

        for (i = 5; i > 0; --i) {
            printf("%d .. ", i);
            fflush(stdout);
            sleep(1);
        }
        puts("SIGUSR1 handled!");
    } else if (signum == SIGUSR2) {
        puts("SIGUSR2 handled!");
    }
}

int main(int argc, char const *argv[]) {
    sigset_t set;
    struct sigaction act;

    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);

    act.sa_flags = SA_NODEFER;
    act.sa_handler = mySigHandler;
    act.sa_mask = set;

    sigaction(SIGUSR1, &act, NULL);
    signal(SIGUSR1, mySigHandler);

    printf("My PID is %d\\n", getpid());
    while (1) {
        pause();
    }

    return 0;
}