반응형
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 옵션을 생각하시면 됩니다.
- 따라서 SIGKILL에 대한 시그널 핸들러는 만들 수 없습니다.
- 들어온 요청을 다 처리하고 종료한다든가, 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
참고
- [프로그래밍] Graceful Shutdown과 SIGINT/SIGTERM/SIGKILL: https://2kindsofcs.tistory.com/53
- [Linux] 프로세스 종료신호 후킹하기: https://tttsss77.tistory.com/212
- [Linux] Kill option 정리: https://bigsun84.tistory.com/355
- [Linux] 시그널이란? 시그널 종류, 상황, 유사 시그널 차이: https://jhnyang.tistory.com/143
반응형
'Study: ComputerScience(CS) > CS: Linux' 카테고리의 다른 글
[Linux] 일정 시간마다 작업 실행시키기(crontab) (0) | 2022.08.12 |
---|---|
[Linux] ln 명령어(feat. 리눅스 파일 링킹): 하드링크(Hard Link) VS 심볼릭링크(Symbolic Link) (0) | 2022.08.09 |
[Linux] 프로세스 종류 및 특징 (feat. Demon과 Background Process의 차이) (0) | 2022.07.31 |
[Linux] 리눅스 로컬네임서버(/etc/hosts) 설정: DNS 서버 만들기 (0) | 2022.07.28 |
[Linux] 리눅스 파일 시스템(File System) (0) | 2022.07.25 |