시그널(signal)은 어떤 이벤트가 발생했음을 알려주기 위해 사용한다. 구체적인 데이터를 전송할 수는 없고, 어떤 이벤트가 발생했는지 체크하기 위한 종류만 전달할 수 있다.
시그널은 방향성이 있어서 보내는 쪽과 받는 쪽이 있다. 그래서 시그널을 보낼 때는 대상 프로세스를 지정하며, 시그널을 수신한 쪽은 누가/언제/어떤 목적으로 시그널을 보낸 것인지 알 수는 없다. (다만 시그널이 어떤 종류인지는 인지한다.) 수신한 쪽은 그냥 시그널의 종류에 따라 동작할 뿐이다.
소켓은 수신하는 쪽에서 데이터를 처리하지 않으면 큐에 쌓인다. 그리고 큐가 허용하는 범위 내에서는 데이터가 손실되지 않고 차곡차곡 쌓인다.
하지만 시그널은 다르다. 시그널은 큐에 쌓이지 않고, 프로세스가 수신한 각 시그널은 종류에 따라 독립적으로 처리된다. 같은 종류의 시그널이 중복해서 수신되면 중복된 녀석은 손실된다. 예를 들어 시그널을 위한 스위치가 있다고 생각하면 된다. SIGINT라는 시그널을 받은 프로세스는 SIGINT 스위치가 켜지고 그 시그널이 2번 오든, 100번 오든 스위치는 한 번만 켜질 것이다.
시그널은 POSIX 표준에 정의된 IPC 도구이다. 그래서 POSIX 표준에 시그널의 종류도 정의되어 있다. SIGINT, SIGTERM, SIGKILL 등 모두 POSIX 표준 시그널이다. 이런 표준 시그널은 유닉스 기반 시스템에서 일관된 방식으로 동작해서 이식성과 호환성을 향상시킨다.
리눅스에서는 POSIX 표준 시그널 이외에도, 특화된 시그널을 제공한다. SIGWINCH(창 크기 변경 시그널), SIGIO(I/O 가능 시그널) 등은 모두 유닉스 표준이 아닌, 리눅스 특화 시그널이다. 또, 리눅스 커널 버전에 따라 지원하는 시그널의 종류가 다를 수 있다. (하지만 대부분 주요 시그널은 POSIX 표준에 정의되어 있어서 너무 걱정할 필요는 없다.)
시그널은 시그널 이름과 시그널 번호로 구성된다. 시그널 이름은 SIG를 접두어로 사용하고 뒤에 시그널의 의미를 나타내는 단어를 붙인다. 예를 들어, SIGINT는 시그널 + INTERRUPT를 의미한다. 각 시그널은 1~64 사이의 유일한 정수 값을 가지는 고유 번호가 부여되고, 이것을 시그널 번호라고 부른다. 리눅스에서 지원하는 시그널 종류는 kill -l 로 확인할 수 있다.
대표적인 시그널은 다음과 같다.
시그널 | 의미 |
---|---|
SIGABRT | 의도적인 중단(Abort) |
SIGALRM | 정해진 시간(alarm)이 되었음 |
SIGBUS | 하드웨어 버스 에러 |
SIGCHLD | 자식 프로세스 종료 |
SIGSTOP | 프로세스 중단 |
(실행을 일시 중지 - suspend, 영원히 끝내버리는 것이 아님) | |
SIGCONT | 중단된 프로세스 재개 |
SIGHUP | 프로세스의 제어 터미널이 닫힘 |
SIGINT | 사용자가 인터럽트(ctrl + c) 생성 |
SIGQUIT | 사용자가 종료 문자(ctrl + \) 생성 |
SIGTSTP | 사용자가 일시 중지 문자(Ctrl + z) 생성 |
SIGPIPE | 프로세스가 잘못된 파이프에 쓰기 시도 |
SIGSEGV | 허용되지 않은 메모리 영역에 접근할 때 발생 |
SIGKILL | 프로세스 종료 명령 |
SIGTERM | 프로세스 종료 명령(조건에 따른 처리 가능) |
SIGTERM: 프로세스가 이 시그널을 받아서 처리할 수 있습니다. 프로세스는 자원을 정리하고, 파일을 닫고, 정상적으로 종료하는 작업을 수행할 수 있습니다.
SIGKILL: 프로세스가 이 시그널을 무시하거나 처리할 수 없습니다. 커널이 즉시 프로세스를 강제 종료합니다. | | SIGUSR1 | 사용자 정의 시그널1 | | SIGUSR2 | 사용자 정의 시그널2 |
프로세스가 시그널을 수신하면 그 프로세스는 수신한 시그널을 처리해야 한다. 프로그램은 시그널 별로 어떻게 처리할지 코드로 정의할 수 있다. 특정 시그널을 수신했을 때 어떻게 처리할지 설정되어 있지 않으면 디폴트로 설정된 방법을 사용한다. 그 방법은 다음 표와 같다.
기본 처리 방법 | 설명 | 주요 시그널 |
---|---|---|
프로세스 종료 | 시그널을 수신하면 프로세스를 종료한다. | SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGTERM, SIGKILL, SIGUSR1, SIGUSR2 등 |
프로세스 중단 | 프로세스를 중단시킨다. | SIGSTOP, SIGTSTP |
프로세스 시작 | 중단되었던 프로세스를 재개한다. | SIGCONT |
무시 | 프로세스가 시그널을 수신해도 아무것도 하지 않는다. | SIGCHLD, SIGURG |
코어 덤프 | 코어 덤프 파일을 생성하고, 프로세스를 종료시킨다. | |
gdb 등 디버거로 코어 덤프 파일을 읽어 문제가 발생한 순간의 메모리와 레지스터 정보를 파악할 수 있다. | SIGABRT, SIGBUS, SIGQUIT, SIGSEGV, SIGFPE |
kill [옵션] PID
로 시그널을 쉽게 전송할 수 있다.
옵션 | 설명 |
---|---|
-l | 사용 가능한 시그널 목록을 출력한다. |
- 시그널_번호 | 시그널 번호로 프로세스에 시그널을 보낸다. |
- 시그널_이름 | 시그널 이름으로 프로세스에 시그널을 보낸다. |
SIG로 시작하는 이름 전체를 보낼 수도 있고, SIG를 제외한 축약형을 보낼 수도 있다. | |
-s 시그널_이름 | |
(--signal 시그널_이름) |
시그널 이름으로 프로세스에 시그널을 보낸다. |