신호 (IPC)

신호 또는 시그널(signal)은 실행 중인 프로그램에 종료 또는 오류 처리와 같은 특정 동작을 유발하도록 보내는 표준화된 메시지이다. 이는 일반적으로 유닉스, 유닉스 계열, 그리고 기타 POSIX 호환 운영 체제에서 사용되는 프로세스 간 통신 (IPC)의 제한된 형태이다.

시그널은 이벤트 발생을 알리기 위해 프로세스 또는 동일한 프로세스 내의 특정 스레드에 전송되는 비동기적 알림이다. 시그널의 일반적인 용도는 프로세스를 중단, 일시 중지, 종료 또는 강제 종료하는 것이다. 시그널은 1970년대 벨 연구소 유닉스에서 시작되었으며 나중에 POSIX 표준에 명시되었다.

시그널이 전송되면 운영 체제는 대상 프로세스의 정상적인 실행 흐름을 중단하여 시그널을 전달한다. 실행은 모든 비원자적 명령어 중에 중단될 수 있다. 프로세스가 이전에 시그널 핸들러를 등록한 경우 해당 루틴이 실행된다. 그렇지 않으면 기본 시그널 핸들러가 실행된다.

임베디드 프로그램은 시그널이 알고리즘 효율성이 뛰어나다는 점에서 프로세스 간 통신에 유용하다고 생각할 수 있다.

시그널은 인터럽트와 유사하지만, 인터럽트가 CPU에 의해 중재되고 커널에 의해 처리되는 반면, 시그널은 커널(시스템 호출을 통해 가능)에 의해 중재되고 개별 프로세스에 의해 처리된다는 점이 다르다. 커널은 인터럽트를 발생시킨 프로세스에 시그널로 전달할 수 있다(일반적인 예로는 SIGSEGV, SIGBUS, SIGILL 및 SIGFPE가 있다).

역사
 버전 1 유닉스 (1971)에는 인터럽트, 종료 및 기계 트랩을 잡기 위한 별도의 시스템 호출이 있었다.
 은 버전 2 (1972)에 나타났다.
 버전 4 (1973)는 모든 트랩을 이라는 하나의 호출로 결합했다.
 버전 5 (1974)는 임의의 시그널을 보낼 수 있었다.
 버전 7 (1979)에서는 각 번호가 매겨진 트랩에 상징적인 이름이 부여되었다.
 플랜 9 (운영체제) (1980년대 후반)는 시그널을 짧고 임의의 문자열을 보낼 수 있는 노트로 대체했다.

시그널 보내기
 시스템 호출은 권한이 허용하는 경우 지정된 시그널을 지정된 프로세스에 보낸다. 마찬가지로 명령은 사용자가 프로세스에 시그널을 보낼 수 있도록 한다. 라이브러리 함수는 현재 프로세스에 지정된 시그널을 보낸다.

0으로 나누기, 세그멘테이션 오류 (SIGSEGV), 부동 소수점 예외 (SIGFPE)와 같은 예외는 코어 덤프를 유발하고 프로그램을 종료시킨다.

커널은 프로세스에 이벤트를 알리기 위해 시그널을 생성할 수 있다. 예를 들어, SIGPIPE는 프로세스가 읽는 쪽에서 닫힌 파이프에 쓸 때 생성된다. 기본적으로 이로 인해 프로세스가 종료되는데, 이는 셸 파이프라인을 구성할 때 편리하다.

실행 중인 프로세스의 제어 터미널에서 특정 키 조합을 입력하면 시스템이 해당 프로세스에 특정 시그널을 보낸다.

 Ctrl-C (구형 유닉스에서는 DEL)는 INT 시그널("인터럽트", SIGINT)을 보낸다. 기본적으로 이로 인해 프로세스가 종료된다.
 Ctrl-Z는 TSTP 시그널("터미널 정지", SIGTSTP)을 보낸다. 기본적으로 이로 인해 프로세스가 실행을 일시 중단한다.
 Ctrl-\는 QUIT 시그널 (SIGQUIT)을 보낸다. 기본적으로 이로 인해 프로세스가 종료되고 코어 덤프가 생성된다.
 Ctrl-T (모든 유닉스에서 지원되지는 않음)는 INFO 시그널 (SIGINFO)을 보낸다. 기본적으로, 명령이 지원하는 경우, 운영 체제가 실행 중인 명령에 대한 정보를 표시한다.

최신 운영 체제에서 이러한 기본 키 조합은 명령으로 변경할 수 있다.

시그널 처리
시그널 핸들러는 또는 시스템 호출을 사용하여 설치할 수 있다. 특정 시그널에 대해 시그널 핸들러가 설치되지 않은 경우 기본 핸들러가 사용된다. 그렇지 않으면 시그널이 가로채지고 시그널 핸들러가 호출된다. 프로세스는 핸들러를 생성하지 않고도 두 가지 기본 동작을 지정할 수 있다: 시그널 무시(SIG_IGN) 및 기본 시그널 핸들러 사용(SIG_DFL). 가로채거나 처리할 수 없는 두 가지 시그널이 있다: SIGKILL 및 SIGSTOP.

위험
시그널 처리는 경쟁 상태에 취약하다. 시그널은 비동기적이므로 시그널 처리 루틴이 실행되는 동안 다른 시그널(동일한 유형이라도)이 프로세스에 전달될 수 있다.

 호출은 시그널 전달을 차단하고 차단 해제하는 데 사용될 수 있다. 차단된 시그널은 차단 해제될 때까지 프로세스에 전달되지 않는다. 무시할 수 없는 시그널(SIGKILL 및 SIGSTOP)은 차단할 수 없다.

시그널은 진행 중인 시스템 호출을 중단시켜 응용 프로그램이 비투명 재시작을 관리하도록 할 수 있다.

시그널 핸들러는 변경, 시그널 마스크 변경, 시그널 처리 변경 및 기타 전역 프로세스 속성 변경과 같이 원치 않는 부작용이 발생하지 않도록 작성되어야 한다. 시그널 핸들러 내부에서 또는 와 같이 재진입 불가능한 함수를 사용하는 것도 안전하지 않다. 특히, POSIX 사양 및 리눅스 맨 페이지 은 시그널 함수에서 직접 또는 간접적으로 호출되는 모든 시스템 함수가 비동기 시그널 안전(async-signal safe)해야 한다고 요구한다. 맨 페이지는 그러한 비동기 시그널 안전 시스템 함수(실질적으로 시스템 호출) 목록을 제공하며, 그렇지 않으면 미정의 동작이다. 시그널 핸들러에서 단순히 일부 변수를 설정하고 다른 곳에서 테스트하는 것이 제안된다.

시그널 핸들러는 대신 시그널을 큐에 넣고 즉시 반환할 수 있다. 그러면 주 스레드는 이벤트 루프에서와 같이 큐에서 시그널을 가져올 때까지 "중단 없이" 계속된다. 여기서 "중단 없이"는 위에 언급된 바와 같이 블로킹되는 작업이 조기에 반환되고 재개되어야 한다는 것을 의미한다. 시그널은 주 스레드에서 큐에서 처리되어야 하며, 작업자 풀에서는 처리되어서는 안 된다. 이는 비동기성 문제를 다시 도입하기 때문이다. 그러나 큐 관리는 만으로는 비동기 시그널 안전한 방식으로 가능하지 않다. 큐에 필요한 증감 또는 (가져오기 및) 감소가 아니라 해당 변수에 대한 단일 읽기 및 쓰기만 원자성이 보장되기 때문이다. 따라서 사실상 핸들러당 하나의 시그널만 로 안전하게 큐에 넣을 수 있으며, 처리될 때까지 가능하다.

하드웨어 예외와의 관계
프로세스의 실행은 예를 들어, 프로세스가 0으로 나누기를 시도하거나 페이지 부재가 발생할 경우 하드웨어 예외를 발생시킬 수 있다.

유닉스 계열 운영 체제에서는 이 이벤트가 자동으로 프로세서 컨텍스트를 변경하여 커널 예외 핸들러를 실행하기 시작한다. 페이지 부재와 같은 일부 예외의 경우 커널은 이벤트를 완전히 스스로 처리하고 프로세스의 실행을 재개하기에 충분한 정보를 가지고 있다.

그러나 다른 예외의 경우 커널은 지능적으로 처리할 수 없으며 대신 예외 처리 작업을 오류 발생 프로세스에 위임해야 한다. 이 위임은 시그널 메커니즘을 통해 이루어지는데, 커널은 현재 예외에 해당하는 시그널을 프로세스에 보낸다. 예를 들어, 프로세스가 x86 CPU에서 정수 0으로 나누기를 시도하면, 나누기 오류 예외가 발생하여 커널이 프로세스에 SIGFPE 시그널을 보내게 된다.

마찬가지로, 프로세스가 가상 주소 공간 외부의 메모리 주소에 액세스하려고 시도하면, 커널은 SIGSEGV (세그멘테이션 오류 시그널)를 통해 이 위반을 프로세스에 알린다. 시그널 이름과 예외 간의 정확한 매핑은 아키텍처마다 예외 유형이 다르므로 분명히 CPU에 따라 다르다.

POSIX 시그널
아래 목록은 단일 유닉스 규격 버전 5에 명시된 시그널을 문서화한다. 모든 시그널은 
 헤더 파일에 "SIG" 접두사와 시그널의 니모닉 이름으로 구성된 매크로 상수로 정의된다.

프로세스는 수신하는 POSIX 시그널을 처리하는 방법을 정의할 수 있다. 프로세스가 시그널에 대한 동작을 정의하지 않으면 해당 시그널의 기본 핸들러가 사용된다. 아래 표는 FreeBSD, OpenBSD, 리눅스와 같은 POSIX 호환 유닉스 시스템의 일부 기본 동작을 나열한다.

 포터블 번호
 대부분의 시그널에 대해 해당 시그널 번호는 구현 정의이다. 이 열은 POSIX 표준에 명시된 번호를 나열한다.

 동작 설명
 종료 프로세스의 비정상 종료. 프로세스는 _exit()의 모든 결과와 함께 종료되지만, wait() 및 waitpid()에 제공되는 상태는 지정된 시그널에 의한 비정상 종료를 나타낸다.
 종료 (코어 덤프) 프로세스의 비정상 종료. 또한 코어 파일 생성과 같은 구현 정의 비정상 종료 작업이 발생할 수 있다.
 무시 시그널 무시.
 정지 프로세스 정지 (또는 일시 중단).
 계속 프로세스가 정지된 경우 계속 진행; 그렇지 않으면 시그널 무시.

 및 
 SIGABRT 시그널은 프로세스에 중단 즉, 종료를 알리기 위해 전송된다. 이 시그널은 일반적으로 C 표준 라이브러리의 abort() 함수를 호출할 때 프로세스 자체에서 시작되지만, 다른 시그널과 마찬가지로 외부에서 프로세스로 전송될 수도 있다.
 SIGIOT는 CPU가 명시적인 "트랩" 명령어(정의된 기능 없음) 또는 구현되지 않은 명령어(에뮬레이션 불가능)를 실행했음을 나타낸다.
참고: "입출력 트랩"은 모든 CPU "트랩" 명령어에 대한 잘못된 명칭이다. 이 용어는 주로 I/O 기능을 구현하는 데 이러한 명령어를 초기에 사용한 것을 반영하지만, 본질적으로 장치 I/O와 연결되어 있지 않으며 가상 및 실제 호스트 간 통신과 같은 다른 목적으로 사용될 수도 있다.
 SIGIOT와 SIGABRT는 일반적으로 동일한 시그널이며, 이 시그널을 수신하면 위의 조건 중 하나를 나타낼 수 있다.
 , 및 
 SIGALRM, SIGVTALRM 및 SIGPROF 시그널은 해당 시간 제한에 도달했을 때 프로세스에 전송된다. 프로세스는 alarm 또는 setitimer를 호출하여 이러한 시간 제한을 설정한다. SIGALRM의 시간 제한은 실제 시간 또는 클럭 시간을 기준으로 하며, SIGVTALRM은 프로세스에서 사용한 CPU 시간을 기준으로 하고, SIGPROF는 프로세스 및 시스템이 프로세스 대신 사용한 CPU 시간(프로파일링 타이머로 알려짐)을 기준으로 한다. 일부 시스템에서는 SIGALRM이 sleep 함수의 구현에 의해 내부적으로 사용될 수 있다.
 
 SIGBUS 시그널은 프로세스가 버스 오류를 일으킬 때 프로세스에 전송된다. 이 시그널이 전송되는 조건은 예를 들어, 잘못된 메모리 접근 정렬 또는 존재하지 않는 물리 주소이다.
 
 SIGCHLD 시그널은 자식 프로세스가 종료되거나, 정지되거나, 정지된 후 재개될 때 프로세스에 전송된다. 이 시그널의 일반적인 용도 중 하나는 wait 시스템 호출을 명시적으로 호출하지 않고도 자식 프로세스 종료 후 운영 체제가 자식 프로세스에서 사용한 리소스를 정리하도록 지시하는 것이다.
 
 SIGCONT 시그널은 SIGSTOP 또는 SIGTSTP 시그널에 의해 이전에 일시 중지된 프로세스를 계속 (재시작)하도록 운영 체제에 지시한다. 이 시그널의 중요한 용도 중 하나는 유닉스 셸의 작업 제어이다.
 
 SIGFPE 시그널은 부동 소수점 또는 정수 산술 하드웨어에서 예외적인(반드시 오류는 아닌) 조건이 감지되었을 때 프로세스에 전송된다. 여기에는 0으로 나누기, 부동 소수점 언더플로 또는 오버플로, 정수 오버플로, 잘못된 연산 또는 부정확한 계산이 포함될 수 있다. 동작은 하드웨어에 따라 다를 수 있다.
 
 SIGHUP 시그널은 제어 터미널이 닫힐 때 프로세스에 전송된다. 원래는 직렬 회선 끊김(행업)을 프로세스에 알리기 위해 고안되었다. 최신 시스템에서는 이 시그널이 일반적으로 제어 의사 또는 가상 터미널이 닫혔음을 의미한다. 많은 데몬 (제어 터미널이 없는)은 이 시그널 수신을 종료 대신 구성 파일을 다시 로드하고 로그 파일을 플러시/재개하는 요청으로 해석한다. Nohup은 명령이 시그널을 무시하도록 하는 명령이다.
 
 SIGILL 시그널은 프로세스가 불법적이거나, 잘못 형성되었거나, 알 수 없거나, 특권 명령어를 실행하려고 시도할 때 프로세스에 전송된다.
 
 SIGINT 시그널은 사용자가 프로세스를 인터럽트하기를 원할 때 제어 터미널에 의해 프로세스에 전송된다. 이는 일반적으로 를 눌러 시작되지만, 일부 시스템에서는 "delete" 문자 또는 "break" 키를 사용할 수 있다.
 
 SIGKILL 시그널은 프로세스에 즉시 종료하도록 (강제 종료) 전송된다. SIGTERM 및 SIGINT와 달리 이 시그널은 잡거나 무시할 수 없으며, 수신 프로세스는 이 시그널을 수신할 때 어떤 정리 작업도 수행할 수 없다. 다음 예외가 적용된다:
 좀비 프로세스는 이미 죽었고 부모 프로세스가 이를 회수하기를 기다리고 있기 때문에 강제 종료될 수 없다.
 차단된 상태에 있는 프로세스는 다시 깨어날 때까지 죽지 않는다.
 Init 프로세스는 특별하다: 처리하고 싶지 않은 시그널은 받지 않으므로 SIGKILL을 무시할 수 있다. 이 규칙의 예외는 리눅스에서 init가 Ptrace될 때이다.
 방해받지 않고 슬립하는 프로세스는 SIGKILL이 전송되더라도 종료되지 않을 수 있다(및 리소스 해제). 이는 유닉스 시스템이 일시적인 소프트웨어 문제를 해결하기 위해 재부팅해야 하는 몇 안 되는 경우 중 하나이다.
SIGKILL은 대부분의 시스템 종료 절차에서 SIGTERM에 응답하여 자발적으로 종료되지 않는 경우 프로세스를 종료하는 최후의 수단으로 사용된다. 컴퓨터 종료 절차를 빠르게 하기 위해 맥 OS X 10.6, 즉 맥 OS X 스노 레퍼드는 스스로 "정리됨"으로 표시한 애플리케이션에 SIGKILL을 전송하여 종료 시간을 단축하고, 아마도 악영향은 없을 것이다. 명령은 예를 들어 리눅스에서 실행될 때 유사하지만 위험한 효과를 가진다. 이 명령은 프로그램이 저장되지 않은 데이터를 저장하는 것을 허용하지 않는다. 다른 옵션이 있으며, 옵션이 없으면 더 안전한 SIGTERM 시그널을 사용한다.
 
 SIGPIPE 시그널은 프로세스가 다른 쪽 끝에 연결된 프로세스가 없는 파이프에 쓰려고 시도할 때 프로세스에 전송된다.
 
 SIGPOLL 시그널은 명시적으로 감시되는 파일 디스크립터에서 이벤트가 발생했을 때 전송된다. 효과적으로 사용하면 커널이 호출자 대신 디스크립터를 폴링하므로 비동기 입출력 요청을 만드는 데 도움이 된다. 이는 능동적인 폴링에 대한 대안을 제공한다.
 ~ 
 SIGRTMIN에서 SIGRTMAX 시그널은 사용자 정의 목적으로 사용될 예정이다. 이들은 실시간 시그널이다.
 
 SIGQUIT 시그널은 사용자가 프로세스를 종료하고 코어 덤프를 수행하도록 요청할 때 제어 터미널에 의해 프로세스에 전송된다.
 
 SIGSEGV 시그널은 프로세스가 유효하지 않은 가상 메모리 참조, 즉 세그멘테이션 오류를 수행할 때 프로세스에 전송된다.
 
 SIGSTOP 시그널은 운영 체제에 프로세스를 나중에 재개하기 위해 정지하도록 지시한다.
 
 SIGSYS 시그널은 프로세스가 시스템 호출에 잘못된 인수를 전달할 때 전송된다. 실제로, 이 종류의 시그널은 응용 프로그램이 libc와 같은 라이브러리에 의존하여 호출을 대신 수행하기 때문에 거의 발생하지 않는다. SIGSYS는 Seccomp 보안 규칙을 위반하여 제한된 응용 프로그램에서 수신될 수 있다. SIGSYS는 또한 외부 시스템 호출을 에뮬레이션하는 데 사용될 수 있다(예: 리눅스에서 윈도우 시스템 호출 에뮬레이션).
 
 SIGTERM 시그널은 프로세스에 종료를 요청하기 위해 전송된다. SIGKILL 시그널과 달리 이 시그널은 프로세스에 의해 잡히고 해석되거나 무시될 수 있다. 이는 프로세스가 적절한 경우 리소스를 해제하고 상태를 저장하여 정상적인 종료를 수행할 수 있도록 한다. SIGINT는 SIGTERM과 거의 동일하다.
 
 SIGTSTP 시그널은 제어 터미널에 의해 프로세스에 전송되어 정지를 요청한다 (terminal stop). 이는 일반적으로 사용자가 를 눌러 시작된다. SIGSTOP과 달리 프로세스는 이 시그널에 대한 시그널 핸들러를 등록하거나 무시할 수 있다.
 및 
 SIGTTIN 및 SIGTTOU 시그널은 프로세스가 백그라운드 상태에서 tty로부터 각각 읽기 또는 쓰기를 시도할 때 프로세스에 전송된다. 일반적으로 이러한 시그널은 작업 제어 하의 프로세스만 수신한다. 데몬은 제어 터미널이 없으므로 이러한 시그널을 절대 수신해서는 안 된다.
 
 SIGTRAP 시그널은 예외(또는 트랩)가 발생할 때 프로세스에 전송된다. 이는 디버거가 정보를 요청한 조건(예: 특정 함수가 실행될 때 또는 특정 변수의 값이 변경될 때)이다.
 
 SIGURG 시그널은 소켓에 긴급 또는 아웃 오브 밴드 데이터가 읽을 수 있을 때 프로세스에 전송된다.
 및 
 SIGUSR1 및 SIGUSR2 시그널은 프로세스에 사용자 정의 조건을 나타내기 위해 전송된다.
 
 SIGXCPU 시그널은 프로세스가 미리 정해진 사용자 설정 값을 초과하는 시간 동안 CPU를 사용했을 때 프로세스에 전송된다. SIGXCPU 시그널의 도착은 수신 프로세스에게 중간 결과를 빠르게 저장하고 운영 체제에 의해 SIGKILL 시그널을 사용하여 종료되기 전에 정상적으로 종료할 기회를 제공한다.
 
 SIGXFSZ 시그널은 프로세스가 허용된 최대 크기를 초과하는 파일을 확장할 때 프로세스에 전송된다.
 
 SIGWINCH 시그널은 제어 터미널의 크기가 변경될 때 (window change) 프로세스에 전송된다.

기타 시그널
다음 시그널은 POSIX 사양에 명시되어 있지 않다. 그러나 때때로 다양한 시스템에서 사용된다.

 
 SIGEMT 시그널은 에뮬레이터 트랩이 발생할 때 프로세스에 전송된다. 에뮬레이터는 일반적으로 다른 프로그램을 실행하는 소프트웨어를 의미하지만, 이 경우 프로그램이 슈퍼바이저 호출 명령어를 실행했음을 의미한다 (EMT는 DEC PDP-11 컴퓨터 시리즈에서 이 목적을 위한 명령어였다).
 
 SIGINFO 시그널은 제어 터미널에서 상태 (정보) 요청을 수신할 때 프로세스에 전송된다.
 
 SIGPWR 시그널은 시스템이 정전을 겪을 때 프로세스에 전송된다.
 
 SIGLOST 시그널은 파일 잠금이 손실될 때 프로세스에 전송된다.
 
 SIGSTKFLT 시그널은 코프로세서가 스택 폴트(즉, 스택이 비어 있을 때 팝하거나 가득 찼을 때 푸시하는 경우)를 겪을 때 프로세스에 전송된다. 리눅스에서 정의되어 있지만 사용되지 않으며, x87 코프로세서 스택 폴트는 대신 SIGFPE를 생성한다.
 
 SIGUNUSED 시그널은 사용되지 않는 시스템 호출 번호로 시스템 호출이 이루어질 때 프로세스에 전송된다. 대부분의 아키텍처에서는 SIGSYS와 동의어이다.
 
 SIGCLD 시그널은 SIGCHLD와 동의어이다.

 같이 보기 
 비동기 프로시저 호출 (APC)
 C 시그널 처리

 각주 

 
 

 외부 링크 
 유닉스 시그널 표, 알리 알란자위, 피츠버그 대학교 
 Man7.org 시그널 맨 페이지
 유닉스 시그널 프로그래밍 소개 
 유닉스 시그널 프로그래밍에 대한 또 다른 소개 (블로그 게시물, 2009)
 시그널 핸들러 헤닝 브라우어

분류:프로세스 간 통신
분류:제어 흐름