티스토리 뷰

728x90

 지난 포스팅에서 시그널의 개념에 대해 알아보았습니다. 이번 시간에는 SIGNAL 관련 예제를 알아보겠습니다~

1. 무한 루프 프로그램에게 시그널 보내기

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
	
	while(1){
		printf("pid : %d \n",getpid());
		sleep(1);
	}
	return 0;
}

 다음은 무한으로 자신의 PID를 출력하는 프로그램입니다. 프로그램을 실행시키고 시그널을 보내겠습니다! 이때 [PID]에는 출력되는 자신의 pid를 입력하시면 됩니다. (ex. 2601)

 

  1. SIGINT -> (1) Ctrl+C 누르기 (2) 터미널에 $kill -SIGINT [PID] 
  2. SIGSTOP-> (1) Ctrl+Z 누르기 (2) 터미널에 $kill -SIGSTOP [PID] 
  3. SIGCONT -> (1) fg 누르기 (2) 터미널에 $kill -SIGCONT [PID]
  4. 4. SIGQUIT -> (1) Ctrl+\ (2) 터미널에 $kill -SIGQUIT [PID]

 3번에서 SIGSTOP 한거 SIGCONT로 다시 원상복귀 시키면 터미널에서 제어권이 없어져서 Ctrl+C로 안죽습니다. 이때는 SIGKILL(9)로 죽여야합니다!

 1,2,3,4를 진행하시면 SIGNAL에 의해 인터럽트가 실행되는걸 직접 확인할 수 있습니다!

2. GDB 이용하여 Core 파일 분석하기

시그널 default action 에는 core dump file을 생성하는 액션이 있습니다. 이번에는 GDB(GNU debugger) 를 이용해서 Core 파일을 분석해보겠습니다.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void foo(){
	printf("hello foo\n");
}

void goo(){
	printf("hello goo\n");
	foo();
}


int main(){
	
	while(1){
		goo();
		printf("pid : %d \n",getpid());
		sleep(1);
	}
	return 0;
}
gcc myprog.c -g -o prog_debug //디버깅용
gcc myprog.c -o prog

 위의 코드를 이용해 컴파일을 진행합니다. 이후 $gdb ./prog_debug 명령어를 입력할 경우 디버거가 실행됩니다. 

디버거에서 l (소스코드 나옴), break, run, next, step ,print(), backtrace를 이용해 디버거를 실행시켜보면 됩니다!!


Sigaction 

 이제 본격적으로 Signal Handler에 대해 다뤄보겠습니다. 지금까지는 kernal에서 Signal이 프로세스에 전달되었을경우 어떤 방식으로 진행되는지 확인하는 예제를 살펴봤습니다. 이제는 Signal이 들어왔을때 default를 진행하는 것이 아니라 직접 제작한 함수를 signal handler로 설정해서 그 함수를 실행하도록 해보겠습니다.

 signal handler를 사용하기 전에 앞서, sigaction에 대해 알아보겠습니다. 원래 리눅스에서는 signal를 처리할때 signal()함수를 사용했습니다. 하지만 더 발전된 함수인 sigaction()이 나온 이후로 sigaction()을 사용하게 됐습니다. sigaction()이 더 많이 쓰이는 이유는 sigaction()에서는 시그널이 발생하여 처리 중일때에는 다른 시그널이 발생하더라도 블록이 되도록 저장할 수 있기 때문입니다. 

 다음은 sigaction의 struct입니다. 각각의 변수가 어떤 일을 하는지 주석에 적었으니 확인해주세요!

struct sigaction 
{
    void (*sa_handler)(int); //signal의 행동을 수행
    void (*sa_sigaction)(int, siginfo_t *, void *); 
    sigset_t sa_mask; //마스크값, signal handler가 수행되는 동안 signal을 block, unblock하는 요소
    int sa_flags; //signal의 특성을 결정하는 변수, | 사용해서 여러개 선언 가능
    void (*sa_restorer)(void); 
}

3. SIGINT Handler 수정

 sigaction을 사용해서 Ctrl+C를 누르면 실행되는 SIGINT의 핸들러를 수정하는 코드입니다. Ctrl+C를 누를 경우 Hello int_handler signo가 출력되도록 코드를 작성했습니다! 

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

void my_int_handler(int sig){
    printf("Hello int_handler %d\n",sig);
}

int main(){

    struct sigaction sa; //sigaction struct 선언

    sigemptyset(&sa.sa_mask); //sigaction sa_mask 초기화
    sa.sa_flags=0; //flags 초기화
    sa.sa_handler=my_int_handler; //sa_handler 설정

    if (sigaction(SIGINT,&sa,NULL)==-1) //에러가 발생한 경우
    {   
        perror("sigaction");
        exit(0);
    }

    for(;;){
        printf("my pid: %d\n",getpid());
        sleep(1);
    }

}

4. SIGQUIT handler 수정 

SIGINT와 SIGQUIT handler도 같이 수정하겠습니다.

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

void my_int_handler(int sig){
    printf("Hello int_handler %d\n",sig);
}

void my_quit_handler(int sig){
    printf("Good bye \n");
    exit(0);

}
int main(){

    struct sigaction sa;
    struct sigaction sa2;

    sigemptyset(&sa.sa_mask);
    sa.sa_flags=0;
    sa.sa_handler=my_int_handler;

    sigemptyset(&sa2.sa_mask);
    sa2.sa_flags=0;
    sa2.sa_handler=my_quit_handler;

    if (sigaction(SIGINT,&sa,NULL)==-1) 
		// &sa : 기존에 지정되어있던걸 받아오는 것
		// NULL 넣어주는 이유 : 이전에 설정되어있던 시그엑션을 받아오지 않겠다는 뜻 
    {   
        perror("sigaction");
        exit(0);
    }

    if (sigaction(SIGQUIT,&sa2,NULL)==-1)
    {   
        perror("sigaction");
        exit(0);
    }

    for(;;){
        printf("my pid: %d\n",getpid());
        sleep(1);
    }

    
}

5. Alarm 발생시키기

 alarm() 함수를 이용해서 SIGALARM signal handler를 만들어보겠습니다.

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

void my_int_handler(int sig){
    printf("Hello int_handler %d\n",sig);
}

void my_quit_handler(int sig){
    printf("Good bye \n");
    exit(0);

}
void my_alarm_handler(int sig){
    printf("Alarm! \n");
    alarm(1);
}

int main(){

    struct sigaction sa;
    struct sigaction sa2;
    struct sigaction sa3;

    sigemptyset(&sa.sa_mask);
    sa.sa_flags=0;
    sa.sa_handler=my_int_handler;
    sigaction(SIGINT,&sa,NULL);

    sigemptyset(&sa2.sa_mask);
    sa2.sa_flags=0;
    sa2.sa_handler=my_quit_handler;
    sigaction(SIGQUIT,&sa2,NULL);

    sigemptyset(&sa3.sa_mask);
    sa3.sa_flags=0;
    sa3.sa_handler=my_alarm_handler;
    sigaction(SIGALRM,&sa3,NULL);

    alarm(1);

    for(;;){
        printf("my pid: %d\n",getpid());
        sleep(1);
    }

    
}

 이번 포스팅에서는 시그널 핸들러를 수정하는 코드를 작성했습니다. 다음 시간에는 sigprocmask 관련 예제에 대해서 알아보겠습니다~

728x90
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함