OS,  Study

[OS] Signal 동작

Process간 통신을 하기 위해 signal을 사용한다. 우리가 평소에 자주 사용하는 많은 명령어들이 signal로 process에게 전달된다.

  • Ctrl+c
  • Ctrl+z
  • kill 명령어
  • 잘못된 메모리 참조

우리가 인위적으로 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

Signal 중에 1 ~ 31까지는 RT가 안 붙고, 34번부터 RT가 붙는데 이런 signal들은 Real Time 의 약자로 network 관련 프로그램에 사용된다.
Signal 전체를 다 외울 필요는 없고, 대표적인 signal을 보면 된다.

NumberNameReturnEvent Condition
1SIGHUP종료터미널과 연결이 끊어졌을 때
2SIGINT종료인터럽트로 Ctrl+c 입력 시
3SIGQUITCore dumpCtrl+\ 입력 시
4SIGILLCore dump잘못된 명령 사용
5SIGTRAPCore dumptrace, breakpoint에서 trap 발생
6SIGABRTCore dumpabort 함수에 의해 발생
9SIGKILL종료강제 종료시 (Handler 설정 불가)
10SIGUSR1종료사용자 정의 signal 1
11SIGSEGVCore dumpSegmentation fault시
12SIGUSR2종료사용자 정의 signal 2
13SIGPIPECore dump파이프 처리 잘못 했을 때
14SIGALRMCore dumpAlarm에 의해 발생
15SIGTERM실행 프로그램 종료
17SIGCHLD무시Child process 상태 변할 시
18SIGCONT무시중지 된 process 실행 시
19SIGSTOP중지이 signal 받을 시 SIGCONT signal 받을 때 까지 process 중지 (Handler 설정 불가)
20SIGSTP중지Ctrl+z 입력 시

Signal의 처리 방식은 크게 4가지로 나뉜다.

  • 종료: 특별한 처리 방법을 선택하지 않음
  • 무시
  • 미리 등록한 handler 처리
  • Core dump

Example

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

void catchint(int signo)
{
    printf(" SIGINT Receive\n");
}  

int main()
{
    signal(SIGINT,catchint);

    printf("sleep call #1\n");      sleep(1);
    printf("sleep call #2\n");      sleep(1);
    printf("sleep call #3\n");      sleep(1);
    printf("sleep call #4\n");      sleep(1);
    printf("Exiting");
    return 0;
}
$ ./a.out 
 sleep call #1
 ^C SIGINT Receive
 sleep call #2
 ^C SIGINT Receive
 sleep call #3
 ^C SIGINT Receive
 sleep call #4
 ^C SIGINT Receive

위 프로그램은 Ctrl+c를 눌러 SIGINT signal이 왔을 때 handler를 호출하는 프로그램이다.


사용자 정의 Signal

사용자가 임의로 signal을 등록하는 프로그램은 다음과 같다. 사용자 정의는 10번 또는 12번이다.

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

void handler(int sig){
    printf("signal no(%d) Received\n",sig);
}

int main() {
    if(signal(SIGUSR1,handler)==SIG_ERR)    {   
        fprintf(stderr,"cannot set USR1\n");
        exit(1);
    }    
    if(signal(SIGUSR2,handler)==SIG_ERR)  {
        fprintf(stderr,"cannot set USR2\n");
        exit(1);
    }   
    for(;;)    
        pause();
    return 0;
}
$ ./a.out 
 signal no(10) Received
 signal no(12) Received

----------------------------------------------------

 shumin@mercury:~$ ps -ef | grep a.out | grep -v grep
 shumin   30292 30034  0 21:46 pts/14   00:00:00 ./a.out
 shumin@mercury:~$ kill -10 30292
 shumin@mercury:~$ kill -12 30292

위 프로그램을 실행시키고 난 뒤에 다른 터미널에서 해당 process ID를 통해 signal을 전달한 결과, 우리가 등록한 handler를 호출했다.

Example (kill 사용)

Source code에서도 kill을 system call 함수인 kill()을 통해 사용할 수 있다.

// parent.c
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#define NUMCHILD 3
int main(int argc, char *argv[]) {
    int pid;
    int chpid[NUMCHILD];
    int i, status;

    for(i=0;i<NUMCHILD;i++)  {
        if((pid=fork())==0)
            execlp("./child","./child", (char *)0);
        chpid[i] = pid;
    }
    printf("parent : %d child process run\n",NUMCHILD);
    sleep(5);
    for(i=0;i<NUMCHILD;i++)
        kill(chpid[i],SIGINT);
    return 0;
}
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void sig(int sig) {
    printf("child die(%d)\n",getpid());
}

int main() {
    signal(SIGINT,sig);
    pause();
    return 0;
}
$ ./sig_parent 
 sig_parent : 3 child process run
 child die(30596)
 child die(30597)
 child die(30595)

위 프로그램은 3 개의 child process를 생성한 뒤에 5초동안 sleep 한 뒤에 모든 child process를 종료시킨다.

Leave a Reply

Your email address will not be published. Required fields are marked *