C 언어에서 스레드별 시그널 마스크 설정 방법

C 언어에서 멀티스레드 환경은 효율적인 병렬 처리를 가능하게 하지만, 스레드 간 시그널 처리 충돌 문제가 발생할 수 있습니다. 이러한 문제를 해결하기 위해 스레드별로 시그널 마스크를 설정하는 방법은 매우 중요합니다. 본 기사에서는 시그널과 시그널 마스크의 기본 개념부터 스레드별 시그널 관리 기술, 실용적인 예제 및 디버깅 방법까지 자세히 알아봅니다. 이를 통해 멀티스레드 환경에서 안정적이고 효율적인 프로그램을 작성하는 방법을 배울 수 있습니다.

목차

시그널과 시그널 마스크란?

시그널의 기본 개념


시그널(signal)은 유닉스 기반 시스템에서 프로세스 간 통신 및 비동기 이벤트 처리를 위해 사용되는 메커니즘입니다. 시그널은 특정 이벤트(예: 종료 요청, 알람, 디버깅 요청)가 발생했을 때 커널이 프로세스에 알리는 역할을 합니다. 대표적인 시그널로는 SIGINT(Ctrl+C), SIGTERM(종료 요청), SIGKILL(강제 종료) 등이 있습니다.

시그널 마스크의 정의


시그널 마스크는 프로세스 또는 스레드가 수신할 시그널을 제어하는 데 사용됩니다. 마스크에 등록된 시그널은 블록되어 처리되지 않으며, 해당 시그널이 도착하면 큐에 보관됩니다. 이 기능은 프로세스나 스레드가 중요한 작업을 수행하는 동안 특정 시그널로 인해 방해받지 않도록 하는 데 유용합니다.

시그널 마스크의 주요 역할

  1. 시그널 처리 제어: 특정 시그널의 처리를 임시로 중단하거나 다시 활성화할 수 있습니다.
  2. 동기화 지원: 멀티스레드 환경에서 시그널 처리 순서를 명확히 하여 예상치 못한 동작을 방지합니다.
  3. 시스템 안정성 보장: 중요한 코드 실행 중에 시그널로 인한 프로그램 중단을 방지합니다.

시그널 마스크를 올바르게 활용하면 비동기 이벤트로 인한 오류를 예방하고 프로그램의 안정성을 크게 향상시킬 수 있습니다.

스레드별 시그널 마스크의 필요성

멀티스레드 환경에서의 시그널 처리 문제


멀티스레드 프로그램에서 시그널은 기본적으로 프로세스 단위로 전달됩니다. 이는 모든 스레드가 동일한 시그널 처리 설정을 공유한다는 것을 의미합니다. 따라서, 한 스레드가 특정 시그널을 처리하려고 할 때 다른 스레드에서 예상치 못한 동작이나 충돌이 발생할 수 있습니다.

예를 들어, 중요한 데이터 구조를 수정하는 스레드가 SIGINT를 처리하려는 시그널 핸들러로 인해 중단된다면 데이터가 손상될 가능성이 있습니다.

스레드별 시그널 마스크의 역할


스레드별 시그널 마스크는 각 스레드가 고유의 시그널 처리 정책을 정의할 수 있게 하여 이러한 문제를 해결합니다. 이를 통해 다음과 같은 이점이 있습니다:

  1. 독립성 확보: 각 스레드가 고유한 시그널 처리 전략을 가질 수 있습니다.
  2. 충돌 방지: 시그널 처리 중 스레드 간의 충돌이나 예기치 않은 동작을 방지합니다.
  3. 작업 안정성 향상: 중요한 작업 중인 스레드가 시그널로 방해받지 않도록 보호할 수 있습니다.

적용 시나리오

  • 백그라운드 작업 관리: 하나의 스레드에서만 특정 시그널(SIGUSR1, SIGUSR2)을 처리하여 이벤트 관리.
  • 실시간 데이터 처리: 데이터 스트림을 처리하는 동안 다른 스레드의 방해를 방지.
  • 크리티컬 섹션 보호: 중요한 데이터 변경 작업 중 시그널 처리로 인해 데이터가 손상되지 않도록 보호.

멀티스레드 환경에서 스레드별 시그널 마스크를 올바르게 설정하면 프로그램의 신뢰성과 안정성을 대폭 향상시킬 수 있습니다.

`pthread_sigmask` 함수의 기본 사용법

`pthread_sigmask` 함수란?


pthread_sigmask는 POSIX 스레드 라이브러리에서 제공하는 함수로, 특정 스레드에 대한 시그널 마스크를 설정하거나 수정하는 데 사용됩니다. 이를 통해 각 스레드가 독립적으로 시그널 처리를 관리할 수 있습니다.

함수 시그니처

#include <pthread.h>
#include <signal.h>

int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
  • how: 시그널 마스크를 설정하는 방식 (SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK).
  • SIG_BLOCK: 기존 마스크에 새로운 시그널을 추가해 블록.
  • SIG_UNBLOCK: 지정된 시그널을 마스크에서 제거.
  • SIG_SETMASK: 기존 마스크를 새로운 시그널 세트로 대체.
  • set: 적용할 시그널 세트.
  • oldset: 기존의 시그널 마스크를 저장할 위치(옵션, NULL 가능).

기본 사용 방법

  1. 시그널 세트 초기화
    sigset_t 구조체를 사용하여 시그널 세트를 정의하고 초기화합니다.
sigset_t set;
sigemptyset(&set);              // 시그널 세트 초기화
sigaddset(&set, SIGINT);        // SIGINT 추가
  1. 시그널 마스크 설정
    pthread_sigmask를 호출하여 설정된 시그널 세트를 적용합니다.
if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
    perror("pthread_sigmask failed");
}
  1. 기존 시그널 마스크 저장
    현재 마스크 상태를 저장하여 나중에 복원할 수 있습니다.
sigset_t oldset;
if (pthread_sigmask(SIG_BLOCK, &set, &oldset) != 0) {
    perror("pthread_sigmask failed");
}

간단한 예제

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

void* thread_func(void* arg) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGINT);

    if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
        perror("pthread_sigmask failed");
        return NULL;
    }

    printf("SIGINT is blocked in this thread.\n");
    sleep(10); // 시그널 테스트를 위해 대기
    printf("Thread execution completed.\n");

    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_func, NULL);
    pthread_join(thread, NULL);
    return 0;
}

결과


이 프로그램은 새로운 스레드에서 SIGINT를 차단합니다. 따라서 프로그램 실행 중 해당 스레드가 활성 상태일 때 Ctrl+C를 눌러도 해당 스레드에는 영향을 미치지 않습니다.

pthread_sigmask는 멀티스레드 환경에서 시그널 관리의 핵심 도구로, 안전하고 안정적인 동작을 보장합니다.

스레드와 시그널 간 충돌 해결

멀티스레드 환경에서의 시그널 충돌 문제


멀티스레드 프로그램에서는 시그널이 모든 스레드에 전달되거나 특정 스레드가 처리하도록 설정할 수 있습니다. 그러나 기본 설정에서는 다음과 같은 충돌 문제가 발생할 수 있습니다:

  1. 시그널 경쟁
    여러 스레드가 동일한 시그널을 처리하려고 시도하여 예기치 않은 동작이 발생할 수 있습니다.
  2. 중요 작업 방해
    스레드가 중요한 작업(예: 크리티컬 섹션 실행) 중 시그널 처리로 인해 중단될 가능성이 있습니다.
  3. 디버깅 복잡성
    여러 스레드가 동시에 시그널을 처리하면 문제의 원인을 추적하기 어렵습니다.

충돌 해결 전략

1. 특정 스레드에서만 시그널 처리


하나의 스레드에서만 시그널을 처리하도록 설정하는 것이 일반적인 해결책입니다. 이를 위해 메인 스레드가 시그널을 처리하고 다른 스레드는 시그널을 차단합니다.

sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);

// 모든 스레드에서 시그널 차단
pthread_sigmask(SIG_BLOCK, &set, NULL);

// 특정 스레드에서만 시그널 처리
while (1) {
    int sig;
    sigwait(&set, &sig);  // SIGINT 시그널 처리 대기
    printf("Received signal: %d\n", sig);
}

2. 크리티컬 섹션 보호


중요한 작업 중 시그널 처리를 일시적으로 차단하여 작업의 연속성을 보장합니다.

sigset_t set, oldset;
sigemptyset(&set);
sigaddset(&set, SIGINT);

// 시그널 차단
pthread_sigmask(SIG_BLOCK, &set, &oldset);

// 크리티컬 섹션
printf("Critical section in progress...\n");

// 시그널 복원
pthread_sigmask(SIG_SETMASK, &oldset, NULL);

3. 스레드별 역할 분리


스레드별로 시그널 처리 역할을 분리하여 충돌 가능성을 줄입니다. 예를 들어, 하나의 스레드는 사용자 입력을 처리하고 다른 스레드는 시그널만 처리합니다.

유용한 함수

  • sigwait: 특정 시그널을 대기하고 처리합니다.
  • pthread_sigmask: 스레드별 시그널 마스크 설정에 사용됩니다.
  • sigaction: 시그널 핸들러를 등록하거나 수정합니다.

적용 예제

void* signal_handler_thread(void* arg) {
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);

    while (1) {
        int sig;
        sigwait(&set, &sig);
        printf("Thread received signal: %d\n", sig);
    }
    return NULL;
}

int main() {
    pthread_t thread;
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);

    // 모든 스레드에서 시그널 차단
    pthread_sigmask(SIG_BLOCK, &set, NULL);

    // 시그널 처리 스레드 생성
    pthread_create(&thread, NULL, signal_handler_thread, NULL);

    // 시그널 발생 테스트
    sleep(1);
    pthread_kill(thread, SIGUSR1);

    pthread_join(thread, NULL);
    return 0;
}

결론


시그널과 스레드 간의 충돌 문제를 방지하려면 적절한 시그널 마스크 설정과 스레드 역할 분리가 필수적입니다. 이를 통해 멀티스레드 프로그램의 안정성과 성능을 향상시킬 수 있습니다.

실습: 간단한 시그널 마스크 설정 예제

예제 설명


이 예제에서는 pthread_sigmask를 사용해 특정 스레드에서만 SIGINT(Ctrl+C)를 처리하도록 설정합니다. 이를 통해 멀티스레드 환경에서 시그널 마스크가 어떻게 동작하는지 이해할 수 있습니다.

코드 예제

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

// 시그널 처리 함수
void* signal_handler_thread(void* arg) {
    sigset_t set;
    int sig;

    // 시그널 세트 초기화 및 SIGINT 추가
    sigemptyset(&set);
    sigaddset(&set, SIGINT);

    printf("Signal handler thread waiting for SIGINT...\n");

    // 시그널 대기
    while (1) {
        sigwait(&set, &sig);
        if (sig == SIGINT) {
            printf("Signal handler thread received SIGINT!\n");
        }
    }
    return NULL;
}

// 일반 작업 스레드
void* worker_thread(void* arg) {
    printf("Worker thread running...\n");
    sleep(10); // 작업 시뮬레이션
    printf("Worker thread completed.\n");
    return NULL;
}

int main() {
    pthread_t signal_thread, worker_thread_id;
    sigset_t set;

    // 모든 스레드에서 SIGINT 차단
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    pthread_sigmask(SIG_BLOCK, &set, NULL);

    // 시그널 핸들러 스레드 생성
    pthread_create(&signal_thread, NULL, signal_handler_thread, NULL);

    // 일반 작업 스레드 생성
    pthread_create(&worker_thread_id, NULL, worker_thread, NULL);

    // 스레드 종료 대기
    pthread_join(signal_thread, NULL);
    pthread_join(worker_thread_id, NULL);

    return 0;
}

코드 해설

  1. sigset_t 초기화 및 시그널 추가
  • sigemptysetsigaddset을 사용해 SIGINT를 포함한 시그널 세트를 만듭니다.
  • 메인 함수에서 모든 스레드에 SIGINT를 차단합니다.
  1. pthread_sigmask로 시그널 차단
  • 메인 스레드에서 시그널을 차단하면, 모든 새로 생성된 스레드도 동일한 마스크를 상속받습니다.
  1. 시그널 핸들링 스레드
  • sigwait를 사용해 SIGINT 시그널을 대기하고 처리합니다.
  • 시그널은 시그널 핸들링 스레드에서만 처리되므로, 다른 스레드 작업에 방해가 되지 않습니다.
  1. 작업 스레드
  • 일반 스레드는 시그널과 독립적으로 동작하며 SIGINT를 차단한 상태로 작업을 수행합니다.

실행 결과

  1. 프로그램이 실행되면 “Signal handler thread waiting for SIGINT…”와 “Worker thread running…” 메시지가 표시됩니다.
  2. Ctrl+C(SIGINT)를 입력하면 시그널 핸들러 스레드에서 “Signal handler thread received SIGINT!” 메시지가 출력됩니다.
  3. 일반 작업 스레드는 SIGINT에 영향을 받지 않고 작업을 완료합니다.

결론


이 예제는 멀티스레드 환경에서 시그널 마스크를 설정하고 특정 스레드에서만 시그널을 처리하는 방법을 보여줍니다. 이를 통해 멀티스레드 프로그램의 안정성을 높이고 시그널 처리의 제어를 강화할 수 있습니다.

고급 사용: 동적 시그널 할당

동적 시그널 할당의 필요성


일부 애플리케이션에서는 시그널을 정적으로 정의하는 대신 동적으로 관리해야 할 필요가 있습니다. 예를 들어, 여러 스레드가 동적으로 생성되고, 각 스레드가 특정 이벤트에 대한 시그널을 처리해야 하는 경우 동적 할당이 유용합니다.

동적 시그널 할당은 다음과 같은 이점을 제공합니다:

  1. 유연성: 프로그램 실행 중 시그널 세트를 동적으로 변경 가능.
  2. 확장성: 스레드별 고유 시그널 처리 설정으로 확장된 애플리케이션 지원.
  3. 리소스 관리: 필요에 따라 시그널을 추가 및 제거하여 리소스를 효율적으로 사용.

동적 시그널 세트 생성


sigset_t 구조체를 사용해 동적으로 시그널 세트를 관리합니다.

sigset_t dynamic_set;
sigemptyset(&dynamic_set);          // 시그널 세트 초기화
sigaddset(&dynamic_set, SIGUSR1);   // SIGUSR1 추가
sigaddset(&dynamic_set, SIGUSR2);   // SIGUSR2 추가

시그널 세트를 동적으로 업데이트하려면, sigaddsetsigdelset을 사용해 시그널을 추가하거나 제거합니다.

스레드별 동적 시그널 관리


각 스레드가 특정 시그널을 동적으로 처리하도록 설정합니다.

void* dynamic_signal_handler(void* arg) {
    sigset_t* set = (sigset_t*)arg;
    int sig;

    while (1) {
        sigwait(set, &sig);
        printf("Thread handled signal: %d\n", sig);
    }
    return NULL;
}

전체 코드 예제


다음 예제는 동적으로 생성된 스레드별로 시그널을 할당하고 처리하는 방법을 보여줍니다.

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

void* dynamic_signal_handler(void* arg) {
    sigset_t* set = (sigset_t*)arg;
    int sig;

    printf("Dynamic signal handler thread ready.\n");

    while (1) {
        sigwait(set, &sig);
        printf("Thread handled signal: %d\n", sig);
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    sigset_t set1, set2;

    // 스레드 1용 시그널 세트
    sigemptyset(&set1);
    sigaddset(&set1, SIGUSR1);

    // 스레드 2용 시그널 세트
    sigemptyset(&set2);
    sigaddset(&set2, SIGUSR2);

    // 모든 스레드에서 기본 시그널 차단
    pthread_sigmask(SIG_BLOCK, &set1, NULL);
    pthread_sigmask(SIG_BLOCK, &set2, NULL);

    // 스레드 생성 및 동적 시그널 세트 전달
    pthread_create(&thread1, NULL, dynamic_signal_handler, &set1);
    pthread_create(&thread2, NULL, dynamic_signal_handler, &set2);

    // 시그널 전송 테스트
    sleep(1);
    pthread_kill(thread1, SIGUSR1);
    pthread_kill(thread2, SIGUSR2);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}

코드 설명

  1. 시그널 세트 생성: 두 개의 sigset_t 구조체를 만들어 각각 SIGUSR1SIGUSR2를 추가합니다.
  2. 스레드별 시그널 설정: 각 스레드가 특정 시그널 세트를 처리하도록 설정합니다.
  3. 동적 시그널 처리: sigwait를 사용해 스레드에서 해당 시그널을 대기하고 처리합니다.

실행 결과

  • 스레드 1은 SIGUSR1만 처리하고, 스레드 2는 SIGUSR2만 처리합니다.
  • 시그널이 각각 해당 스레드로 전달되면, 처리 결과가 출력됩니다.

결론


동적 시그널 할당은 복잡한 멀티스레드 애플리케이션에서 각 스레드의 역할을 유연하게 정의하고 관리할 수 있는 강력한 도구입니다. 이를 활용하면 프로그램의 확장성과 안정성을 더욱 높일 수 있습니다.

시그널 마스크와 디버깅

시그널 관련 문제의 주요 원인


멀티스레드 환경에서 시그널 처리가 예상대로 동작하지 않을 경우 다음과 같은 문제가 발생할 수 있습니다:

  1. 시그널 블록 상태: 특정 시그널이 의도치 않게 마스크되어 수신되지 않음.
  2. 스레드 간 시그널 전달 오류: 잘못된 스레드가 시그널을 처리하거나 시그널이 유실됨.
  3. 핸들러 충돌: 여러 스레드가 동일한 시그널 핸들러를 호출하여 예상치 못한 동작 발생.

디버깅 도구와 기술


시그널 관련 문제를 효과적으로 디버깅하려면 다음 도구와 기술을 활용할 수 있습니다:

1. `strace`를 사용한 시그널 추적


strace는 리눅스 환경에서 시스템 호출 및 시그널 전달을 추적할 수 있는 도구입니다.

strace -e signal ./your_program
  • 발생한 시그널과 전달 여부를 확인할 수 있습니다.
  • 시그널이 잘못된 스레드에 전달되었는지 확인합니다.

2. 시그널 마스크 상태 확인


시그널 마스크를 디버깅하려면 현재 마스크 상태를 확인할 필요가 있습니다. 이를 위해 다음과 같은 코드를 삽입할 수 있습니다:

void print_signal_mask() {
    sigset_t current_mask;
    sigprocmask(SIG_BLOCK, NULL, &current_mask);

    for (int i = 1; i < NSIG; i++) {
        if (sigismember(&current_mask, i)) {
            printf("Signal %d is blocked.\n", i);
        }
    }
}
  • 이 함수는 현재 블록된 시그널 목록을 출력합니다.
  • 문제 발생 시 특정 시그널이 마스크되어 있는지 확인합니다.

3. 로그를 통한 시그널 흐름 추적


프로그램에 디버깅 로그를 추가하여 시그널 처리 흐름을 추적할 수 있습니다.

void signal_handler(int sig) {
    printf("Signal %d received in thread %ld\n", sig, pthread_self());
}
  • 각 스레드에서 시그널 수신 여부를 기록합니다.
  • 로그를 통해 잘못된 시그널 전달 경로를 확인합니다.

예제: 디버깅 시그널 블로킹 문제

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

void print_signal_mask() {
    sigset_t current_mask;
    sigprocmask(SIG_BLOCK, NULL, &current_mask);

    printf("Current signal mask in thread %ld:\n", pthread_self());
    for (int i = 1; i < NSIG; i++) {
        if (sigismember(&current_mask, i)) {
            printf("  Signal %d is blocked.\n", i);
        }
    }
}

void* thread_func(void* arg) {
    printf("Thread %ld started.\n", pthread_self());
    print_signal_mask();

    sleep(5);  // 작업 시뮬레이션
    return NULL;
}

int main() {
    pthread_t thread;
    sigset_t set;

    // SIGINT 블록
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    pthread_sigmask(SIG_BLOCK, &set, NULL);

    // 스레드 생성
    pthread_create(&thread, NULL, thread_func, NULL);

    // 메인 스레드 시그널 마스크 출력
    printf("Main thread signal mask:\n");
    print_signal_mask();

    pthread_join(thread, NULL);
    return 0;
}

출력

  • 각 스레드의 시그널 마스크 상태를 확인합니다.
  • SIGINT가 올바르게 차단되었는지 확인합니다.

결론


시그널 처리 문제는 디버깅이 어렵지만, strace와 같은 도구 및 시그널 마스크 상태 출력 등의 방법을 활용하면 문제를 빠르게 파악하고 해결할 수 있습니다. 이러한 디버깅 기술은 멀티스레드 환경에서 안정적인 시그널 처리를 보장하는 데 필수적입니다.

응용 예제와 연습 문제

응용 예제: 스레드별 시그널 기반 작업 관리


다음 예제는 여러 스레드가 동적으로 생성되고 각 스레드가 고유한 시그널을 처리하는 애플리케이션을 보여줍니다. 이를 통해 시그널 마스크를 활용한 작업 분리를 학습할 수 있습니다.

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

// 스레드별 시그널 처리 함수
void* signal_handler_thread(void* arg) {
    sigset_t* set = (sigset_t*)arg;
    int sig;

    printf("Thread %ld waiting for signals...\n", pthread_self());
    while (1) {
        sigwait(set, &sig);
        printf("Thread %ld received signal: %d\n", pthread_self(), sig);
    }
    return NULL;
}

int main() {
    pthread_t threads[3];
    sigset_t sets[3];

    // 시그널 세트 초기화
    for (int i = 0; i < 3; i++) {
        sigemptyset(&sets[i]);
        sigaddset(&sets[i], SIGUSR1 + i);  // SIGUSR1, SIGUSR2, SIGUSR3 사용
    }

    // 모든 스레드에서 기본 시그널 차단
    for (int i = 0; i < 3; i++) {
        pthread_sigmask(SIG_BLOCK, &sets[i], NULL);
    }

    // 스레드 생성
    for (int i = 0; i < 3; i++) {
        pthread_create(&threads[i], NULL, signal_handler_thread, &sets[i]);
    }

    // 시그널 전송 테스트
    sleep(1);
    for (int i = 0; i < 3; i++) {
        pthread_kill(threads[i], SIGUSR1 + i);
    }

    // 스레드 종료 대기
    for (int i = 0; i < 3; i++) {
        pthread_join(threads[i], NULL);
    }

    return 0;
}

코드 설명

  1. 시그널 세트 생성: 세 개의 스레드가 각각 SIGUSR1, SIGUSR2, SIGUSR3을 처리하도록 설정합니다.
  2. 시그널 마스크 적용: 각 스레드에 고유한 시그널 마스크를 설정하여 분리된 시그널 처리가 가능합니다.
  3. 시그널 테스트: pthread_kill을 사용해 각 스레드로 시그널을 보냅니다.

실행 결과

  • 각 스레드는 자신에게 할당된 시그널을 처리하고 로그를 출력합니다.
  • 예:
  Thread 123456 waiting for signals...
  Thread 123457 waiting for signals...
  Thread 123458 waiting for signals...
  Thread 123456 received signal: 10
  Thread 123457 received signal: 11
  Thread 123458 received signal: 12

연습 문제

  1. 문제 1: 여러 시그널 처리
    각 스레드가 두 개 이상의 시그널을 처리하도록 코드를 수정하고, 모든 시그널이 제대로 처리되었는지 확인하세요.
  2. 문제 2: 동적 스레드 생성
    사용자 입력에 따라 동적으로 스레드를 생성하고, 각각의 스레드가 다른 시그널을 처리하도록 설정하세요.
  3. 문제 3: 크리티컬 섹션 보호
    크리티컬 섹션 내에서 시그널 처리가 이루어지지 않도록 마스크를 설정하고, 작업 후 복원하도록 구현하세요.
  4. 문제 4: 시그널 마스크 디버깅
    print_signal_mask 함수를 확장하여 각 스레드의 시그널 마스크 상태를 주기적으로 출력하도록 구현하세요.

결론


위의 응용 예제와 연습 문제를 통해 시그널 마스크 설정과 활용에 대한 실질적인 경험을 쌓을 수 있습니다. 이러한 연습은 멀티스레드 애플리케이션 개발 및 디버깅 기술을 향상시키는 데 도움을 줄 것입니다.

목차