개발자/파이썬 Python

Python에서 SIGINT(CTRL-C)와 같은 OS 신호 캡처 및 처리

지구빵집 2023. 4. 26. 09:21
반응형

 

 

본 포스팅에서는 Linux 및 Windows OS에서 SIGINT 및 SIGBREAK 와 같은 운영 체제 신호를 캡처하고 처리하여 실행 중에 Python 스크립트의 흐름을 제어하는 ​​방법을 배웁니다 . Python 3의 신호 모듈을 사용하여 OS 신호를 캡처하고 처리합니다. 

 

이 튜토리얼에서는 SIGINT(CTRL +C ), SIGBREAK(CTRL+BREAK)와 같은 OS 신호를 포착하는 사용자 지정 신호 핸들러(custom signal handler)를 직접 작성하는 방법을 알려드립니다, 

 

사용자 지정 신호 처리기에 신호를 등록하고 기본 신호 동작을 재정의하는 방법을 알아보고, 파이썬에서 무한 루프에서 빠져나오기 위해 SIGINT, SIGBREAK를 사용하고 Linux 및 Windows 시스템에서 파일 객체, 직렬 연결 등과 같은 열려 있는 리소스를 질서정연하게 닫습니다. 

 

아래 예시에서는 다음과 같은 방식으로 지속적으로 무언가를 수행하는 무한 루프가 있습니다. 예를 들어 직렬 포트에서 데이터를 지속적으로 읽어 파일에 쓰는 무한 루프를 가정합니다.  

 

while 1:

         read from port()

 

이제 스크립트가 수행하던 작업을 중지하고 직렬 포트 개체 또는 파일 개체와 같은 열려 있는 모든 리소스를 닫는 동안 정상적으로 종료하려고 합니다. 여기에서 OS 신호를 사용하여 무한 루프 실행을 중단할 수 있습니다. 

 

신호란 무엇인가? 

 

신호는 프로그램 또는 프로세스(여기서는 Python 스크립트)가 키보드 누름(CTRL + C) 또는 오류와 같은 특정 이벤트가 발생한 운영 체제로부터 정보를 수신할 수 있는 방법입니다. 신호는 OS에 의해 정수 값으로 할당됩니다. 

 

 

https://www.xanthium.in/operating-system-signal-handling-in-python3

 

키보드에서 CTRL + C를 누르면 운영 체제(Windows 및 Linux 모두) 는 현재 활성화된 프로그램으로 전송되는 SIGINT 신호를 생성합니다 . 수신 프로그램은 SIGINT 신호에 의해 지정된 기본 기능을 실행할 수 있습니다.

 

또는 신호 처리기를 사용하여 SIGINT 신호를 트랩하고 사용자 정의 사용자 지정 함수를 실행할 수 있습니다. 모든 시스템에서 모든 신호를 사용할 수 있는 것은 아닙니다. 운영 체제(Linux/Windows) 간에 정의된 시스템의 신호(할당 정수 혹은 기능)가 다름니다. 

 

OS에서 사용 가능한 신호를 확인합니다. 모든 OS에서 모든 신호를 사용할 수 있는 것은 아니므로 시스템에서 사용할 수 있는 신호를 아는 것이 좋습니다. 여기서는 Python 3(Python 3.9.x)을 사용하고 신호에 액세스하려면 신호 모듈을 가져와야 합니다. 

 

아래는 파이선에서 OS 신호 종류를 보여주는 코드입니다. python_available_signals.py

 

#OS Signal Handling in Python 3.x.x
#Display available signals on our System
#www.xanthium.in

import signal    # Import signal module using the import keyword
import platform  # for identifying the OS 
import pprint    # for pretty print 

# available signals on our System
valid_signals = signal.valid_signals()     # requires python 3.9.0
                                           # returns a SET
                                           
#print('Available Signals           ->',valid_signals)
print('\n\nOperating System            ->',platform.platform())
print('Number of Available Signals ->', len(valid_signals) , '\n')

pprint.pprint(valid_signals)#using pretty print to display set datastructure

 

위 파이선 스크립트를 실행한 결과는 아래와 같습니다.

 

윈도우 10에서 사용 가능한 OS 신호

Operating System            -> Windows-10-10.0.22621-SP0
Number of Available Signals -> 7 

{<Signals.SIGINT: 2>,
 <Signals.SIGILL: 4>,
 <Signals.SIGFPE: 8>,
 <Signals.SIGSEGV: 11>,
 <Signals.SIGTERM: 15>,
 <Signals.SIGBREAK: 21>,
 <Signals.SIGABRT: 22>}

 

리눅스에서 사용가능한 Signal 종류는 위 프로그램이 버전 3.9라서 에러가 나서 그냥 리눅스 시스템 명령어로 긁어 왔습니다.

 

kill -l

 

결과는 아래와 같습니다. 아주 아름답게 출력이 되네요~^^

 

(base) tako@rtx-a5000:~/pythontest$ 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

 

위의 결과에서 볼 수 있듯이 Linux 및 Windows 시스템에서 사용할 수 있는 신호의 수와 유형에는 상당한 차이가 있습니다. 

 

 

Python 3에서 신호 프로그래밍  

 

스크립트에서 받은 신호를 사용하려면 두 가지 작업을 수행해야 합니다.

1. 신호가 수신되면 사용자 지정 처리를 수행할 신호 처리기를 작성합니다 .

2. 신호 처리기 기능으로 신호 등록 

 

import signal

def your_custom_signal_handler(signal_number,frame):
           code to do something
           code to do something
           code to do something

signal.signal(signal.SIGNALNAME,your_custom_signal_handler)

 

여기서는 SIGINT 신호를 사용하여 Python 3 스크립트의 실행을 제어합니다. SIGINT 신호는 Windows 및 Linux 시스템 모두에 공통적이며 Windows 및 Linux 시스템 모두에서 코드를 실행하는 데 도움이 됩니다. Github의 코드를 사용하십시오 

 

 

시스널 핸들러 등록

 

 

import signal # Import signal module

def SignalHandler_SIGINT(SignalNumber,Frame):
     print('SignalHandler of signal.SIGINT')

#register the signal with Signal handler
signal.signal(signal.SIGINT,SignalHandler_SIGINT)

while 1:  
    print("Press Ctrl + C ") 
    time.sleep(1)

 

아래는 SIGINT 신호를 처리하는 전체 코드입니다.

 

#OS Signal Handling in Python 3.x.x

#using Signals in Python 3.x.x

#Signals.SIGINT:  CTRL + C ,Default action is to raise KeyboardInterrupt.

#www.xanthium.in
 

import signal # Import signal module using the import keyword
import time   # available signals on your system

# Create a Signal Handler for Signals.SIGINT:  CTRL + C 
def SignalHandler_SIGINT(SignalNumber,Frame):
	print('SignalHandler of signal.SIGINT')
	print(f'Signal Number -> {SignalNumber} Frame -> {Frame}')
    

# Register the Signal Handler def SignalHandler_SIGINT() with the Signal       
# signal.signal(Signal Name,Name of Handler Function)
signal.signal(signal.SIGINT,SignalHandler_SIGINT) 



while 1:  
    print("Press Ctrl + C ") 
    time.sleep(1)

 

Python SIGINT 신호 코드 작업 

 

위의 코드를 실행하면,

  1. Python 스크립트는 무한 루프에서 "Press CTRL + C"를 인쇄합니다.
  2. 키보드에서 CTRL + C를 누르면 OS(Windows/Linux)에서 SIGINT 신호가 생성되어 실행 중인 스크립트로 전송됩니다.
  3. 그런 다음 Python 스크립트는 def SignalHandler_SIGINT (SignalNumber,Frame): 스크립트 내부의 신호 처리기로 컨트롤을 전송한 다음 "SignalHandler of signal.SIGINT" 아래에서 인쇄 문을 실행합니다.
  4. 그런 다음 제어가 다시 무한 루프로 전환됩니다. 

실행은 아래와 같습니다.

 

Python의 무한 루프에서 안전하게 종료

 

이 예제에서는 신호를 사용하여 Python 3의 무한 루프에서 안전하게 종료하는 방법을 배웁니다.

 

Python으로 작성된 직렬 포트 데이터 수집 시스템 소프트웨어와 같은 일부 응용 프로그램에서는 무한 루프에서 Arduino 또는 Raspberry Pi 보드와 같은 센서 또는 마이크로 컨트롤러를 지속적으로 쿼리해야 합니다.

 

이러한 애플리케이션에서는 SIGINT 신호(CTRL +C)를 사용하여 무한 루프를 중단하고 리소스 누수 없이 애플리케이션을 안전하게 닫을 수 있습니다.

 

SIGBREAK(CTRL + BREAK) 신호를 사용하여 그렇게 할 수도 있지만 신호는 Windows에서만 사용할 수 있지만 SIGINT는 Linux와 Windows 모두에서 사용할 수 있습니다. 

 

아래는 무한 루프에서 시그널을 받아 처리하는 전체 코드입니다.

 

# OS Signal Handling in Python 3.x.x

# using signals to get out of an infinite loop
# Signal used is Signals.SIGINT:  CTRL + C
# www.xanthium.in

import signal # Import signal module using the import keyword
import time   # available signals on your system

Sentry = True

# Create a Signal Handler for Signals.SIGINT:  CTRL + C 
def SignalHandler_SIGINT(SignalNumber,Frame):
    print('SignalHandler of signal.SIGINT')
    #print(f'Signal Number -> {SignalNumber} Frame -> {Frame}')
    global Sentry
    Sentry = False

signal.signal(signal.SIGINT,SignalHandler_SIGINT) #register the signal handler

while Sentry:
    print('Long continous event Eg,Read from sensor,Press CTRL+C to exit')
    time.sleep(1)

print('Out of the while loop')
print('Clean up Here')

 

위 코드가 실행하는 방법은 아래와 같습니다.

  1. 이제 위의 코드에서 우리는 Sentry 변수를 사용하여 하단에 있는 while 루프의 흐름을 제어합니다.
  2. Sentry 변수는 스크립트가 처음 실행되고 while 루프가 계속 실행될 때 True, Sentry = True 로 설정됩니다 .
  3. 사용자가 루프를 중지하고 싶을 때 CTRL + C를 눌러 SIGINT 신호를 생성할 수 있습니다.
  4. SIGINT 신호 처리기가 실행되고 Sentry 변수를 False 로 설정합니다.
  5. 그런 다음 컨트롤은 Sentry == False 인 While 루프로 전달 되어 루프가 종료되고 컨트롤이 아래의 print() 문으로 전달됩니다.
  6. 사용자는 그곳에서 정리를 할 수 있습니다.

 

Sentry는 신호 처리기 내에서 Global로 정의해야 합니다. 

 

Windows에서 Python의 무한 루프에서 벗어나기 

 

 

SIGINT를 사용하여 Linux의 Python에서 무한 루프에서 벗어나기 

 

 

 

참고자료

 

Capturing and Handling OS signals like SIGINT (CTRL-C) in Python 

본 포스팅 유튜브 영상 참

Github 파일 다운로드  

리눅스 시그널이란? 

 

 

 

반응형