리눅스

01. 리눅스_시그널(signal) 정의 및 함수

만수르코딩방 2024. 3. 11. 23:06

Signal 이란?

시그널이란 프로세스 간 특정 이벤트가 발생하였을때 신호를 보내서 알려주는 방식입니다. 시그널은 소프트웨어 인터럽트 라고도 하며 시그널이 발생하면 기존의 프로세스를 중단하고 시그널이 처리될 때까지 인터럽트 처리를 합니다. 이때 발생한 시그널에 따라 정의된 시그널 핸들러를 호출하여 지정된 방법에 따라서 시그널을 처리합니다. 

출처 : Linux Signals

Signal handler란?

시그널 핸들러란 특정 signal을 처리하기 위해 지정된 함수를 뜻합니다. 시그널 처리함수를 커널에게 등록요청을 한 후 특정 시그널이 발생하면 기존의 프로세스를 중단하고 정의된 처리 함수를 호출하여 수행합니다. 한편, SIGKILL, SIGSTOP등의 프로세스 동작 제어와 관련된 신호는 시그널 핸들러에의해 처리 방식을 재정의 하는 것이 불가능 합니다.  

출처 : Linux Signals(https://devopedia.org/linux-signals)

 

시그널 및 프로세스 관련 함수

1) getpid() : 현재 프로세스의 id 확인 

pid_t get pid(void) 

2) psignal() : 특정 시그널에 대한 설명을 출력

void psignal(int sig, const char *s)

3) signal() : 특정 신호가 수신되면 시그널 핸들러를 호출하여 시그널 처리하도록 시그널 처리기 함수를  커널에 등록

sighandler_t ( signal (int sig, void(*func)(int)) )(int);

* 정상 처리 시 : 시그널 핸들러 함수 포인터 리턴, 비정상 처리 시 : SIG_ERR 리턴

4) pause() : 시그널(모든 시그널)을 수신할 때까지 대기상태 전환

int pause(void);

5) kill() : 특정 프로세스에 특정 시그널을 전달 (정상 전달 시 0, 에러 발생 시 -1)

//kill(pid, 0) 프로세스에 시그널 전송 접근 권한 체크하는 방식으로 사용

int kill(pid_t pid, int sig);

* 커맨드 창 kill - l 시그널에 대한 설명

 (SIGINT(2번) : Ctrl + C로 키보드 입력 가능 / 기본동작 종료

  SIGTSTP(20번) : Ctrl + Z로 키보드 입력 가능 /기본 동작 종료

  SIGKILL(9번) : 기본동작 종료 (SIGTSTP,SIGKILL 함수 시그널 핸들러 함수로 시그널 처리 재정의 불가)

  SIGALRM(14번) : 알람수신되면 프로세스 동기화

6) _exit() : 종료 핸들러 호출하지 않고 즉각 종료 (비정상 에러처리 시) 

void _exit ( Status)

7) wait() :  자식 프로세스가 종료될때까지 프로세스 대기

pid_t wait(int *status);

* wail(NULL) retrun 값 : 종료된 자식 프로세스의 pid

* wait(status) 16bit로 구성된 프로세스의 종료 정보 

8) execl() : 파일경로에 접근하여 파일 실행 (실행 파일만 실행 가능)

int execl(const char *path, const char *arg, ...)

9) sleep() : 지정된 시간 동안 추가로 인터럽트가 들어와도 반응하지 않음 (시그널 처리기 내, 시그널 수신 시에 주로 사용) 

bool sleep (unsigned int seconds);

* sleep return값 지정된 시간동안 다른 인스턴스가 공유자원에 접근하려는 시도를 하였을 때 false 리턴,

  인터럽션 없이 지정된 시간동안 인터럽트가 완료되었을 때 true 리턴

10) raise() : sigle process 일때 현재 프로세스에서 시그널 호출, kill(getpid(), sig)와 동일 

int raise(int sig)

11) alarm() : 일정 시간 후에 SIGARLM 발생

unsigned int alarm(unsigned int seconds);

12) fork() : 새로운 프로세스를 생성(자식 프로세스 생성)

pid_t fork(void);

* 비정상 처리 시 : -1 리턴

* 정상 처리 시 : 부모프로세스(자식 프로세스의 pid 리턴), 자식 프로세스 0리턴

 

예제 1) 비동기 처리

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

void sighandler1(int signo){
  printf("\r recv Signal num = %d\n", signo);
  raise(SIGINT); //현재 프로세스에서 sigint를 보내 sighandler2 실행
  printf("\r sighander1 done\n"); //sighandler2 실행이 완료된 이후 수행
}
void sighandler2(int signo){
  printf("\r recv Signal num = %d\n", signo);
  //_exit(0); //정상종료 sighandler, error handler 에서는 _exit 사용 (즉시 종료)
  printf("\r sighandler2 done\n");
}

int main (){
  signal(SIGALRM, sighandler1);
  signal(SIGINT, sighandler2);
  alarm(2); //2초뒤에 알람시그널을 보내고 싶어
  write(1, "wait 10s\n", 9);
  sleep(10); //신호를 받을 때까지 10초간 대기 (추가로 시그널 받지않음)
  write(1, "done\n", 5);
  return 0;
}

 

예제2) sleep, pause 함수

#include <stdlib.h>
#include <stdio.h> //파일입출력
#include <unistd.h>
#include <signal.h>

void sigint_handler(int signo){
  printf("recv SIGINT!\n");
  psignal(signo, "Received signal"); //void psignal(int sig, const char*s)
  sleep(4);
  printf("done SIGINT\n");
}
int main(void){
  printf("my pid %d\n", getpid());
  if (signal(SIGINT, sigint_handler) == SIG_ERR){ //SIG_ERR
    perror("Cannot handle SIGINT!\n");
    _exit(0);
  }
  for (;;) pause(); //waiting a signal
  return 0;
}

 

예제 3) 프로세스간 시그널 처리

//child.c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

int main (){
  int n_count = 0;
  while(1){
    printf("child.c %d\n", n_count);
    n_count++;
    sleep(1);
  }
  return 0;
}
//parent.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>

int pid = 0;
void exitChild(int signo){
  printf("\nAlarm\n");
  kill(pid, SIGINT); //send signal 
}

int main(int argc, char * argv[]){
  pid = fork();
  if(pid == 0){ //child process
    execl("./child", "child", NULL);
    printf("after execl\n");
  }else{ //parent process
    signal(SIGALRM, exitChild);
    alarm(5);
    printf("make alarm\n");
    int status = 0;
    int pid;
    pid = wait(NULL);
    wait(&status);
    int exitnum = status>>8;
    int signalnum = status &= 0xFF;
    printf("pid num :%d\n",pid);
    printf("Child complete! ret: %d, signal:%d\n", exitnum, signalnum);
    return 0;
  }
}

'리눅스' 카테고리의 다른 글

02. 리눅스 _소켓 통신(TCP/IP) 개념과 예제  (0) 2024.03.18