signal(), fork(), wait()

박선하·2023년 11월 19일
#include <stdio.h>
#include <signal.h>
#include <string.h>

#define INPUTLEN 100

void inthandler(int s) {
    printf("Received siganl %d .. waiting\n", s);
    sleep(2);
    printf("Leaving inthandler\n");
}

void quithandler(int s) {
    printf("Received siganl %d .. waiting\n", s);
    sleep(3);
    printf("Leaving quithandler\n");
}

int main(int ac, char *av[]) {
    void inthandler(int);
    void quithandler(int);
    char input[INPUTLEN];
    int nchars;

    signal(SIGINT, inthandler);
    signal(SIGQUIT, quithandler);

    do {
        printf("\nType a message\n");
        nchars = read(0, input, (INPUTLEN-1)); //read(fd, buf, size);
        if (nchars == -1)
            perror("read returned an error");
        else {
            input[nchars] = '\0'; // 맨 마지막에 널문자
            printf("You typed: %s", input);
        }
    }
        while (strncmp(input, "quit", 4) != 0);
}

inthandler: 이 함수는 SIGINT 시그널을 처리하기 위해 등록되어 있습니다. 일반적으로 사용자가 터미널에서 Ctrl+C를 누를 때 생성됩니다.
quithandler: 이 함수는 SIGQUIT 시그널을 처리하기 위해 등록되어 있습니다. 일반적으로 사용자가 터미널에서 Ctrl+\를 누를 때 생성됩니다.

실행결과

SIGINTSIGQUIT은 모두 사용자에 의해 터미널에서 발생시키는 시그널이지만 몇 가지 중요한 차이가 있습니다.

  1. 원래 목적:

    • SIGINT (Interrupt)은 사용자가 프로그램에게 정상적으로 종료하라고 요청하는 시그널입니다. 주로 Ctrl+C를 사용하여 프로세스를 중지하는 데 사용됩니다.
    • SIGQUIT (Quit)은 사용자가 프로그램에게 종료하라는 요청을 보내지만, SIGINT와 달리 해당 프로세스가 코어 덤프를 생성하도록 요청합니다. 주로 Ctrl+\를 사용하여 프로세스를 중지하는 데 사용됩니다.
  2. 동작:

    • SIGINT를 받은 프로세스는 종료되며, 종료 전에 시그널 핸들러를 실행할 수 있습니다. 일반적으로는 정상적인 종료를 수행하도록 프로그램이 설계되어 있습니다.
    • SIGQUIT을 받은 프로세스는 종료되고, 종료 전에 SIGQUIT 핸들러를 실행하고 코어 덤프를 생성합니다. 이는 프로그램이 비정상 종료되었거나 디버깅이 필요한 경우 유용합니다.
  3. 코어 덤프 생성:

    • SIGINT는 기본적으로 코어 덤프를 생성하지 않습니다. 프로그램을 종료하면 종료 상태만 반환됩니다.
    • SIGQUIT은 종료 전에 코어 덤프를 생성하도록 요청합니다. 이는 프로그램이 종료되기 전에 메모리 상태 및 실행 상태를 파일로 저장하여 디버깅에 사용할 수 있도록 합니다.
  4. 키 조합:

    • SIGINT는 Ctrl+C를 눌러서 발생시킵니다.
    • SIGQUIT는 Ctrl+\를 눌러서 발생시킵니다.

일반적으로 SIGINT는 프로그램이 정상적으로 종료될 때 사용되고, SIGQUIT은 디버깅이나 예상치 못한 문제 해결을 위해 코어 덤프를 생성할 때 사용됩니다.

네, 맞습니다. newhandler.sa_flags = SA_RESETHAND | SA_RESTART; 코드 라인은 두 가지 플래그를 설정하고 있습니다.

  1. SA_RESETHAND:

    • 이 플래그는 시그널 핸들러 함수가 한 번 호출된 후에 해당 시그널에 대한 핸들러를 디폴트 핸들러로 재설정합니다.
    • 다시 말해, 시그널 핸들러 함수가 한 번 실행되면 그 이후에는 해당 시그널이 발생하더라도 핸들러 함수는 디폴트 핸들러로 재설정됩니다.
  2. SA_RESTART:

    • 이 플래그는 입출력 함수가 시그널에 의해 중단될 경우, 해당 함수를 재시작하도록 지정합니다.
    • 따라서 입출력 함수(fgets 등)가 실행 중에 SIGINT 시그널이 발생하면, 해당 함수는 중단된 지점에서 다시 시작됩니다.

결과적으로, 한 번 SIGINT 시그널이 발생하고 해당 핸들러 함수가 실행되면, 그 이후에는 SIGINT 시그널이 발생해도 핸들러 함수가 다시 호출되지 않습니다. 대신에 디폴트 핸들러로 재설정되며, 입출력 함수는 중단된 지점에서 재시작됩니다. 이 동작은 SA_RESETHANDSA_RESTART 플래그의 조합으로 결정됩니다.

업로드중..

  1. 프로그램이 실행되고 fgets 함수가 호출되어 사용자로부터 입력을 받을 때, "1234"를 입력하였습니다.
  2. 그 후에 SIGINT 시그널이 발생하여 시그널 핸들러 함수 inthandler가 호출되었습니다.
  3. inthandler 함수에서 "Called with signal 2"를 출력하고, sleep(2)에 의해 2초 동안 대기한 후 "done handling signal 2"를 출력하였습니다.
  4. 이후, fgets 함수가 중단된 지점에서 다시 시작되어 사용자로부터 입력을 받을 때, "5678"을 입력하였습니다.
  5. "input: 5678"이 출력되었습니다.

따라서 "1234"는 출력되지 않는 것이 정상입니다. 왜냐하면 시그널 핸들러 함수 inthandler에서 sleep(2)에 의해 2초 동안 대기하는 동안, fgets 함수는 중단된 상태이며, 그 동안에는 입력이 받아지지 않습니다. 즉, "1234"는 시그널 핸들러 함수가 처리되는 동안 입력되었지만, 그 이후에야 다시 fgets 함수가 실행되어 "5678"이 입력되고 출력되었습니다.

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

int main() {
    int i=5;
    sigset_t sigs; // 시그널을 포함하는 시그널 집합
    sigset_t prevsigs; // 현재 시그널 마스크를 저장하기 위한 이전 시그널 집합

    sigemptyset(&sigs);
    sigaddset(&sigs, SIGINT);

    printf("Critical section in\n");
    // sigprocmask(): sigs 시그널 집합에 포함된 시그널을 블록함.
    // 현재 시그널 마스크는 prevsigs에 저장됩니다.
    sigprocmask(SIG_BLOCK, &sigs, &prevsigs);
    // SIGINT 시그널이 블록되어 있음
    while (i--) {
        sleep(1);
    }
    sigprocmask(SIG_SETMASK, &prevsigs, NULL);
    // 이전 시그널 마스크로 복구하여 SIGIN 블록을 해제
    printf("Critical section out\n");
    whlie (i--) {
        sleep(1);
    }

wait 함수와 waitpid 함수의 차이점

wait 함수

pid_t wait(int *status);

wait 함수는 모든 자식 프로세스 중 하나의 종료를 기다립니다.
대기 중인 모든 자식 프로세스 중 하나가 종료되면 해당 자식 프로세스의 프로세스 ID를 반환합니다.
부모 프로세스는 자식 프로세스의 종료 상태를 status에 저장합니다.
대기 중인 자식 프로세스가 없다면, 즉시 반환합니다.

waitpid 함수

pid_t waitpid(pid_t pid, int *status, int options);

waitpid 함수는 특정 자식 프로세스의 종료를 기다립니다.
pid 인자를 사용하여 대기할 자식 프로세스의 PID를 지정할 수 있습니다.
options 인자를 사용하여 대기 방식을 세부적으로 제어할 수 있습니다.
pid > 0이면 해당 PID의 자식 프로세스가 종료될 때까지 대기합니다.
pid == -1이면 모든 자식 프로세스 중 하나가 종료될 때까지 대기합니다.
pid == 0이면 현재 프로세스 그룹 내의 모든 자식 프로세스 중 하나가 종료될 때까지 대기합니다.
pid < -1이면 프로세스 그룹 ID가 -pid와 같은 모든 자식 프로세스 중 하나가 종료될 때까지 대기합니다.
반환 값은 종료된 자식 프로세스의 PID입니다.
주요 옵션:
WNOHANG (0x01): 대기 중인 자식이 종료되지 않았다면 즉시 반환합니다. 대기 중인 자식이 없으면 0을 반환합니다.

WUNTRACED (0x02): 대기 중인 자식이 정지된 상태인 경우에도 반환합니다.

WCONTINUED (0x04): 대기 중인 자식이 재개된 경우에도 반환합니다.

차이점 요약

wait 함수는 간단하게 모든 자식 프로세스 중 하나의 종료를 기다립니다.
waitpid 함수는 더 세부적인 제어가 가능하며, 특정 PID의 자식을 대기하거나 다양한 옵션을 사용할 수 있습니다.

0개의 댓글