리눅스에서는 프로세스에 Signal을 전달할 수 있다. 그리고 프로세스는 이 Signal에 의한 처리를 막기 위해 원하는 Signal을 Block 할 수 있다. 이를 가능하게 하는 것이 sigprocmask
이다.
sigset_t
우리는 원하는 Signal을 block 하기 위하여 특정 구조체를 사용하여야 한다. 그 구조체는 sigset_t
이다. 사실 sigset_t
의 구조는 그리 복잡하지 않다. 정수형 자료형의 배열이라고 생각해도 무관하다. 비트마스크를 사용하기 때문에 사용되는 Signal의 종류 수만큼의 비트 수가 필요하다. kill -l
명령어를 통해 어떤 signal이 몇번인지 볼 수 있다.
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
만약 위의 64개의 경우를 모두 표현하고 싶다면 64비트 이상이 되도록 배열의 크기를 그에 맞게 조정하여 사용해야 하는데, 그 결과물이 sigset_t
이다. sigset_t
는 위의 경우들을 비트마스크로 모두 표현하고도 남는 크기를 가지고 있다. 64비트 ubuntu 18.04 버전 기준으로 sizeof(sigset_t)
의 결과를 보면 128
이라는 수를 볼 수 있다. 즉, 1024 비트 라는 것인데, 왜 이렇게 큰지는 모르겠으나 일단 64개의 조합의 경우의 수를 표현하는 데에는 문제가 없다. 이제 우리는 관련 함수들을 이용하여 block 하고싶은 Signal들을 sigset_t
에 담을 것이다. 앞에서 길게 적어놓았지만, 단순하게 그저 다루고자 하는 signal의 집합이라고 생각하면 된다.
sigset_t 조작하기
sigset_t에 원하는 signal들을 담을 것이다. 그전에 어떤 sigset_t를 비우거나 꽉 채우는 함수 두개를 소개한다.
sigfillset(sigset_t* set)
: set에 모든 signal을 추가한다. 성공시 0, 실패시 -1 반환sigemptyset(sigset_t* set)
: set의 모든 signal을 비운다. 성공시 0, 실패시 -1 반환
아래 함수는 sigset_t에 원하는 signal을 추가하거나 제거하는 함수들이다.
sigaddset(sigset_t set, int signo)
: set에 signo에 해당하는 signal을 추가한다. 성공시 0, 실패시 -1을 반환한다.sigdelset(sigset_t set, int signo)
: set에서 signo에 해당하는 signal을 제거한다. 성공시 0, 실패시 -1을 반환한다.
아래는 사용 예시이다.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(void){
sigset_t mask;
sigfillset(&mask); // mask에 모든 signal이 추가된다.
sigemptyset(&mask); // mask에서 모든 signal을 제거한다.
sigaddset(&mask,SIGINT); // mask에 SIGINT signal을 추가한다.
sigdelset(&mask,SIGINT); // mask에서 SIGINT signal을 제거한다.
return 0;
}
sigprocmask
sigprocmask
를 사용하면 SIGKILL 과 SIGSTOP을 제외하고 원하는 signal들을 block할 수 있다. (SIGKILL과 SIGSTOP은 block이 불가능하고, handler도 설정할 수 없다는 것을 기억하자.) sigprocmask
의 형태는 다음과 같다.
sigprocmask(int how, const sigset_t *set, sigset_t *oset)
how
값에 따라 sigprocmask
의 동작이 다르다. how값은 SIG_BLOCK
, SIG_SETMASK
, SIG_UNBLOCK
중 하나이어야 한다. 아래는 how
값에 따른 동작이다.
SIG_BLOCK
: set에 등록된 signal들의 block을 설정한다.SIG_UNBLOCK
: set에 등록된 signal들의 block을 해제한다.SIG_SETMASK
: set에 등록된 signal들을 block 하도록 설정하고 set에 등록되지 않은 signal들의 block은 해제한다.
여기서 SIG_BLOCK
과 SIG_SETMASK
의 차이는 SIG_BLOCK
은 sigprocmask
사용 이전에 block 하던 signal들과 set에 등록한 signal들을 같이 block을 하지만, SIG_SETMASK
은 set에 등록된 signal들만 block 한다는 차이점이 있다.
oset
은 sigprocmask
를 사용하기 이전에 block 하던 signal들의 정보를 담을 sigset_t*
이다. 담고 싶지 않다면 null
값을 주면 된다. 만약 set
의 값이 null값인데 oset
의 값이 설정되어 있다면, 지금 block 하도록 설정된 signal들의 집합을 oset
에 담아서 주는 일 말고 다른 일은 하지 않는다.
sigprocmask 사용 예제
Ctrl-c를 누름으로 SIGINT
signal을 보낼 수 있다. 프로세스가 SIGINT
signal을 받으면 기본적으로는 해당 프로세스는 종료된다. 그렇다면 SIGINT
signal을 block 하여 crtl+c를 눌러도 프로그램이 종료되지 않게 할 수 있을까?
아래 프로그램은 처음 시작하자마자 SIGINT
signal을 block 한다. 그리고 10초 후 SIGINT
를 block 하였던 것을 원래대로 되돌린다. 이후 10초간 아무것도 하지 않다가 프로그램을 종료한다. 아래 c 코드를 컴파일하여 만든 실행파일을 sigp라 하자.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main(void){
sigset_t mask,prev;
sigemptyset(&mask);
sigaddset(&mask,SIGINT);
sigprocmask(SIG_BLOCK,&mask,&prev);
printf("SIGINT blocked.\n");
sleep(10);
printf("SIGINT unblocked.\n");
sigprocmask(SIG_SETMASK,&prev,0);
sleep(10);
printf("End\n");
}
아래는 몇 가지 경우에 대한 결과이다. 일단, 20초 동안 건들지 않은 경우에는 마지막 End
까지 출력이 되면서 프로그램이 종료된다.
$ ./sigp
SIGINT blocked.
SIGINT unblocked.
End
$
SIGINT
의 block을 해제한 후 Ctrl+c를 누르는 경우 프로그램이 바로 종료되는 것을 볼 수 있다.
$ ./sigp
SIGINT blocked.
SIGINT unblocked.
^C
$
그렇다면 SIGINT
가 block 되어 있는 동안 Ctrl+c를 누르면 무슨 일이 일어날까?
$ ./sigp
SIGINT blocked.
^C
^C
^C
^C
SIGINT unblocked.
$
위 결과를 보면 SIGINT
가 block 되어있는 동안 Ctrl+c를 아무리 많이 눌러도 프로그램이 종료되지 않는 것을 볼 수 있었다. 이후 SIGINT
의 block을 해제해주면 block 되어있었던 SIGINT
signal이 프로그램으로 들어오게 되고 그 결과 프로그램이 종료되는 것을 볼 수 있다.
'Study > System Programming' 카테고리의 다른 글
lseek, pread, pwrite 사용하기 (0) | 2021.11.17 |
---|---|
open, read, write, close 사용하기 (0) | 2021.11.15 |
wait, waitpid의 사용 (0) | 2021.10.19 |
fork 사용하기 (0) | 2021.10.17 |
댓글