시프 17-signals

강준호·2024년 6월 2일

시스템프로그래밍

목록 보기
11/18

POSIX 신호

POSIX 신호 개요

  • POSIX 신호는 또 다른 형태의 프로세스 간 통신입니다.
  • 프로그램 내에서 동시성을 생성하는 방법이기도 합니다.
  • 이러한 이유로 POSIX 신호는 복잡하고 미묘합니다.

신호의 메시지로서의 역할

비동기 메시지

  • POSIX 신호는 비동기 메시지입니다.
  • 비동기란 신호 수신이 언제든지 발생할 수 있음을 의미합니다.
  • 각 신호는 작은 정수로 된 번호를 가지고 있습니다.
  • POSIX 신호는 그 외의 데이터를 포함하지 않습니다.

신호 유형

두 가지 기본 유형

  • 신뢰할 수 있는 신호
  • 실시간 신호

실시간 신호는 더 복잡하며 데이터를 전달할 수 있습니다. 이번 강의에서는 신뢰할 수 있는 신호만 다룹니다.

비동기 수신

애플리케이션 관점

  • 신호는 차단(block)되거나 무시(ignored)될 수 있습니다.
  • 활성화된 신호는 두 프로세서 명령 사이에서 수신될 수 있습니다.
  • 수신된 신호는 사용자 정의 함수인 신호 핸들러를 실행할 수 있습니다.
  • 이로 인해 활성화된 신호와 프로그램 코드는 공유 또는 전역 데이터를 매우 신중하게 조작해야 합니다.

신호 목록

POSIX 정의 신호

  • SIGHUP, 1 (터미널이 연결이 끊어졌을 때)
  • SIGINT, 2 (Ctrl-C를 눌렀을 때)
  • SIGKILL, 9 (캐치할 수 없으며, 프로세스를 종료)
  • SIGSEGV, 11 (잘못된 메모리 접근 시)
  • SIGCHLD, 17 (자식 프로세스가 종료될 때)

신호 핸들러

신호 핸들러 설치

  • 프로세스는 신호 핸들러를 설치하여 신호를 수신하고자 하는 의사를 표시합니다.
  • 각 신호에는 기본 핸들러가 있습니다:
    • 신호 무시
    • 프로세스 중지
    • 프로세스 계속
    • 프로세스 종료
    • 프로세스 종료 및 코어 덤프

신호 핸들러 함수

사용자 정의 신호 핸들러

  • 프로세스는 SIGKILL 및 SIGSTOP을 제외한 모든 신호에 대해 신호 핸들러를 설치할 수 있습니다.
  • 신호 핸들러는 함수로, 신호가 수신될 때 호출됩니다.
  • 신호 핸들러는 sighandler_t 타입입니다:
typedef void (*sighandler_t)(int);

핸들러 설치

신호 함수 사용

sighandler_t signal(int signum, sighandler_t handler);
  • signal 함수는 신호 번호와 핸들러 함수를 받아, 해당 함수를 신호에 바인딩합니다.
  • 이후 신호를 수신하면 바인딩된 함수가 호출됩니다.
  • 이 함수는 이전 신호 핸들러를 반환합니다.

특수 핸들러

특수 신호 핸들러

  • SIG_IGN: 신호 무시
  • SIG_DFL: 기본 동작 복원
signal(SIGCHLD, SIG_DFL);

신호의 이식성 문제

signal() 함수의 이식성 문제

  • 일부 시스템에서는 신호 수신 시 핸들러를 SIG_DFL로 재설정합니다.
  • 일부 시스템에서는 신호가 signal()로 설정된 핸들러 동안 도착할 수 있습니다.

sigaction() 함수 사용

  • 이러한 이유로, POSIX는 더 명확한 동작을 가진 sigaction() 함수를 제공합니다.

신호 수신

커널의 신호 전달

  • 커널은 언제든지 신호를 전달할 수 있습니다.
  • 신호를 수신하면 프로세스는:
    • 현재 프로그램 카운터를 스택에 푸시
    • 신호 핸들러로 점프
    • 신호 핸들러 실행
    • 저장된 PC를 팝하고 반환

신호 차단

신호 차단 방법

  • 프로그램은 신호를 차단할 수 있습니다.
  • 차단된 신호는 차단 해제 시 전달됩니다.
  • 신호는 다음과 같이 차단될 수 있습니다:
    • 핸들러가 현재 실행 중인 경우 암묵적으로 차단
    • 프로그래머가 sigprocmask()를 사용하여 명시적으로 차단

sigprocmask() 함수 사용

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigemptyset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
  • sigprocmask()는 신호 집합으로 주어진 신호를 차단하거나 차단 해제합니다.

sigprocmask() 예시

sigset_t mask, oldmask;

sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &oldmask);

// 여기서 SIGCHLD가 차단됨

sigprocmask(SIG_SETMASK, &oldmask, NULL);

// SIGCHLD가 차단 해제되어 이전 상태로 복원
// 차단 해제되고 대기 중이면 핸들러가 지금 실행됨

공유 데이터

신호와 공유 데이터

  • 신호는 프로그램에 동시성을 도입합니다.
  • 신호 핸들러는 예측할 수 없는 시점에 실행되므로, 공유 데이터를 액세스하는 것은 위험합니다.
  • 데이터가 불완전한 상태에서 핸들러가 액세스하면 손상 또는 프로그램 오류가 발생할 수 있습니다.

신호의 동시성 문제

동시성 문제 예시

  • 신호 핸들러가 실행되는 동안 데이터가 변경되면 데이터 손상 가능성이 있습니다.

신호 전송

신호 전송 방법

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
  • 신호는 kill() 함수를 사용하여 프로세스에 전송됩니다.
  • kill() 함수는 수신 프로세스를 실제로 종료시키지 않을 수도 있습니다.
  • 프로세스는 일반적으로 자신 또는 동일한 사용자가 소유한 다른 프로세스만 종료할 수 있습니다.

요약

  • 신호는 프로세스 간 통신입니다.
  • 각 신호는 메시지입니다.
  • 신호는 함수에 의해 처리됩니다.
  • 신호 핸들러는 동시성을 도입합니다.
  • 신호를 사용할 때는 공유 데이터를 신중하게 조작해야 합니다.

0개의 댓글