# `signal` — 비동기 이벤트에 대한 처리기 설정

이 모듈은 파이썬에서 시그널 처리기를 사용하는 메커니즘을 제공합니다.

## 일반 규칙

`signal.signal()` 함수는 시그널이 수신될 때 실행될 사용자 정의 처리기를 정의하도록 합니다. 소수의 기본 처리기가 설치됩니다: `SIGPIPE`는 무시되고 (그래서 파이프와 소켓에 대한 쓰기 에러는 일반 파이썬 예외로 보고될 수 있습니다) `SIGINT`는 부모 프로세스가 변경하지 않았다면 `KeyboardInterrupt` 예외로 번역됩니다.

일단 설정되면, 특정 시그널에 대한 처리기는 명시적으로 재설정 될 때까지 (파이썬은 하부 구현과 관계없이 BSD 스타일 인터페이스를 흉내 냅니다) 설치된 상태로 유지됩니다. `SIGCHLD`에 대한 처리기는 예외인데, 하부 구현을 따릅니다.

### 파이썬 시그널 처리기의 실행

파이썬 시그널 처리기는 저수준 (C) 시그널 처리기 내에서 실행되지 않습니다. 대신, 저수준 시그널 처리기는 가상 기계에게 나중에 (예를 들어 다음 바이트 코드 명령에서) 해당 파이썬 시그널 처리기를 실행하도록 지시하는 플래그를 설정합니다. 결과는 다음과 같습니다:

- C 코드에서의 유효하지 않은 연산으로 인해 발생하는 `SIGFPE`나 `SIGSEGV`와 같은 동기 에러를 잡는 것은 그리 의미가 없습니다. 파이썬은 시그널 처리기에서 C 코드로 돌아오는데, 같은 시그널을 다시 발생시켜서, 파이썬이 멈출 것입니다. 파이썬 3.3부터는, `faulthandler` 모듈을 사용하여 동기 에러를 보고할 수 있습니다.

- C로만 구현된 오래 실행되는 계산(가령 커다란 텍스트 본문에 대한 정규식 일치)은 수신된 시그널에 상관없이 임의의 시간 동안 중단없이 실행될 수 있습니다. 계산이 끝나면 파이썬 시그널 처리기가 호출됩니다.

### 시그널과 스레드

Python signal handlers are always executed in the main Python thread, even if the signal was received in another thread. This means that signals can’t be used as a means of inter-thread communication. You can use the synchronization primitives from the `threading` module instead.

Besides, only the main thread is allowed to set a new signal handler.

## 모듈 내용

버전 3.5에서 변경: 이하에 열거된 시그널 (SIG*), 처리기 (`SIG_DFL`, `SIG_IGN`) 및 sigmask (`SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK`) 관련 상수는 `열거형`으로 바뀌었습니다. `getsignal()`, `pthread_sigmask()`, `sigpending()` 및 `sigwait()` 함수는 사람이 읽을 수 있는 `열거형`을 반환합니다.

`signal` 모듈에 정의된 변수는 다음과 같습니다:

`signal.``SIG_DFL`

  이것은 두 가지 표준 시그널 처리 옵션 중 하나입니다; 단순히 시그널의 기본 기능을 수행합니다. 예를 들어, 대부분의 시스템에서 `SIGQUIT`의 기본 동작은 코어를 덤프하고 종료하는 것이지만, `SIGCHLD`의 기본 동작은 단순히 무시하는 것입니다.

`signal.``SIG_IGN`

  이것은 주어진 시그널을 무시하는 또 다른 표준 시그널 처리기입니다.

`signal.``SIGABRT`

  abort(3)로 부터의 중단 시그널.

`signal.``SIGALRM`

  alarm(2)로 부터의 타이머 시그널.

  가용성: 유닉스.

`signal.``SIGBREAK`

  키보드 인터럽트 (CTRL + BREAK).

  가용성: 윈도우.

`signal.``SIGBUS`

  버스 에러 (메모리 액세스 불량).

  가용성: 유닉스.

`signal.``SIGCHLD`

  자식 프로세스가 중지되었거나 종료되었습니다.

  가용성: 유닉스.

`signal.``SIGCLD`

  `SIGCHLD`에 대한 별칭.

`signal.``SIGCONT`

  현재 중지되었으면 프로세스를 재개합니다.

  가용성: 유닉스.

`signal.``SIGFPE`

  부동 소수점 예외. 예를 들어, 0으로 나누기.

  더 보기

  나누기나 모듈로 연산의 두 번째 인자가 0이면 `ZeroDivisionError`가 발생합니다.

`signal.``SIGHUP`

  제어 터미널이 끊어졌거나 제어 프로세스가 죽었습니다.

  가용성: 유닉스.

`signal.``SIGILL`

  잘못된 명령어.

`signal.``SIGINT`

  키보드 인터럽트 (CTRL + C).

  기본 액션은 `KeyboardInterrupt`를 발생시키는 것입니다.

`signal.``SIGKILL`

  킬 시그널.

  잡거나, 차단하거나, 무시할 수 없습니다.

  가용성: 유닉스.

`signal.``SIGPIPE`

  끊어진 파이프: 판독기가 없는 파이프에 쓰기.

  기본 동작은 시그널을 무시하는 것입니다.

  가용성: 유닉스.

`signal.``SIGSEGV`

  세그먼테이션 오류: 유효하지 않은 메모리 참조.

`signal.``SIGTERM`

  종료 시그널.

`signal.``SIGUSR1`

  사용자 정의 시그널 1.

  가용성: 유닉스.

`signal.``SIGUSR2`

  사용자 정의 시그널 2.

  가용성: 유닉스.

`signal.``SIGWINCH`

  창 크기 조정 시그널.

  가용성: 유닉스.

`SIG*`

  모든 시그널 번호는 기호적으로 정의됩니다. 예를 들어, 행업(hangup) 시그널은 `signal.SIGHUP`으로 정의됩니다; 변수 이름은 `<signal.h>`에 있는 C 프로그램에서 사용되는 이름과 동일합니다. ‘`signal()`’에 대한 유닉스 매뉴얼 페이지는 존재하는 시그널을 나열합니다 (일부 시스템에서는 signal(2)이고, 다른 시스템에서는 signal(7)입니다). 모든 시스템이 같은 시그널 이름 집합을 정의하는 것은 아님에 유의하십시오; 시스템에서 정의한 이름만 이 모듈에서 정의합니다.

`signal.``CTRL_C_EVENT`

  `Ctrl+C` 키 입력 이벤트에 해당하는 시그널. 이 시그널은 `os.kill()`에서만 사용할 수 있습니다.

  가용성: 윈도우.

  버전 3.2에 추가.

`signal.``CTRL_BREAK_EVENT`

  `Ctrl+Break` 키 입력 이벤트에 해당하는 시그널. 이 시그널은 `os.kill()`에서만 사용할 수 있습니다.

  가용성: 윈도우.

  버전 3.2에 추가.

`signal.``NSIG`

  가장 높은 시그널 번호보다 하나 큰 값.

`signal.``ITIMER_REAL`

  간격 타이머(interval timer)를 실시간으로 감소시키고, 만료 시 `SIGALRM`을 전달합니다.

`signal.``ITIMER_VIRTUAL`

  프로세스가 실행 중일 때만 간격 타이머(interval timer)를 감소시키고, 만료 시 SIGVTALRM을 전달합니다.

`signal.``ITIMER_PROF`

  프로세스가 실행될 때와 시스템이 프로세스를 대신하여 실행될 때 간격 타이머(interval timer)를 감소시킵니다. ITIMER_VIRTUAL과 함께 사용되어, 이 타이머는 일반적으로 사용자와 커널 공간에서 응용 프로그램이 소비한 시간을 프로파일링하는 데 사용됩니다. 만료 시 SIGPROF를 전달합니다.

`signal.``SIG_BLOCK`

  `pthread_sigmask()`의 how 매개 변수에 가능한 값으로 시그널이 차단됨을 나타냅니다.

  버전 3.3에 추가.

`signal.``SIG_UNBLOCK`

  `pthread_sigmask()`의 how 매개 변수에 가능한 값으로 시그널이 차단 해제됨을 나타냅니다.

  버전 3.3에 추가.

`signal.``SIG_SETMASK`

  `pthread_sigmask()`의 how 매개 변수에 가능한 값으로 시그널 마스크가 교체됨을 나타냅니다.

  버전 3.3에 추가.

`signal` 모듈은 하나의 예외를 정의합니다:

exception `signal.``ItimerError`

  하부 `setitimer()`나 `getitimer()` 구현으로부터의 에러를 알리기 위해 발생합니다. 유효하지 않은 간격 타이머나 음의 시간이 `setitimer()`에 전달되면 이 에러가 예상됩니다. 이 에러는 `OSError`의 서브 형입니다.

  버전 3.3에 추가: 이 에러는 `IOError`의 서브 형이었습니다, 이제는 `OSError`의 별칭입니다.

`signal` 모듈은 다음 함수를 정의합니다:

`signal.``alarm`(time)

  time이 0이 아니면, 이 함수는 `SIGALRM` 시그널이 time 초 내에 프로세스로 전송되도록 요청합니다. 이전에 예약된 알람은 취소됩니다 (임의의 시간에 오직 하나의 알람만 예약될 수 있습니다). 반환된 값은 이전에 설정된 알람이 전달되기까지 남은 초(seconds)입니다. time이 0이면, 알람이 예약되지 않고, 예약된 알람이 취소됩니다. 반환 값이 0이면, 현재 예약된 알람이 없습니다.

  가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 alarm(2)를 참조하십시오.

`signal.``getsignal`(signalnum)

  시그널 signalnum에 대한 현재 시그널 처리기를 반환합니다. 반환된 값은 콜러블 파이썬 객체이거나, 특수 값 `signal.SIG_IGN`, `signal.SIG_DFL` 중 하나이거나 `None`일 수 있습니다. 여기서 `signal.SIG_IGN`은 시그널이 이전에 무시되었음을 의미하고, `signal.SIG_DFL`은 시그널을 처리하는 기본 방법이 이전에 사용 중임을 의미하고, `None`은 이전 시그널 처리기가 파이썬에서 설치되지 않았음을 의미합니다.

`signal.``strsignal`(signalnum)

  시그널 signalnum의 시스템 설명을 반환합니다, 가령 “Interrupt”, “Segmentation fault”, 등. 시그널이 인식되지 않으면 `None`을 반환합니다.

  버전 3.8에 추가.

`signal.``valid_signals`()

  이 플랫폼에서 유효한 시그널 번호 집합을 반환합니다. 일부 시그널이 시스템에서 내부 용으로 예약되었으면 `range(1, NSIG)`보다 작을 수 있습니다.

  버전 3.8에 추가.

`signal.``pause`()

  시그널이 수신될 때까지 프로세스를 휴면 상태로 만듭니다; 그런 다음 적절한 처리기가 호출됩니다. 아무것도 반환하지 않습니다.

  가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 signal(2)를 참조하십시오.

  `sigwait()`, `sigwaitinfo()`, `sigtimedwait()` 및 `sigpending()`도 참조하십시오.

`signal.``raise_signal`(signum)

  호출하는 프로세스에 시그널을 보냅니다. 아무것도 반환하지 않습니다.

  버전 3.8에 추가.

`signal.``pthread_kill`(thread_id, signalnum)

  Send the signal signalnum to the thread thread_id, another thread in the same process as the caller. The target thread can be executing any code (Python or not). However, if the target thread is executing the Python interpreter, the Python signal handlers will be executed by the main thread. Therefore, the only point of sending a signal to a particular Python thread would be to force a running system call to fail with `InterruptedError`.

  thread_id에 적합한 값을 얻으려면 `threading.get_ident()` 나 `threading.Thread` 객체의 `ident` 어트리뷰트를 사용하십시오.

  signalnum이 0이면, 시그널이 전송되지 않지만, 여전히 에러 검사가 수행됩니다; 대상 스레드가 여전히 실행 중인지 확인하는 데 사용할 수 있습니다.

  인자 `thread_id`, `signalnum`으로 감사 이벤트 `signal.pthread_kill`을 발생시킵니다.

  가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 pthread_kill(3)을 참조하십시오.

  `os.kill()`도 참조하십시오.

  버전 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`은 차단할 수 없습니다.

  가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigprocmask(3)과 pthread_sigmask(3)을 참조하십시오.

  `pause()`, `sigpending()` 및 `sigwait()`도 참조하십시오.

  버전 3.3에 추가.

`signal.``setitimer`(which, seconds, interval=0.0)

  seconds(`alarm()`과 달리 float가 허용됩니다) 이후에 그리고 (interval이 0이 아니면) 그 후로 interval 초마다 발사(fire)하도록 which로 지정된 간격 타이머(`signal.ITIMER_REAL`, `signal.ITIMER_VIRTUAL` 또는 `signal.ITIMER_PROF` 중 하나)를 설정합니다. which로 지정된 간격 타이머는 seconds를 0으로 설정하여 지울 수 있습니다.

  간격 타이머가 발사(fire)하면, 시그널이 프로세스로 전송됩니다. 전송된 시그널은 사용 중인 타이머에 따라 다릅니다; `signal.ITIMER_REAL`은 `SIGALRM`을, `signal.ITIMER_VIRTUAL`은 `SIGVTALRM`을, `signal.ITIMER_PROF`는 `SIGPROF`를 전달합니다.

  이전 값은 튜플로 반환됩니다: (지연, 간격).

  유효하지 않은 간격 타이머를 전달하려고 하면 `ItimerError`가 발생합니다.

  가용성: 유닉스.

`signal.``getitimer`(which)

  which로 지정된 주어진 간격 타이머의 현재 값을 반환합니다.

  가용성: 유닉스.

`signal.``set_wakeup_fd`(fd, *, warn_on_full_buffer=True)

  웨이크업 파일 기술자를 fd로 설정합니다. 시그널이 수신되면 시그널 번호는 단일 바이트로 fd에 기록됩니다. 라이브러리에서 poll이나 select 호출을 깨워서, 시그널을 완전히 처리하는 데 사용될 수 있습니다.

  이전 웨이크업 fd가 반환됩니다 (또는 파일 기술자 웨이크업이 활성화되지 않았으면 -1). fd가 -1이면, 파일 기술자 웨이크업이 비활성화됩니다. -1이 아니면, fd는 비 블로킹이어야 합니다. poll이나 select를 다시 호출하기 전에 fd에서 바이트를 제거하는 것은 라이브러리의 책임입니다.

  When threads are enabled, this function can only be called from the main thread; attempting to call it from other threads will cause a `ValueError` exception to be raised.

  이 함수를 사용하는 일반적인 두 가지 방법이 있습니다. 두 방법 모두, 시그널이 도착할 때 깨어나기 위해 fd를 사용하지만, 어떤 시그널이나 시그널들이 도착했는지 판단하는 방법이 다릅니다.

  첫 번째 방법에서는, fd의 버퍼에서 데이터를 읽고, 바이트 값이 시그널 번호를 제공합니다. 이것은 간단합니다만, 드물게 문제가 될 수 있습니다: 일반적으로 fd에는 제한된 버퍼 공간이 있으며, 너무 많은 시그널이 너무 빨리 도착하면, 버퍼가 가득 차고, 일부 시그널이 손실될 수 있습니다. 이 방법을 사용하면, 시그널이 손실될 때 최소한 stderr에 경고가 인쇄되도록 `warn_on_full_buffer=True`를 설정해야 합니다.

  두 번째 방법에서는, 오직 웨이크업만을 위해 웨이크업 fd를 사용하고, 실제 바이트 값은 무시합니다. 이 경우, 우리가 신경 쓰는 것은 fd의 버퍼가 비어 있는지 비어 있지 않은지 입니다; 가득 찬 버퍼는 전혀 문제를 가리키지 않습니다. 이 방법을 사용하면, 사용자가 가짜 경고 메시지로 혼동되지 않도록 `warn_on_full_buffer=False`를 설정해야 합니다.

  버전 3.5에서 변경: 윈도우에서, 이 함수는 이제 소켓 핸들도 지원합니다.

  버전 3.7에서 변경: `warn_on_full_buffer` 매개 변수를 추가했습니다.

`signal.``siginterrupt`(signalnum, flag)

  시스템 호출 재시작 동작을 변경합니다: flag가 `False`이면, 시그널 signalnum에 의해 인터럽트 될 때 시스템 호출이 다시 시작되고, 그렇지 않으면 시스템 호출이 중단됩니다. 아무것도 반환하지 않습니다.

  가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 siginterrupt(3)을 참조하십시오.

  `signal()`로 시그널 처리기를 설치하면 주어진 시그널에 대해 flag 값을 참으로 `siginterrupt()`를 묵시적으로 호출하여 재시작 동작을 인터럽트 가능으로 재설정합니다.

`signal.``signal`(signalnum, handler)

  시그널 signalnum의 처리기를 함수 handler로 설정합니다. handler는 두 개의 인자(아래를 참조하십시오)를 취하는 콜러블 파이썬 객체, 또는 특수 값 `signal.SIG_IGN`이나 `signal.SIG_DFL` 중 하나일 수 있습니다. 이전 시그널 처리기가 반환됩니다 (위의 `getsignal()` 설명을 참조하십시오). (자세한 내용은 유닉스 매뉴얼 페이지 signal(2)를 참조하십시오.)

  When threads are enabled, this function can only be called from the main thread; attempting to call it from other threads will cause a `ValueError` exception to be raised.

  handler는 두 개의 인자로 호출됩니다: 시그널 번호와 현재 스택 프레임 (`None`이나 프레임 객체; 프레임 객체에 대한 설명은, 형 계층에 있는 설명을 참조하거나 `inspect` 모듈의 어트리뷰트 설명을 참조하십시오).

  윈도우에서, `signal()`은 `SIGABRT`, `SIGFPE`, `SIGILL`, `SIGINT`, `SIGSEGV`, `SIGTERM` 또는 `SIGBREAK`로만 호출 할 수 있습니다. 다른 경우에는 `ValueError`가 발생합니다. 모든 시스템이 같은 시그널 이름 집합을 정의하는 것은 아님에 유의하십시오; 시그널 이름이 `SIG*` 모듈 수준 상수로 정의되지 않으면 `AttributeError`가 발생합니다.

`signal.``sigpending`()

  호출하는 스레드로 전달 계류 중인 시그널 집합을 검사합니다 (즉, 차단된 동안 발생한 시그널). 계류 중인 시그널 집합을 반환합니다.

  가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigpending(2)를 참조하십시오.

  `pause()`, `pthread_sigmask()` 및 `sigwait()`도 참조하십시오.

  버전 3.3에 추가.

`signal.``sigwait`(sigset)

  시그널 집합 sigset에 지정된 시그널 중 하나가 전달될 때까지 호출하는 스레드의 실행을 일시 중단합니다. 이 함수는 시그널을 받아들이고 (계류 중인 시그널 목록에서 제거합니다), 시그널 번호를 반환합니다.

  가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigwait(3)을 참조하십시오.

  `pause()`, `pthread_sigmask()`, `sigpending()`, `sigwaitinfo()` 및 `sigtimedwait()`도 참조하십시오.

  버전 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`를 표현하는 객체입니다.

  가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigwaitinfo(2)를 참조하십시오.

  `pause()`, `sigwait()` 및 `sigtimedwait()`도 참조하십시오.

  버전 3.3에 추가.

  버전 3.5에서 변경: 이 함수는 이제 sigset에 없는 시그널에 의해 중단되고 시그널 처리기가 예외를 발생시키지 않으면 재시도됩니다 (이유는 PEP 475를 참조하십시오).

`signal.``sigtimedwait`(sigset, timeout)

  `sigwaitinfo()`와 유사하지만, 시간제한을 지정하는 추가 timeout 인자를 취합니다. timeout이 `0`으로 지정되면, 폴링이 수행됩니다. 시간제한 초과가 발생하면 `None`을 반환합니다.

  가용성: 유닉스. 자세한 내용은 매뉴얼 페이지 sigtimedwait(2)를 참조하십시오.

  `pause()`, `sigwait()` 및 `sigwaitinfo()`도 참조하십시오.

  버전 3.3에 추가.

  버전 3.5에서 변경: 이 함수는 이제 sigset에 없는 시그널에 의해 중단되고 시그널 처리기가 예외를 발생시키지 않으면 다시 계산된 timeout으로 재시도됩니다 (이유는 PEP 475를 참조하십시오).

## 예

다음은 최소한의 예제 프로그램입니다. `alarm()` 함수를 사용하여 파일을 여는 데 대기하는 시간을 제한합니다; 이것은 파일이 켜져 있지 않을 수 있는 직렬 장치를 위한 파일일 때 유용하며, 일반적으로 `os.open()`이 무기한 정지됩니다. 해결책은 파일을 열기 전에 5초 알람을 설정하는 것입니다; 작업이 너무 오래 걸리면, 알람 시그널이 전송되고, 처리기가 예외를 발생시킵니다.

    import signal, os

    def handler(signum, frame):
        print('Signal handler called with signal', 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`로 설정하지 마십시오. 그렇게 하면 프로그램이 여전히 쓰고 있는 동안 소켓 연결이 중단될 때마다 프로그램이 예기치 않게 종료되도록 합니다.