importsignalimportsysimporttimerunning=Truedefgraceful_shutdown(signum,frame):globalrunningprint(f"\nReceived signal {signum}. Shutting down gracefully...")running=False# SIGINT와 SIGTERM 모두 처리signal.signal(signal.SIGINT,graceful_shutdown)signal.signal(signal.SIGTERM,graceful_shutdown)print("Server running. PID:",os.getpid())whilerunning:print("Working...")time.sleep(1)print("Cleanup complete. Goodbye!")
3. 이전 핸들러 저장
1
2
3
4
5
6
7
8
9
10
11
12
13
importsignaldefcustom_handler(signum,frame):print("Custom handler called")# 이전 핸들러 호출ifcallable(old_handler):old_handler(signum,frame)# 이전 핸들러 저장old_handler=signal.signal(signal.SIGINT,custom_handler)# 나중에 복원signal.signal(signal.SIGINT,old_handler)
4. 시그널 무시
1
2
3
4
5
6
7
importsignal# SIGINT 무시 (Ctrl+C가 작동 안 함)signal.signal(signal.SIGINT,signal.SIG_IGN)# 기본 동작 복원signal.signal(signal.SIGINT,signal.SIG_DFL)
5. 알람 타이머 (Unix)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
importsignalimporttimedeftimeout_handler(signum,frame):raiseTimeoutError("Operation timed out!")# 알람 핸들러 설정signal.signal(signal.SIGALRM,timeout_handler)# 5초 후 알람signal.alarm(5)try:print("Starting long operation...")time.sleep(10)# 5초 후 TimeoutErrorexceptTimeoutErrorase:print(e)finally:signal.alarm(0)# 알람 취소
importsignalfromcontextlibimportcontextmanager@contextmanagerdeftimeout(seconds):deftimeout_handler(signum,frame):raiseTimeoutError(f"Timed out after {seconds} seconds")# 핸들러 설정old_handler=signal.signal(signal.SIGALRM,timeout_handler)signal.alarm(seconds)try:yieldfinally:signal.alarm(0)# 알람 취소signal.signal(signal.SIGALRM,old_handler)# 복원# 사용try:withtimeout(3):importtimetime.sleep(10)# TimeoutError 발생exceptTimeoutErrorase:print(e)
7. 특정 시그널 블록 (Unix)
1
2
3
4
5
6
7
8
9
10
11
importsignalimporttime# SIGINT 일시 블록signal.pthread_sigmask(signal.SIG_BLOCK,[signal.SIGINT])print("SIGINT blocked for 5 seconds")time.sleep(5)# 블록 해제signal.pthread_sigmask(signal.SIG_UNBLOCK,[signal.SIGINT])print("SIGINT unblocked")
8. 대기 중 시그널 처리 (pause)
1
2
3
4
5
6
7
8
9
10
11
12
13
importsignalimportosdefhandler(signum,frame):print(f"Received signal {signum}")signal.signal(signal.SIGUSR1,handler)print(f"PID: {os.getpid()}")print("Waiting for SIGUSR1...")# 시그널 대기 (다른 터미널에서 kill -USR1 <PID>)signal.pause()print("Signal received!")
9. 자식 프로세스 종료 감지
1
2
3
4
5
6
7
8
9
importsignalimportosdefchild_handler(signum,frame):# 종료된 자식 수거pid,status=os.waitpid(-1,os.WNOHANG)print(f"Child {pid} terminated with status {status}")signal.signal(signal.SIGCHLD,child_handler)
10. 데몬 프로세스 리로드 (SIGHUP)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
importsignalimportsysconfig={'debug':False}defreload_config(signum,frame):globalconfigprint("Reloading configuration...")# 설정 파일 다시 로드# config = load_config()config['debug']=notconfig['debug']print(f"Config reloaded: {config}")signal.signal(signal.SIGHUP,reload_config)print(f"PID: {os.getpid()}")print("Send SIGHUP to reload config")whileTrue:signal.pause()
Windows 제한사항
1
2
3
4
5
6
7
8
9
10
11
12
13
importsignalimportsysifsys.platform=='win32':# Windows에서 사용 가능한 시그널# SIGINT, SIGTERM, SIGABRT, SIGFPE, SIGILL, SIGSEGV# SIGALRM, SIGHUP 등은 사용 불가passelse:# Unix 전용 시그널 사용 가능signal.signal(signal.SIGHUP,handler)signal.signal(signal.SIGALRM,handler)
자주 하는 실수
1. 핸들러에서 복잡한 작업
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
importsignal# 나쁨: 핸들러에서 I/O나 락 사용defbad_handler(signum,frame):withopen('log.txt','w')asf:# 위험!f.write("Signal received")# 좋음: 플래그만 설정shutdown_flag=Falsedefgood_handler(signum,frame):globalshutdown_flagshutdown_flag=True# 메인 루프에서 플래그 확인whilenotshutdown_flag:do_work()
2. 비동기 안전하지 않은 함수 호출
1
2
3
4
5
6
7
8
9
10
11
importsignal# 핸들러에서 피해야 할 것들:# - print() (버퍼링 문제)# - logging# - 락 획득# - 메모리 할당# 안전한 작업:# - 플래그 설정 (volatile)# - 파이프에 바이트 쓰기