본문 바로가기
[OS]/Linux

[참고][Linux] Graceful Shutdown이란?

by METAVERSE STORY 2024. 4. 7.
반응형
728x170

 

 

 

Graceful Shutdown 이란?

우아한 종료라고 직역하면 뭔가 어색하지만, 그 역의 경우를 생각해보면 제법 어울리는 표현이라는 생각이 듭니다. 우아한 종료는 프로그램이 종료될 때 최대한 side effect가 없도록 로직들을 잘 처리하고 종료하는 것을 말합니다.

Gracueful Shutdown <-> Hard Shutdown

hard shutdown: 종료 시그널과 동시에 모든 작업을 중단합니다.

  • e.g. 그대로 컴퓨터를 바로 끄고 퇴근하는 것

graceful shutdown: 하고있던 작업을 적절히 마무리한 뒤 종료되는 것이 핵심입니다.

  • e.g. http 요청을 처리하는 웹서버라면 서버가 종료되기 전, 기존 처리 중이던 요청들을 모두 처리하고 사용했던 리소스(파일, 소켓, DB 연결등)를  닫은 뒤 종료되도록 구현하는 것이다. 

의도한 종료든 의도하지 않은 종료든 간에 최대한 side effect를 줄이기 위해 graceful하게 shutdown 해야합니다. 

 

SIGINT / SIGTERM / SIGKILL

그러면 Graceful Shutdown은 어떻게 구현하는가? 프로그램을 종료하라는 유닉스 신호를 catch하여 처리 로직을 하는 형태로 구현할 수 있습니다. 

  • 유닉스 신호는 유닉스 계열 등의 운영체제에서 사용하는 프로세스 간 통신의 일종입니다.
  • e.g. 프로그램 실행 중에 ctrl + c를 입력하면 SIGINT 시그널이 프로세스로 가게 되고, 대게 프로그램이 종료됩니다.

SIGTERM VS SIGINT

  • SIGTERM은 프로그램을 종료하는 일반적인 방법입니다. 
  • SIGINT는 유저가 직접 프로그램 종료를 지시했다는 것이 명확하다는 점에서 차이가 있습니다.
  • SIGINT의 INT는 interrupt에서 온 것인데, 결과적으로 프로그램이 종료된다는 점에서는 SIGTERM과 다를 바 없습니다.
  • SIGINT와 SIGTERM은 둘 다 catch가 가능하여, 각 시그널을 전송받았을 때 자신만의 로직을 처리할 수 있다. (if process.Get("SIGTERM") ~ 와 같이)
    • 특정 시그널을 받았을 때 실행하는 함수를 시그널 핸들러(signal handler)라고 합니다. 
    • 특정 시그널을 catch 할 수 있다는 것은, 시그널 핸들러를 제작할 수 있다는 뜻이기도 하지만 동시에 무시할 수 있다는 뜻이기도 합니다.
      • 시그널을 catch하여 처리하는 것을 시그널 후킹이라고도 합니다.
  • 과거에는 SIGINT가 어떻게 활용되었는지 모르겠지만, 현재 대부분의 프로그램들은 SIGINT를 받았을 때 프로그램을 종료하는 형태로 구현되어있다. 

SIGTERM / SIGINT vs SIGKILL

  • SIGKILL은 말 그대로 프로세스를 kill 하여 catch가 불가능합니다.
    • 따라서 SIGKILL에 대한 시그널 핸들러는 만들 수 없습니다.
      • 이 외에도 SIGSTOP의 경우 시그널 핸들러를 만들 수 없습니다.
    • linux 명령어 중 kill -9 옵션을 생각하시면 됩니다.
  • 들어온 요청을 다 처리하고 종료한다든가, db에 유저가 작성중이던 텍스트 데이터를 저장한다든가 같은 로직을 처리하는 것이 불가능합니다. 

 

SIGNAL의 종류

대표적인 위 3가지 신호를 포함하여 다양한 종류의 신호가 있으며, 리눅스의 kill 명령어에 -l 옵션을 주어 확인할 수 있습니다.

// 시그널의 종류를 출력
$ 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 SIGHUP HangUp의 약어로 로그아웃과 같이 터미널에서 접속이 끊겼을 때 보내지는 시그널입니다.
데몬 관련 환경 설정 파일을 변경 후 적용하기 위해 재시작할 때 이 시그널을 사용합니다.
종료
2 SIGINT [CTRL]+[c] 입력 시에 보내지는 시그널로 실행을 종료할 때 사용합니다. 종료
3 SIGQUIT [CTRL]+[₩] 입력 시에 보내지는 시그널로 실행을 종료한 후 코어를 덤프할 때 사용합니다. 코어 덤프
4 SIGILL illegal instruction의 약자로 잘못된 명령을 사용했을 때 발생합니다. 코어 덤프
5 SIGTRAP trace, breakpoint에서 TRAP 시그널이 발생합니다. 코어 덤프
6 SIGABRT abort의 약자로 비정상종료 함수에 의해 발생합니다. 코어 덤프
7 SIGBUS 메모리 접근 에러시 발생하는 시그널입니다. 코어 덤프
9 SIGKILL KILL! 프로세스를 강제로 종료시키는 시그널입니다. 종료
11 SIGSEGV Invalid memory reference 코어 덤프 + 종료
15 SIGTERM Terminate의 약자로 가능한 정상 종료시키는 시그널로 kill 명령의 기본 시그널입니다. 종료
17 SIGCHLD 자식 프로세스가 stop 되거나 종료되었을 때 부모에게 전달되는 신호입니다. 무시
18 SIGCONT continue의 약자로 STOP 시그널에 의해 정지된 프로세스를 다시 실행시킬 때 사용합니다. 재시작
19 SIGSTOP 터미널에서 입력된 정지 시그널로 SIGCONT 시그널로 재실행 시킬 수 있습니다. 중지
20 SIGTSTP 실행 저지 후 다시 실행을 계속하기 위해 대기시키는 시그널입니다.
[CTRL]+[₩] 입력 시에 보내지는 시그널로 실행을 중지할 때 사용합니다.
SIGCONT 시그널로 재실행 시킬 수 있습니다.
중지
29 SIGIO 비동기 입출력이 발생했을 경우 발생하는 시그널입니다. 종료

 

특정 Signal이 발생하고 싶은 경우

명령어를 통해 특정 프로세스에 시그널을 보내고 싶다면? 다음과 같이 사용할 수 있습니다.

kill <Option> <ProcessID>

예를 들어,

kill -9 14345
kill -SIGKILL 14345

 

Signal Handler 예제 코드

void signal_handler(int signo)
{
    LOG_INF("signal recv: {}", signo);
    g_main->stop();
    signal( SIGINT, SIG_DFL);
    signal( SIGTERM, SIG_DFL);
}

int main(int argc, char *argv[]){
    signal( SIGINT, signal_handler);
    signal( SIGTERM, signal_handler);
}

시그널은 x86_64-linux-gnu에 다음과 같이 구현되어 있으며, 34번 이후의 signal은 직접 만들어 사용할 수 있습니다.

#define	SIG_ERR	 ((__sighandler_t) -1)	/* Error return.  */
#define	SIG_DFL	 ((__sighandler_t)  0)	/* Default action.  */
#define	SIG_IGN	 ((__sighandler_t)  1)	/* Ignore signal.  */

/* Historical signals specified by POSIX. */
#define	SIGHUP		1	/* Hangup.  */
#define	SIGQUIT		3	/* Quit.  */
#define	SIGTRAP		5	/* Trace/breakpoint trap.  */
#define	SIGKILL		9	/* Killed.  */
#define SIGBUS		10	/* Bus error.  */
#define	SIGSYS		12	/* Bad system call.  */
#define	SIGPIPE		13	/* Broken pipe.  */
#define	SIGALRM		14	/* Alarm clock.  */

/* New(er) POSIX signals (1003.1-2008, 1003.1-2013).  */
#define	SIGURG		16	/* Urgent data is available at a socket.  */
#define	SIGSTOP		17	/* Stop, unblockable.  */
#define	SIGTSTP		18	/* Keyboard stop.  */
#define	SIGCONT		19	/* Continue.  */
#define	SIGCHLD		20	/* Child terminated or stopped.  */
#define	SIGTTIN		21	/* Background read from control terminal.  */
#define	SIGTTOU		22	/* Background write to control terminal.  */
#define	SIGPOLL		23	/* Pollable event occurred (System V).  */
#define	SIGXCPU		24	/* CPU time limit exceeded.  */
#define	SIGXFSZ		25	/* File size limit exceeded.  */
#define	SIGVTALRM	26	/* Virtual timer expired.  */
#define	SIGPROF		27	/* Profiling timer expired.  */
#define	SIGUSR1		30	/* User-defined signal 1.  */
#define	SIGUSR2		31	/* User-defined signal 2.  */

/* Nonstandard signals found in all modern POSIX systems
   (including both BSD and Linux).  */
#define	SIGWINCH	28	/* Window size change (4.3 BSD, Sun).  */

/* Archaic names for compatibility.  */
#define	SIGIO		SIGPOLL	/* I/O now possible (4.2 BSD).  */
#define	SIGIOT		SIGABRT	/* IOT instruction, abort() on a PDP-11.  */
#define	SIGCLD		SIGCHLD	/* Old System V name */

/* Not all systems support real-time signals.  bits/signum.h indicates
   that they are supported by overriding __SIGRTMAX to a value greater
   than __SIGRTMIN.  These constants give the kernel-level hard limits,
   but some real-time signals may be used internally by glibc.  Do not
   use these constants in application code; use SIGRTMIN and SIGRTMAX
   (defined in signal.h) instead.  */
#define __SIGRTMIN	32
#define __SIGRTMAX	__SIGRTMIN

 

 

 

 

 

출처 : https://csj000714.tistory.com/518

참고


반응형
그리드형

댓글