ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • IPC - signal(), kill()
    운영체제 2025. 5. 24. 15:30

    Signal이란?

    IPC의 여러 방법 중 하나다. 즉, 프로세스끼리 통신하는 방법이다.

    다른 IPC와는 다르게 Signal은 비동기적이라는 특징이 있다.

    사용 목적

    프로세스 간의 이벤트 전달을 위한 기능이다. 제어 흐름(control flow) 중 외부에서 개입하기 위한 수단으로 사용된다.

    예를 들어, 터미널 실행 중 Ctrl+C를 누르면 강제종료되는데, 이때 Signal이 사용된다.

    사용 예시

    아래는 프로그램이 무한루프를 돌며, 도중에 Ctrl+C를 누르면 "Caught signal"을 출력하며 강제종료되는 코드다.

    #include <signal.h>
    
    void handler(int sig) {
        printf("Caught signal %d\n", sig);
    }
    
    int main() {
        signal(SIGINT, SIG_DFL); // Ctrl+C 누르면 강제 종료
        while (1); // 무한 루프
    }

    이때 signal함수의 인자로 SIGINT라는 상수와 SIG_DFL라는 상수를 받는다는걸 알 수 있다.

    이 인자들이 어떤 의미인지 더 자세히 알아보자.

     

    첫번째 인자

    시그널 상수이다. 시그널 상수는 "어떤 상황에 시그널이 작동되는지?" + "어떤 동작을 할지?" 를 의미한다.

    다음과 같이 시그널 상수가 정해져있다.

    • SIGHUP (1번): "터미널 연결이 끊긴 경우" + "프로세스 종료"
    • SIGINT (2번): "Ctrl-C 누를 경우" +  "프로세스 종료"
    • SIGKILL (9번): "kill 명령어 사용하는 경우 or kill()함수 호출한 경우" + "프로세스 종료(핸들러 작동X)"
    • SIGSEGV (11번): "잘못된 메모리 접근한 경우" (예: NULL 포인터 접근) + "종료 & 코어덤프"
    • SIGCHLD (17번): "자식 프로세스가 종료 or 상태변화시" + "부모에게 자식 상태 보냄"

    이 외에도 여러 상수값이 존재한다.

     

    두번째 인자

    위 예시에서 사용한 SIG_DFL 상수는 첫번째 인자의 동작 방식을 따른다는 시그널 상수다.

    이 상수 대신 핸들러를 넣을 수도 있다.

    핸들러를 사용하면 첫번째 인자의 동작 방식 대신 다른 동작으로 커스텀할 수 있다.

     

    핸들러 사용 예시

    아래는 Ctrl+C 입력시 강제 종료하지 않고 "Received signal"만 출력하는 코드다.

    #include <signal.h>
    #include <stdio.h>
    
    void handler(int signum) {
        printf("Received signal %d\n", signum);
    }
    
    int main() {
        signal(SIGINT, handler);  // Ctrl+C 시 handler 실행
        while (1);                // 무한 루프
        return 0;
    }

     

     이처럼 핸들러를 통해 시그널을 차단하거나 무시할 수 있다.

     

    kill()

    signal()이 시그널을 받는 함수라면, kill()은 시그널을 보내는 함수다.

    시그널을 보내려는 프로세스에 대해 권한이 있는 경우에만 사용 가능하다. 즉, 같은 UID이거나 root 권한이 있는 사용자만 가능하다

     

    kill() 정의

    첫번쨰 인자는 시그널을 보낼 프로세스 id이고, 두번째 인자는 시그널 상수다. 시그널 상수는 signal()의 첫번쨰 인자에서 받는 상수와 같은 것을 사용한다.

     

    사용 예시

    • kill(pid, SIGSTOP) : 프로세스 일시중지 (핸들러 작동X)
    • kill(pid, SIGCONT) : 일시정지된 프로세스 계속 실행하기
    • kill(pid, SIGTERM) : 프로세스 정상 종료 (핸들러 작동O)
    • kill(pid, SIGKILL) : 프로세스 강제 종료 (핸들러 작동X)

    SIGKILLSIGSTOP의 경우 기본 동작(일시중지 or 종료)을 무조건 수행하게 한다. signal()에서 핸들러를 등록해도 작동하지 않는다.

     


    signal에서 발생할 수 있는 문제 - 재진입 문제(reentrance)

    핸들러 실행 중에 또 시그널이 오는 문제를 말한다.

    아래 그림과 같이 무한히 핸들러가 호출될 수 있다.

     

     

    시그널 전달 타이밍

    위와 같은 문제를 해결하기 위해, 커널은 시그널을 바로 보내지 않고 특정 타이밍에만 전달하도록 설계되어있다.
    그 타이밍은 다음과 같다.

    1. 프로세스가 커널 모드에서 유저 모드로 돌아갈 때
    2. 쉬거나(waiting) 멈춘(idle) 상태에서 다시 실행될 때
    3. 차단(block)된 시그널이 차단 해제될 때 
    4. 시그널 핸들러가 종료될 때

    즉, 시그널은 프로세스가 다시 실행되기 직전에 커널이 전달한다.

     

    시그널 전달 타이밍 예시

    아래 그림은 1개의 CPU가 두 프로세스 A,B를 실행하는 과정이다.

    가로 한칸은 한 타임슬롯을 나타낸다. 

    1. 프로세스 A부터 실행된다.

    2. A의 타임슬롯이 종료되면 커널을 통해 프로세스B로 넘어가 실행한다.

    3. B의 타임슬롯이 종료되면 커널을 통해 또다시 프로세스A로 넘어간다

    4. 이때 프로세스 A를 실행하기 전에 시그널이 있는지 확인한다.

    5. 만약 시그널이 존재하면 커널이 그에 해당하는 핸들러를 실행한다.

    6. 핸들러를 다 실행하고 나면 다시 프로세스A를 실행한다.

     

     

Designed by Tistory.