Linux/Linux_programing

IPC - 01. 프로세스 신호 signal 주고받기

sosal 2009. 10. 31. 05:56
반응형
/*
 http://sosal.tistory.com/
 * made by so_Sal
 */



프로세스 통신방법엔 대표적인 방법 3가지. siganl (신호), pipe (파이프), socket (소켓) 이 있다.
signal은 돌맹이
pipe는 편지
socket는 소포에 비유할 수 있다.

signal이 돌맹이에 비유한다는 것은,
짱똘을 던지면 내가 너에게 화가났다.
조약돌을 던지면 내가 너를 사랑한다. 등등..
행동에 대해 미리 약속을 해놓은 후 그 행동을 통하여
서로 대화를 나누는 방법이랄까?
가수들이 무대에서 무슨 손가락 모양을 하면서
비밀스런 애인에게 사랑을 전달하는 방법도 이에 포함되겠다 ^-^;;

pipe나 socket에 대한 비유는..음..
이글은 SIGNAL 관련 글이므로 생략하겠습니다 ㅋㅋ

프로세스는 신호를 일으키고, 잡고, 신호를 처리하고 무시할 수 있습니다.
서로 약속해 놓은 명시적인 이벤트 이기때문에 약속되지 않는 신호의 경우는
프로세스가 즉시 종료하게 되고, core라는 이름으로 현재 디렉토리에 저장됩니다.
저장되는 코어 덤프 파일은 프로세스의 이미지를 담고 있고, 디버깅 할때 유용하게 사용됩니다.



kill은 프로세스에게 신호를 보내는 명령어입니다.
자세한 내용은 메뉴얼 페이지(man kill) 에 대해서 자세히 살펴보세요.
다음 장 signal 02.글에 kill 명령어에 대한 자세한 내용을 실으려 합니다.

위 그림을 보면 kill 명령어(신호 보내기)에 대한 명시적인 방법의 갯수들이 나오는군요.
굉장히 많습니다.

중요한 신호들의 내용을 알아볼까요?

신호 이름

:: 설명

SIGABORT

:: 프로세스 취소

SIGALRM

:: 알람 클록

SIGFPE

:: 부동 소수점 예외

SIGHUP

:: 회선 연결 끊기, 터미널

SIGILL

:: 잘못된 명령

SIGINT

:: 터미널 인터럽트

SIGKILL

:: 강제 종료

SIGQUIT

:: 터미널 종료

SIGSEGV

:: 유효치 않은 메모리 접근

SIGTERM

:: 종료

SIGCHLD

:: 자식 프로세스의 종료

SIGSTOP

:: 강제종료 (잡거나 무시 x)

SIGTSTP

:: 터미널 중단

SIGCONT

:: 중단되었다면 수행 재개

SIGCHID는 자식 프로세스 관리에 많이 사용된다.
SIGCONT는 중단된 프로세스가 실행을 재개하도록 만든다.

하지만 나머지 모든 신호는 해당 프로세스가 종료하도록 만든다.
(사용자 프로그램들이 이러한 신호를 사용하는 경우는 드물다.)

SIGINT는 Ctrl + c 를 눌렀을때 발생하는 인터럽트. (터미널에..)

위의 kill 명령어를 사용해서 프로세스에게 signal을 보낼 수 있다.
ex)
kill -HUP pid //사실 이것은 kill -1 pid 와 같다. (pid는 프로세스 아이디 숫자를 말함)
kill -SIGINT pid == kill -2 pid // 터미널 인터럽트

HUP이 1이라던지, SIGINT가 2라던지 등의 사실은 위 그림에서 볼 수 있듯,
kill -l 으로 볼 수 있다. 자세한 내용은 man kill 메뉴얼 페이지 참조


그럼 이제 신호처리 기반의 함수들과 예제 소스를 통해서 익혀보자.

#include<signal.h>

void (  *signal(int sig,void(*func)(int))  ) (int);
:: signal 함수는 int형식의 매개변수와 int를 받고 void(nothing)을 돌려주는 함수를 가리키는 함수 포인터 형식의
매개변수를 받아오고, void 함수 포인터를 돌려준다.
** sig로 주어진 번호에 해당하는 신호가 발생했을 때, func에 대한 함수를 호출하여
그 행동을 실행하게된다.

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

void _signal(int sig);

int main(void){
    (void)signal(SIGINT,_signal);
/*  이제부터 현제 프로세스에게
 *  SIGINT에 해당하는 신호를 받으면,
 *  _signal이란 함수를 호출하게 됨
 */

    while(1){
        printf("give me a signal\n");
        sleep(1);
    }
    exit(0);
}

void _signal(int sig){
    printf("You give a signal to me, thanks\n");
    (void) signal(SIGINT, SIG_DFL);
    //SIGINT 신호를 받아왔을때, 간략한 문장을 출력후
    //SIG_DEL (기본 행동 수행)으로 메인함수가
    //하던일을 계속 수행하도록 한다.
    //이후에 한번더 SIGINT를 날려주면 프로세스가 종료됨
}

프로그램을 실행 후 ctrl + c (==SIGINT)로 신호를 보낼 수 있고,
./SIGINT & (만약 현재 프로그램의 이름이 SIGINT라면..)로 백그라운드로 실행시켜서
kill 명령어로 신호를 보낼 수 있다. (백그라운드로 실행시키면 기본적으로 실행된 process의 pid가 출력됨)
kill -2 pid, 또는 kill -SIGINT pid 또는 kill -INT pid 등등..

사실 프로세스가 계속 출력하기때문에 쉘의 명령문이 자꾸 깨진다.
하지만 보기엔 깨져 있어도, 내가 입력한 명령어는 그대로 존재하니, kill 명령어를 충분히 사용 가능함
다른 터미널 창을 띄워서 신호를 보내도 충분히 가능

* 하지만 signal 인터페이스를 이용하여 신호를 잡는것에는 한계가 있다.
  signal이 호출하는 함수에서 재귀함수처럼 신호를 계속 잡아주는 프로그램이 있다면
  레이스컨디션 취약점이 발생할 수 있으므로, sigaction 인터페이스를 사용하길 바란다.
  이 내용은 다음 글을 참조