유저 공간 시그널 설정은 어떻게 할까?

 

이번 시간에 시그널을 설정하면 유저 공간에서 커널 공간까지 어떤 흐름으로 처리하는지 소스 코드를 보면서 살펴 보겠습니다. 유저 레벨 프로세스에서 시그널 설정을 어떻게 하는지 소스 코드와 함께 살펴봅시다. 유저 공간에서 시그널을 설정하는 간단한 코드입니다.

 

1 #include <unistd.h>

2 #include <signal.h>

3 #include <stdio.h>

4 #include <stdlib.h>

5

6 void sig_handler(int signum) {

7 switch(signum) {

8 case SIGINT:

9 printf("sig num [%d] \n", signum);

10 break;

11

12 case SIGALRM:

13 printf("sig num [%d] \n", signum);

14 break;

15

16 case SIGUSR1:

17 printf("sig num [%d] \n", signum);

18 break;

19

20 default:

21 printf(" default sig num [%d] \n", signum);

22 }

23 }

24

25 int main()

26 {

27 struct sigaction act;

28 sigset_t set;

29

30 sigemptyset(&(act.sa_mask));

31

32 sigaddset(&(act.sa_mask), SIGALRM);

33 sigaddset(&(act.sa_mask), SIGINT);

34 sigaddset(&(act.sa_mask), SIGUSR1);

35

36 act.sa_handler = sig_handler;

37

38 sigaction(SIGALRM, &act, NULL);

39 sigaction(SIGINT, &act, NULL);

40 sigaction(SIGUSR1, &act, NULL);

41

42 for (;;)

43 pause();

44 }

 

위 코드는 SIGALRM, SIGINT, SIGUSR1 시그널을 sig_handler() 함수를 시그널 핸들러로 등록합니다. 3가지 시그널이 전달되면 sig_handler() 함수를 호출하는 동작입니다.

 

30번째 줄 코드부터 봅시다.

 

30 sigemptyset(&(act.sa_mask));

31

32 sigaddset(&(act.sa_mask), SIGALRM);

33 sigaddset(&(act.sa_mask), SIGINT);

34 sigaddset(&(act.sa_mask), SIGUSR1);

 

30~34번째 줄 코드는 sigemptyset() 함수를 써서 시그널 집합을 초기화한 후 sigaddset() 함수로 SIGALRM, SIGINT, SIGUSR1 시그널을 추가합니다.

 

다음 36~40번째 줄 코드를 분석하겠습니다.

 

36 act.sa_handler = sig_handler;

37

38 sigaction(SIGALRM, &act, NULL);

39 sigaction(SIGINT, &act, NULL);

40 sigaction(SIGUSR1, &act, NULL);

 

act.sa_handler에 sig_handler() 이란 시그널 핸들러 함수를 등록합니다. sigaction() 함수를 호출해서 프로세스가 SIGALRM, SIGINT, SIGUSR1 시그널 중 하나을 받으면 시그널 핸들러로 sig_handler() 함수를 실행하도록 지정합니다.

 

다음 42~43번째 줄 코드를 보겠습니다.

 

42 for (;;)

43 pause();

 

pause() 함수는 시그널을 받을 때까지 기다리는 역할을 수행합니다.

 

다음은 SIGALRM, SIGINT, SIGUSR1 시그널이 발생하면 리눅스 커널에서 호출하는 시그널 핸들러 함수인 sig_handler()를 분석하겠습니다.

 

6 void sig_handler(int signum) {

7 switch(signum) {

8 case SIGINT:

9 printf("sig num [%d] \n", signum);

10 break;

11

12 case SIGALRM:

13 printf("sig num [%d] \n", signum);

14 break;

15

16 case SIGUSR1:

17 printf("sig num [%d] \n", signum);

18 break;

19

20 default:

21 printf(" default sig num [%d] \n", signum);

22 }

23 }

 

 

sig_handler() 함수와 같은 시그널 핸들러는 직접적으로 누가 언제 실행할까요? 주인공은 리눅스 커널입니다. 시그널을 받은 프로세스가 시그널 핸들러를 실행하도록 리눅스 커널은 지원합니다.

 

sig_handler() 함수는 SIGINT, SIGALRM, SIGUSR1 시그널이 전달되면 실행하는 시그널 핸들러 함수입니다. 시그널 번호를 signum 인자로 받아서 출력하는 역할을 수행합니다.

 

시그널은 어떻게 생성할 수 있을까요?

Ctl+C 키를 누르거나 alarm(), kill()과 같은 리눅스 표준 함수를 호출하면 커널은 SIGINT와 SIGALRM 시그널을 생성한 다음 시그널 핸들러로 등록한 sig_handler() 함수를 실행합니다.

 

+ Recent posts