시그널 프로그래밍의 이해
시그널은 운영체제가 프로세스 간 통신을 위해 제공하는 비동기적 메시지 전달 메커니즘이다. 시그널은 프로세스에 특정 이벤트가 발생했음을 알리거나 작업을 중단하도록 요청하기 위해 사용된다. 이 글에서는 시그널의 동작 원리, System V와 POSIX 표준에 기반한 시그널 프로그래밍, 그리고 운영체제와 시그널의 관계를 자세히 설명한다.
시그널의 기본 개념
- 시그널의 정의
시그널은 정수로 표현되는 작은 메시지로, 특정 이벤트를 알리기 위해 사용된다. 프로세스는 시그널을 받으면 이를 처리하거나 무시한다. - 시그널의 주요 상태
- Pending: 시그널이 도착했지만 아직 처리되지 않은 상태.
- Blocked: 프로세스가 시그널을 처리하지 않고 차단한 상태.
- Not Queued: 동일한 시그널이 여러 번 발생해도 한 번만 처리됨.
- 시그널의 기본 동작 각 시그널은 고유의 기본 동작을 가진다:
- Terminate: 프로세스 종료.
- Core Dump: 프로세스 종료 및 코어 덤프 생성.
- Stop: 프로세스 일시 중지.
- Ignore: 시그널 무시.
- Continue: 중지된 프로세스 재개.
- 시그널 발생 방법 시그널은 다음과 같은 방법으로 발생할 수 있다:
- 키보드 입력: Ctrl+C로 SIGINT 발생, Ctrl+Z로 SIGTSTP 발생.
- 시스템 호출: kill() 함수를 통해 특정 시그널을 보냄.
- 시스템 이벤트: 예를 들어, SIGCHLD는 자식 프로세스 종료 시 발생.
- 알람 타이머: alarm() 함수로 특정 시그널을 설정.
운영체제와 시그널의 관계
운영체제는 커널 내부에서 시그널을 처리하며, 다음과 같은 단계로 동작한다:
- 시그널 생성
키보드 입력, 프로세스 상태 변화, 시스템 호출 등의 이벤트로 인해 시그널이 생성된다. - 시그널 대기
생성된 시그널은 프로세스의 시그널 비트 벡터에 추가된다. 비트 벡터는 각 시그널의 상태를 나타내며, 동일한 시그널이 여러 번 발생해도 한 번만 처리된다. - 시그널 전달
운영체제는 프로세스가 실행 가능한 상태가 될 때 시그널 핸들러를 호출하거나, 기본 동작을 수행하도록 한다.
시그널 핸들러
시그널 핸들러는 프로세스가 특정 시그널을 처리하기 위해 호출하는 사용자 정의 함수이다. 시그널 핸들러는 시그널이 발생했을 때 운영체제가 자동으로 실행하며, 시그널 번호를 매개변수로 전달받아 동작을 정의할 수 있다. 핸들러는 다음과 같은 방식으로 동작한다:
- 등록 및 호출:
- System V에서는 signal() 함수로 핸들러를 등록하며, POSIX에서는 sigaction()을 통해 더 안전하고 세밀한 설정이 가능하다.
- 핸들러는 시그널이 발생하면 즉시 실행된다. 예를 들어, SIGINT 시그널이 발생하면 핸들러는 이를 처리하고 기본 동작(프로세스 종료)을 대체할 수 있다.
- 비동기 동작:
- 핸들러는 실행 중인 프로그램의 흐름과 독립적으로 호출된다. 이는 프로세스가 다른 작업을 수행하는 동안에도 시그널을 처리할 수 있음을 의미한다.
- 제한사항:
- 핸들러 내에서 호출되는 함수는 반드시 Async-Signal-Safe이어야 한다. 예를 들어, printf()는 안전하지 않을 수 있으므로 대신 write()를 사용하는 것이 권장된다.
- SIGKILL과 SIGSTOP 시그널에는 핸들러를 등록할 수 없다. 이는 시스템 안정성을 보장하기 위한 제한이다.
- 재진입 문제:
- 핸들러가 실행되는 동안 동일한 시그널이 다시 발생할 수 있다. 이를 방지하려면 POSIX 표준의 sa_mask를 사용해 특정 시그널을 블록해야 한다.
- 실제 사용 예:
- 자원 정리: 파일 닫기, 메모리 해제 등.
- 사용자 피드백 제공: 종료 알림 메시지 출력.
System V 시그널 프로그래밍
System V는 signal() 함수를 사용하여 시그널 핸들러를 등록한다. 아래는 SIGINT 시그널에 대한 핸들러 등록 예제이다:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
// SIGINT 핸들러
void handle_sigint(int sig) {
printf("Caught signal %d (SIGINT). Exiting gracefully...\n", sig);
exit(0);
}
int main() {
// SIGINT 핸들러 등록
signal(SIGINT, handle_sigint);
printf("Press Ctrl+C to exit.\n");
while (1) {
printf("Running...\n");
sleep(1);
}
return 0;
}
주요 개념
- signal()
- signal(int signum, void (*handler)(int)): 특정 시그널에 대한 핸들러를 설정한다.
- 위 예제에서는 SIGINT 시그널에 대해 handle_sigint 핸들러를 등록한다.
- SIGINT
- Ctrl+C 입력으로 발생하며, 핸들러에서 이를 처리한다.
- 한계
System V 방식은 비재진입성을 보장하지 않으며, 다중 스레드 환경에서 안전하지 않을 수 있다.
POSIX 시그널 프로그래밍
POSIX 표준은 sigaction() 함수를 사용하여 시그널을 처리하며, System V보다 더 안전하고 유연한 방식으로 동작한다.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
// SIGINT 핸들러
void handle_sigint(int sig) {
printf("Caught SIGINT: %d. Cleaning up and exiting...\n", sig);
exit(0);
}
int main() {
struct sigaction sa;
// 핸들러 함수 설정
sa.sa_handler = handle_sigint;
sa.sa_flags = 0; // 추가 옵션 비활성화
sigemptyset(&sa.sa_mask); // 블록할 시그널 없음
// SIGINT에 대한 핸들러 등록
sigaction(SIGINT, &sa, NULL);
printf("Running... Press Ctrl+C to exit.\n");
while (1) {
printf("Waiting for SIGINT...\n");
sleep(2);
}
return 0;
}
POSIX 코드의 주요 개념
- sigaction 구조체
- sa_handler: 시그널 핸들러 함수 포인터.
- sa_mask: 시그널 처리 중 블록할 추가 시그널 집합.
- sa_flags: 핸들링 동작을 제어하는 플래그.
- 유연성과 안전성
- sigaction()은 핸들러 설정 시 시그널 블록 및 재진입 방지 설정이 가능하다.
- System V 방식보다 더 안전하며, 다중 스레드 환경에서 권장된다.
- 추가 설정
- sa_flags에 SA_RESTART를 설정하면, 시그널 발생 후 차단된 시스템 호출이 자동 재시도된다.
시그널 프로그래밍 관련 함수
kill()
- kill(pid, signo)는 특정 프로세스나 프로세스 그룹에 시그널을 보낸다.
- PID에 따라 다음과 같이 동작한다:
- pid > 0: 해당 프로세스에 시그널을 보낸다.
- pid == 0: 같은 프로세스 그룹에 시그널을 보낸다.
- pid < 0: 절대값이 프로세스 그룹 ID인 모든 프로세스에 시그널을 보낸다.
raise()
- raise(signo)는 자기 자신에게 시그널을 보낸다.
alarm()
- alarm(seconds)는 지정된 초 후에 SIGALRM 시그널을 보낸다.
- 타이머가 만료되면 프로세스가 기본적으로 종료된다.
pause()
- pause()는 시그널이 발생할 때까지 프로세스를 대기 상태로 둔다.
결론
시그널은 프로세스 간 비동기적 소통을 가능하게 하는 중요한 메커니즘이다. System V의 signal() 함수는 간단한 구현에 적합하지만, POSIX 표준의 sigaction()은 더 안전하고 유연한 기능을 제공한다. 시그널의 동작 원리를 이해하고 적절히 활용하면, 안정적이고 효율적인 소프트웨어를 개발할 수 있다.
반응형
'CS 지식 > 시스템 프로그래밍' 카테고리의 다른 글
Assembly(어쌤블리어)의 기초 (0) | 2024.10.18 |
---|---|
[C언어] Signal 프로그래밍에서의 Race Condition과 해결법 (0) | 2024.10.17 |
[c언어] 멀티 프로세스 프로그래밍 (0) | 2024.10.17 |
댓글