signal — 비동기 이벤트 핸들러 설정
signal — 비동기 이벤트 핸들러 설정
이 모듈은 Python에서 시그널 핸들러를 사용하는 메커니즘을 제공합니다.
본문
일반 규칙
signal.signal() 함수는 시그널이 수신될 때 실행될 사용자 정의 핸들러를 정의할 수 있게 합니다. 소수의 기본 핸들러가 설치됩니다: SIGPIPE는 무시되고(따라서 파이프와 소켓의 쓰기 오류는 일반적인 Python 예외로 보고될 수 있음), SIGINT는 부모 프로세스가 변경하지 않았다면 KeyboardInterrupt 예외로 변환됩니다.
특정 시그널에 대한 핸들러는 일단 설정되면 명시적으로 재설정될 때까지 설치된 상태로 유지됩니다(Python은 기저 구현과 관계없이 BSD 스타일 인터페이스를 에뮬레이트합니다). 단, 기저 구현을 따르는 SIGCHLD의 핸들러는 예외입니다.
WebAssembly 플랫폼에서는 시그널이 에뮬레이트되므로 다르게 동작합니다. 여러 함수와 시그널은 이러한 플랫폼에서 사용할 수 없습니다.
Python 시그널 핸들러의 실행
Python 시그널 핸들러는 저수준(C) 시그널 핸들러 안에서 실행되지 않습니다. 대신 저수준 시그널 핸들러는 가상 머신에게 해당 Python 시그널 핸들러를 나중에(예: 다음 바이트코드 명령에서) 실행하라고 알리는 플래그를 설정합니다. 이것은 결과가 있습니다:
- C 코드의 잘못된 연산으로 인한
SIGFPE나SIGSEGV와 같은 동기 오류를 잡는 것은 거의 의미가 없습니다. Python은 시그널 핸들러에서 C 코드로 반환하며, C 코드는 같은 시그널을 다시 발생시킬 가능성이 높아 Python이 겉보기에 멈추게 됩니다. Python 3.3부터faulthandler모듈을 사용하여 동기 오류를 보고할 수 있습니다. - 순수 C로 구현된 오래 실행되는 계산(큰 텍스트 본문의 정규식 일치와 같은)은 수신된 시그널과 관계없이 임의의 시간 동안 중단 없이 실행될 수 있습니다. Python 시그널 핸들러는 계산이 끝날 때 호출됩니다.
- 핸들러가 예외를 발생시키면 메인 스레드에서 "공중에서(out of thin air)" 발생합니다. 이에 대한 논의는 아래 참고를 참고하세요.
시그널과 스레드
Python 시그널 핸들러는 시그널이 다른 스레드에서 수신되었더라도 항상 메인 인터프리터의 메인 Python 스레드에서 실행됩니다. 이것은 시그널을 스레드 간 통신 수단으로 사용할 수 없음을 의미합니다. 대신 threading 모듈의 동기화 프리미티브를 사용할 수 있습니다.
또한 메인 인터프리터의 메인 스레드만 새 시그널 핸들러를 설정할 수 있습니다.
Warning
threading.Lock과 같은 동기화 프리미티브는 시그널 핸들러 안에서 사용해서는 안 됩니다. 그렇게 하면 예기치 않은 교착 상태가 발생할 수 있습니다.
모듈 내용
versionchanged: 3.5에서 아래 나열된 signal(SIG*), handler(SIG_DFL, SIG_IGN), sigmask(SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK) 관련 상수는 각각 열거형(Signals, Handlers, Sigmasks)으로 바뀌었습니다.
getsignal(),pthread_sigmask(),sigpending(),sigwait()함수는 사람이 읽을 수 있는 열거형을Signals객체로 반환합니다.
signal 모듈은 세 개의 열거형을 정의합니다.
- signal.Signals — SIG* 상수와 CTRL_* 상수의
enum.IntEnum컬렉션.versionadded: 3.5.
- signal.Handlers —
SIG_DFL과SIG_IGN상수의enum.IntEnum컬렉션.versionadded: 3.5.
- signal.Sigmasks —
SIG_BLOCK,SIG_UNBLOCK,SIG_SETMASK상수의enum.IntEnum컬렉션.- Availability: Unix.
- 자세한 내용은 매뉴얼 페이지 sigprocmask(2)와 pthread_sigmask(3) 참고.
-
versionadded: 3.5.
signal 모듈에 정의된 변수는:
- signal.SIG_DFL — 두 표준 시그널 처리 옵션 중 하나. 시그널에 대한 기본 함수를 수행합니다. 예를 들어 대부분의 시스템에서
SIGQUIT의 기본 동작은 코어 덤프 후 종료하는 것이고,SIGCHLD의 기본 동작은 단순히 무시하는 것입니다. - signal.SIG_IGN — 또 다른 표준 시그널 핸들러로, 주어진 시그널을 단순히 무시합니다.
- signal.SIGABRT — abort(3)의 중단 시그널.
- signal.SIGALRM — alarm(2)의 타이머 시그널. Availability: Unix.
- signal.SIGBREAK — 키보드(CTRL + BREAK)의 인터럽트. Availability: Windows.
- signal.SIGBUS — 버스 오류(잘못된 메모리 접근). Availability: Unix.
- signal.SIGCHLD — 자식 프로세스가 중지되거나 종료됨. Availability: Unix.
- signal.SIGCLD —
SIGCHLD의 별칭. Availability: not macOS. - signal.SIGCONT — 현재 중지된 프로세스 재개. Availability: Unix.
- signal.SIGFPE — 부동소수점 예외. 예: 0으로 나누기. (
ZeroDivisionError는 나눗셈이나 나머지 연산의 두 번째 인자가 0일 때 발생합니다.) - signal.SIGHUP — 제어 터미널에서 헹업 감지 또는 제어 프로세스의 종료. Availability: Unix.
- signal.SIGILL — 불법 명령.
- signal.SIGINT — 키보드(CTRL + C)의 인터럽트. 기본 동작은
KeyboardInterrupt발생. - signal.SIGKILL — 킬 시그널. 잡히거나, 차단되거나, 무시될 수 없습니다. Availability: Unix.
- signal.SIGPIPE — 깨진 파이프: 독자가 없는 파이프에 쓰기. 기본 동작은 시그널 무시. Availability: Unix.
- signal.SIGPROF — 프로파일링 타이머 만료. Availability: Unix.
- signal.SIGQUIT — 터미널 종료 시그널. Availability: Unix.
- signal.SIGSEGV — 세그멘테이션 오류: 잘못된 메모리 참조.
- signal.SIGSTOP — 실행 중지(잡히거나 무시될 수 없음). Availability: Unix.
- signal.SIGSTKFLT — 코프로세서의 스택 오류. Linux 커널은 이 시그널을 발생시키지 않습니다: 사용자 공간에서만 발생시킬 수 있습니다. Availability: Linux. 시그널을 사용할 수 있는 아키텍처에서. 자세한 내용은 매뉴얼 페이지 signal(7) 참고.
versionadded: 3.11.
- signal.SIGTERM — 종료 시그널.
- signal.SIGUSR1 — 사용자 정의 시그널 1. Availability: Unix.
- signal.SIGUSR2 — 사용자 정의 시그널 2. Availability: Unix.
- signal.SIGVTALRM — 가상 타이머 만료. Availability: Unix.
- signal.SIGWINCH — 창 크기 조정 시그널. Availability: Unix.
- signal.SIGXCPU — CPU 시간 제한 초과. Availability: Unix.
- SIG* — 모든 시그널 번호는 심볼로 정의됩니다. 예를 들어 헹업 시그널은
signal.SIGHUP으로 정의됩니다. 변수 이름은<signal.h>에 있는 C 프로그램에서 사용되는 이름과 동일합니다. 'signal'에 대한 Unix 매뉴얼 페이지는 기존 시그널을 나열합니다(일부 시스템에서는 signal(2), 다른 시스템에서는 목록이 signal(7)에 있습니다). 모든 시스템이 같은 시그널 이름 집합을 정의하지는 않는다는 점에 유의하세요. 이 모듈은 시스템이 정의하는 이름만 정의합니다. - signal.CTRL_C_EVENT — Ctrl+C 키 입력 이벤트에 해당하는 시그널. 이 시그널은
os.kill()과만 사용할 수 있습니다. Availability: Windows.versionadded: 3.2.
- signal.CTRL_BREAK_EVENT — Ctrl+Break 키 입력 이벤트에 해당하는 시그널. 이 시그널은
os.kill()과만 사용할 수 있습니다. Availability: Windows.versionadded: 3.2.
- signal.NSIG — 가장 높은 시그널 번호보다 하나 많은 수. 유효한 시그널 번호를 얻으려면
valid_signals()를 사용하세요. - signal.ITIMER_REAL — 실시간으로 간격 타이머를 감소시키고 만료 시
SIGALRM을 전달합니다. - signal.ITIMER_VIRTUAL — 프로세스가 실행 중일 때만 간격 타이머를 감소시키고 만료 시
SIGVTALRM을 전달합니다. - signal.ITIMER_PROF — 프로세스가 실행 중이고 시스템이 프로세스를 대신해 실행 중일 때 모두 간격 타이머를 감소시킵니다.
ITIMER_VIRTUAL과 함께 이 타이머는 응용 프로그램이 사용자와 커널 공간에서 보내는 시간을 프로파일링하는 데 보통 사용됩니다. 만료 시SIGPROF가 전달됩니다. - signal.SIG_BLOCK — 시그널이 차단되어야 함을 나타내는
pthread_sigmask()의how매개변수에 대한 가능한 값.versionadded: 3.3.
- signal.SIG_UNBLOCK — 시그널이 차단 해제되어야 함을 나타내는
pthread_sigmask()의how매개변수에 대한 가능한 값.versionadded: 3.3.
- signal.SIG_SETMASK — 시그널 마스크가 교체되어야 함을 나타내는
pthread_sigmask()의how매개변수에 대한 가능한 값.versionadded: 3.3.
signal 모듈은 하나의 예외를 정의합니다.
- signal.ItimerError — 기저
setitimer()또는getitimer()구현의 오류를 알리기 위해 발생. 잘못된 간격 타이머나 음수 시간이setitimer()에 전달되면 이 오류가 발생할 것으로 예상하세요. 이 오류는OSError의 하위 유형입니다.versionadded: 3.3. 이 오류는 한때
IOError의 하위 유형이었으며, 지금은OSError의 별칭입니다.
signal 모듈은 다음 함수를 정의합니다.
- signal.alarm(time) —
time이 0이 아니면 이 함수는SIGALRM시그널을time초 후에 프로세스로 보내도록 요청합니다. 이전에 예약된 알람은 취소됩니다(한 번에 하나의 알람만 예약될 수 있음). 반환 값은 이전에 설정된 알람이 전달되기까지 남은 초 수입니다.time이 0이면 알람이 예약되지 않고 예약된 알람이 취소됩니다. 반환 값이 0이면 현재 예약된 알람이 없습니다.- Availability: Unix. 자세한 내용은 매뉴얼 페이지 alarm(2) 참고.
- signal.getsignal(signalnum) — 시그널
signalnum에 대한 현재 시그널 핸들러를 반환합니다. 반환 값은 호출 가능한 Python 객체이거나 특수 값signal.SIG_IGN,signal.SIG_DFL또는None일 수 있습니다.signal.SIG_IGN은 시그널이 이전에 무시되었음을,signal.SIG_DFL은 시그널의 기본 처리 방식이 이전에 사용되었음을,None은 이전 시그널 핸들러가 Python에서 설치되지 않았음을 의미합니다. - signal.strsignal(signalnum) —
SIGINT에 대한 "Interrupt"와 같은 시그널signalnum의 설명을 반환합니다.signalnum에 설명이 없으면None을 반환합니다.signalnum이 유효하지 않으면ValueError를 발생시킵니다.versionadded: 3.8.
- signal.valid_signals() — 이 플랫폼에서 유효한 시그널 번호 집합을 반환합니다. 일부 시그널이 시스템의 내부 사용을 위해 예약된 경우
range(1, NSIG)보다 작을 수 있습니다.versionadded: 3.8.
- signal.pause() — 시그널이 수신될 때까지 프로세스를 잠들게 합니다. 그러면 적절한 핸들러가 호출됩니다. 아무것도 반환하지 않습니다.
- Availability: Unix. 자세한 내용은 매뉴얼 페이지 signal(2) 참고. sigwait(), sigwaitinfo(), sigtimedwait(), sigpending()도 참고하세요.
- signal.raise_signal(signum) — 신호를 호출 프로세스로 보냅니다. 아무것도 반환하지 않습니다.
versionadded: 3.8.
- signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0) — 파일 디스크립터
pidfd가 참조하는 프로세스로 시그널sig를 보냅니다. Python은 현재siginfo매개변수를 지원하지 않습니다.None이어야 합니다.flags인자는 향후 확장을 위해 제공됩니다. 현재 정의된 플래그 값은 없습니다.- 자세한 내용은 pidfd_send_signal(2) 매뉴얼 페이지 참고.
- Availability: Linux >= 5.1, Android >= 빌드 시점 API 레벨 31.
-
versionadded: 3.9.
- signal.pthread_kill(thread_id, signalnum) — 시그널
signalnum을 호출자와 같은 프로세스의 다른 스레드인thread_id스레드로 보냅니다. 대상 스레드는 어떤 코드(Python이든 아니든)를 실행할 수 있습니다. 그러나 대상 스레드가 Python 인터프리터를 실행 중이면 Python 시그널 핸들러는 메인 인터프리터의 메인 스레드에 의해 실행됩니다. 따라서 특정 Python 스레드에 시그널을 보내는 유일한 목적은 실행 중인 시스템 호출을InterruptedError로 실패시키는 것입니다.thread_id에 적합한 값을 얻으려면threading.get_ident()또는threading.Thread객체의ident속성을 사용하세요.signalnum이 0이면 시그널이 전송되지 않지만 오류 검사는 여전히 수행됩니다. 이것을 사용하여 대상 스레드가 여전히 실행 중인지 확인할 수 있습니다.- 감사 이벤트
signal.pthread_kill을 인자thread_id, signalnum으로 발생시킵니다. - Availability: Unix. 자세한 내용은 매뉴얼 페이지 pthread_kill(3) 참고. os.kill()도 참고하세요.
-
versionadded: 3.3.
- signal.pthread_sigmask(how, mask) — 호출 스레드의 시그널 마스크를 가져오거나 변경합니다. 시그널 마스크는 호출자에 대해 현재 전달이 차단된 시그널 집합입니다. 이전 시그널 마스크를 시그널 집합으로 반환합니다.
- 호출 동작은
how의 값에 따라 달라집니다.SIG_BLOCK: 차단된 시그널의 집합은 현재 집합과mask인자의 합집합입니다.SIG_UNBLOCK:mask의 시그널은 현재 차단된 시그널 집합에서 제거됩니다. 차단되지 않은 시그널의 차단 해제를 시도하는 것은 허용됩니다.SIG_SETMASK: 차단된 시그널의 집합이mask인자로 설정됩니다.
mask는 시그널 번호 집합입니다(예:{signal.SIGINT, signal.SIGTERM}). 모든 시그널을 포함하는 전체 마스크에 대해valid_signals()를 사용하세요.- 예:
signal.pthread_sigmask(signal.SIG_BLOCK, [])는 호출 스레드의 시그널 마스크를 읽습니다. SIGKILL과SIGSTOP은 차단될 수 없습니다.- Availability: Unix. 자세한 내용은 매뉴얼 페이지 sigprocmask(2)와 pthread_sigmask(3) 참고. pause(), sigpending(), sigwait()도 참고하세요.
-
versionadded: 3.3.
- 호출 동작은
- signal.setitimer(which, seconds, interval=0.0) —
which가 지정하는 주어진 간격 타이머(signal.ITIMER_REAL,signal.ITIMER_VIRTUAL또는signal.ITIMER_PROF중 하나)를seconds후에 발생시키도록 설정하고(alarm()과 달리 float가 허용됨), 그 다음에는 매interval초마다 발생시킵니다(interval이 0이 아니면).seconds를 0으로 설정하면which가 지정하는 간격 타이머를 지울 수 있습니다.- 간격 타이머가 발생하면 시그널이 프로세스로 전송됩니다. 보내지는 시그널은 사용 중인 타이머에 따라 다릅니다:
signal.ITIMER_REAL은SIGALRM을,signal.ITIMER_VIRTUAL은SIGVTALRM을,signal.ITIMER_PROF는SIGPROF를 전달합니다. - 이전 값은 튜플로 반환됩니다:
(delay, interval). - 잘못된 간격 타이머를 전달하려고 하면
ItimerError가 발생합니다. - Availability: Unix.
- 간격 타이머가 발생하면 시그널이 프로세스로 전송됩니다. 보내지는 시그널은 사용 중인 타이머에 따라 다릅니다:
- signal.getitimer(which) —
which가 지정하는 주어진 간격 타이머의 현재 값을 반환합니다.- Availability: Unix.
- *signal.set_wakeup_fd(fd, , warn_on_full_buffer=True) — 웨이크업 파일 디스크립터를
fd로 설정합니다. 프로그램이 시그널 핸들러를 등록한 시그널이 수신되면 시그널 번호가 단일 바이트로fd에 기록됩니다. 관심 있는 시그널에 대한 시그널 핸들러를 등록하지 않았다면 웨이크업 fd에 아무것도 기록되지 않습니다. 이것은 라이브러리가 poll 또는 select 호출을 깨우는 데 사용할 수 있으며 시그널이 완전히 처리되도록 허용합니다.- 이전 웨이크업 fd가 반환됩니다(파일 디스크립터 웨이크업이 활성화되지 않았으면 -1).
fd가 -1이면 파일 디스크립터 웨이크업이 비활성화됩니다. -1이 아니면fd는 논블로킹이어야 합니다. poll 또는 select를 다시 호출하기 전에fd에서 바이트를 제거하는 것은 라이브러리의 몫입니다. - 스레드가 활성화되면 이 함수는 메인 인터프리터의 메인 스레드에서만 호출할 수 있습니다. 다른 스레드에서 호출하려 하면
ValueError예외가 발생합니다. - 이 함수를 사용하는 두 가지 일반적인 방법이 있습니다. 두 접근 모두
fd를 사용하여 시그널이 도착하면 깨어나지만, 어떤 시그널이 도착했는지 결정하는 방법이 다릅니다.- 첫 번째 접근에서는
fd의 버퍼에서 데이터를 읽고 바이트 값이 시그널 번호를 제공합니다. 이것은 간단하지만 드물게 문제가 발생할 수 있습니다: 일반적으로fd는 제한된 버퍼 공간을 가지며 너무 많은 시그널이 너무 빠르게 도착하면 버퍼가 가득 차고 일부 시그널이 손실될 수 있습니다. 이 접근을 사용한다면warn_on_full_buffer=True를 설정해야 하며, 적어도 시그널이 손실될 때 stderr에 경고를 출력하게 됩니다. - 두 번째 접근에서는 웨이크업 fd를 웨이크업에만 사용하고 실제 바이트 값을 무시합니다. 이 경우
fd의 버퍼가 비어 있는지 비어 있지 않은지만 신경 씁니다. 가득 찬 버퍼는 전혀 문제를 나타내지 않습니다. 이 접근을 사용한다면warn_on_full_buffer=False를 설정하여 사용자가 가짜 경고 메시지에 혼동하지 않도록 해야 합니다.
- 첫 번째 접근에서는
-
versionchanged: 3.5에서 Windows에서 이 함수는 이제 소켓 핸들도 지원합니다.
-
versionchanged: 3.7에서
warn_on_full_buffer매개변수가 추가되었습니다.
- 이전 웨이크업 fd가 반환됩니다(파일 디스크립터 웨이크업이 활성화되지 않았으면 -1).
- signal.siginterrupt(signalnum, flag) — 시스템 호출 재시작 동작을 변경합니다:
flag가False이면 시스템 호출은 시그널signalnum에 의해 중단될 때 재시작되고, 그렇지 않으면 시스템 호출은 중단됩니다. 아무것도 반환하지 않습니다.- Availability: Unix. 자세한 내용은 매뉴얼 페이지 siginterrupt(3) 참고.
signal()로 시그널 핸들러를 설치하면 주어진 시그널에 대해 참인 플래그 값으로siginterrupt()를 암시적으로 호출하여 재시작 동작이 중단 가능하도록 재설정됩니다.
- signal.signal(signalnum, handler) — 시그널
signalnum의 핸들러를 함수handler로 설정합니다.handler는 두 인자를 받는 호출 가능한 Python 객체(아래 참고)이거나 특수 값signal.SIG_IGN또는signal.SIG_DFL일 수 있습니다. 이전 시그널 핸들러가 반환됩니다(위의getsignal()설명 참고). (자세한 내용은 Unix 매뉴얼 페이지 signal(2) 참고.)- 스레드가 활성화되면 이 함수는 메인 인터프리터의 메인 스레드에서만 호출할 수 있습니다. 다른 스레드에서 호출하려 하면
ValueError예외가 발생합니다. - 핸들러는 두 인자로 호출됩니다: 시그널 번호와 현재 스택 프레임(
None또는 프레임 객체). 프레임 객체에 대한 설명은 타입 계층 구조의 설명 또는 inspect 모듈의 속성 설명을 참고하세요. - Windows에서
signal()은SIGABRT,SIGFPE,SIGILL,SIGINT,SIGSEGV,SIGTERM,SIGBREAK로만 호출할 수 있습니다. 다른 경우에는ValueError가 발생합니다. 모든 시스템이 같은 시그널 이름 집합을 정의하지는 않는다는 점에 유의하세요. 시그널 이름이 SIG* 모듈 수준 상수로 정의되지 않으면AttributeError가 발생합니다.
- 스레드가 활성화되면 이 함수는 메인 인터프리터의 메인 스레드에서만 호출할 수 있습니다. 다른 스레드에서 호출하려 하면
- signal.sigpending() — 호출 스레드에 전달 대기 중인 시그널 집합(즉, 차단된 동안 발생한 시그널)을 검사합니다. 대기 중인 시그널 집합을 반환합니다.
- Availability: Unix. 자세한 내용은 매뉴얼 페이지 sigpending(2) 참고. pause(), pthread_sigmask(), sigwait()도 참고하세요.
-
versionadded: 3.3.
- signal.sigwait(sigset) — 시그널 집합
sigset에 지정된 시그널 중 하나가 전달될 때까지 호출 스레드의 실행을 일시 중지합니다. 함수는 시그널을 수락하고(대기 중인 시그널 목록에서 제거) 시그널 번호를 반환합니다.- Availability: Unix. 자세한 내용은 매뉴얼 페이지 sigwait(3) 참고. pause(), pthread_sigmask(), sigpending(), sigwaitinfo(), sigtimedwait()도 참고하세요.
-
versionadded: 3.3.
- signal.sigwaitinfo(sigset) — 시그널 집합
sigset에 지정된 시그널 중 하나가 전달될 때까지 호출 스레드의 실행을 일시 중지합니다. 함수는 시그널을 수락하고 대기 중인 시그널 목록에서 제거합니다.sigset의 시그널 중 하나가 이미 호출 스레드에 대해 대기 중이면 함수는 그 시그널에 대한 정보와 함께 즉시 반환합니다. 전달된 시그널에 대해서는 시그널 핸들러가 호출되지 않습니다.sigset에 없는 시그널에 의해 중단되면InterruptedError가 발생합니다.- 반환 값은
siginfo_t구조에 포함된 데이터를 나타내는 객체입니다. 즉si_signo,si_code,si_errno,si_pid,si_uid,si_status,si_band입니다. - Availability: Unix. 자세한 내용은 매뉴얼 페이지 sigwaitinfo(2) 참고. pause(), sigwait(), sigtimedwait()도 참고하세요.
-
versionadded: 3.3.
-
versionchanged: 3.5에서 함수는
sigset에 없는 시그널에 의해 중단되고 시그널 핸들러가 예외를 발생시키지 않으면 재시도됩니다(근거는 PEP 475 참고).
- 반환 값은
- signal.sigtimedwait(sigset, timeout) —
sigwaitinfo()와 같지만 타임아웃을 지정하는 추가timeout인자를 받습니다.timeout이 0으로 지정되면 폴링이 수행됩니다. 타임아웃이 발생하면None을 반환합니다.- Availability: Unix. 자세한 내용은 매뉴얼 페이지 sigtimedwait(2) 참고. pause(), sigwait(), sigwaitinfo()도 참고하세요.
-
versionadded: 3.3.
-
versionchanged: 3.5에서 함수는
sigset에 없는 시그널에 의해 중단되고 시그널 핸들러가 예외를 발생시키지 않으면 재계산된 타임아웃으로 재시도됩니다(근거는 PEP 475 참고).
예제
최소 예제 프로그램입니다. alarm() 함수를 사용하여 파일 열기를 기다리는 시간을 제한합니다. 직렬 장치용 파일이 켜지지 않아 os.open()이 무기한 멈추게 하는 경우에 유용합니다. 해결책은 파일을 열기 전에 5초 알람을 설정하는 것입니다. 작업이 너무 오래 걸리면 알람 시그널이 전송되고 핸들러가 예외를 발생시킵니다.
import signal, os
def handler(signum, frame):
signame = signal.Signals(signum).name
print(f'Signal handler called with signal {signame} ({signum})')
raise OSError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable the alarm
SIGPIPE 참고
프로그램의 출력을 head(1)와 같은 도구로 파이핑하면 표준 출력의 수신자가 일찍 닫으면 SIGPIPE 시그널이 프로세스로 전송됩니다. 이로 인해 BrokenPipeError: [Errno 32] Broken pipe와 같은 예외가 발생합니다. 이를 처리하려면 엔트리 포인트를 감싸 이 예외를 잡으세요:
import os
import sys
def main():
try:
# simulate large output (your code replaces this loop)
for x in range(10000):
print("y")
# flush output here to force SIGPIPE to be triggered
# while inside this try block.
sys.stdout.flush()
except BrokenPipeError:
# Python flushes standard streams on exit; redirect remaining output
# to devnull to avoid another BrokenPipeError at shutdown
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
sys.exit(1) # Python exits with error code 1 on EPIPE
if __name__ == '__main__':
main()
BrokenPipeError를 피하려고 SIGPIPE의 처분을 SIG_DFL로 설정하지 마세요. 그렇게 하면 프로그램이 소켓 연결이 어떤 순간 중단될 때마다 예기치 않게 종료됩니다.
시그널 핸들러와 예외 참고
시그널 핸들러가 예외를 발생시키면 예외는 메인 스레드로 전파되고 어떤 바이트코드 명령 후에 발생할 수 있습니다. 특히 KeyboardInterrupt가 실행 중 어느 지점에서나 나타날 수 있습니다. 표준 라이브러리를 포함한 대부분의 Python 코드는 이것에 견고하게 만들 수 없으며, 따라서 KeyboardInterrupt(또는 시그널 핸들러에서 발생하는 다른 예외)는 드물게 프로그램을 예기치 않은 상태에 빠뜨릴 수 있습니다.
이 문제를 설명하기 위해 다음 코드를 고려하세요:
class SpamContext:
def __init__(self):
self.lock = threading.Lock()
def __enter__(self):
# If KeyboardInterrupt occurs here, everything is fine
self.lock.acquire()
# If KeyboardInterrupt occurs here, __exit__ will not be called
...
# KeyboardInterrupt could occur just before the function returns
def __exit__(self, exc_type, exc_val, exc_tb):
...
self.lock.release()
특히 KeyboardInterrupt에 종료만 하려는 많은 프로그램의 경우 이것은 문제가 되지 않지만, 복잡하거나 높은 신뢰성이 필요한 응용 프로그램은 시그널 핸들러에서 예외를 발생시키는 것을 피해야 합니다. 또한 정상 종료 수단으로 KeyboardInterrupt를 잡는 것도 피해야 합니다. 대신 자체 SIGINT 핸들러를 설치해야 합니다. 아래는 KeyboardInterrupt를 피하는 HTTP 서버의 예입니다:
import signal
import socket
from selectors import DefaultSelector, EVENT_READ
from http.server import HTTPServer, SimpleHTTPRequestHandler
interrupt_read, interrupt_write = socket.socketpair()
def handler(signum, frame):
print('Signal handler called with signal', signum)
interrupt_write.send(b'\0')
signal.signal(signal.SIGINT, handler)
def serve_forever(httpd):
sel = DefaultSelector()
sel.register(interrupt_read, EVENT_READ)
sel.register(httpd, EVENT_READ)
while True:
for key, _ in sel.select():
if key.fileobj == interrupt_read:
interrupt_read.recv(1)
return
if key.fileobj == httpd:
httpd.handle_request()
print("Serving on port 8000")
httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
serve_forever(httpd)
print("Shutdown...")