42Seoul

[42 Seoul] Minitalk : signal 함수로 IPC(Inter-Process Communication) 구현

kyo 2021. 7. 2. 12:21

1. signal 이란

 

시그널(signal)은 software interrupt 로 프로세스 간의 비동기적 이벤트와 데이터를 공유, 전송할 수 있도록 운영체제에서 제공되는 IPC 중 하나이다.

signal을 프로세스에 보내면 각 signal에 지정된 동작을 수행한다. 

 

2. signal 함수

  • signal 

signal 함수는 <signal.h> 에 정의되어 있으며 시그널을 받아 지정된 함수를 실행할 수 있다. 

signal(SIGINT, (void *)sig_handler);

 

아래는 signal의 종류이다.

 

SIGHUP 터미널 연결이 끊어졌을   터미널과 연결된 세션 리더 또는 세션에 속한 모든 프로세스들에게 보내지는데  시그널을 받으면 종료
SIGINT 터미널에서 인터럽트 키를 눌렀을  보내지는데  시그널 받으면 종료
SIGILL 불법 명령어를 실행할  보내지는데  시그널을 받으면 코어 덤프  종료
SIGABRT Abort 함수를 호출하면 보내는데  시그널을 받으면 코어 덤프  종료
SIGBUS 하드웨어 결함이 탐지 되면 보내는데  시그널 받으면 종료
SIGFPE 0으로 나누기, 부동소수점 오류 등이 발생했을  보내지는데  시그널을 받으면 코어 덤프  종료
SIGKILL 프로세스를 종료시키기 위해 보내는데  시그널 받으면 반드시 종료
SIGUSR1 사용자가 정의해 사용할  있는 시그널로  시그널을 받으면 종료
SIGSEGV 잘못된 메모리 주소를 접근하고자   보내지는데  시그널 받으면 코어 덤프  종료
SIGUSR2 사용자가 정의해 사용할  있는 시그널로  시그널을 받으면 종료
SIGPIPE 닫힌 파이프에 쓰고자   보내지는데  시그널을 받으면 코어 덤프  종료
SIGALRM Alarm 함수를 호출하면 보내는데  시그널을 받으면 종료
SIGTERM 프로세스를 종료시키기 전에 하던 일을 정리하고 종료  것을 알릴  보냄
SIGCHLD 자식 프로세스가 종료되면 부모 프로세스에 보내짐, wait에서  시그널이 의해 깨어남,  시그널 받으면 무시
SIGCONT 중단되어 있는 프로세스가  시그널을 받으면 실행, 실행 중인 프로세스가 받으면 무시
SIGSTOP 프로세스를 멈추기 위해 보내는데  시그널을 받으면 반드시 멈춤
SIGTSTP 터미널에서 일시 중지 키를 눌렀을  보내지는데  시그널을 받으면 멈춤
SIGTTIN 백그라운드 작업 중인 프로세스가 표준 입력을 하려할  현재 실행 중인 프로세스에 보내는데  시그널을 받으면 멈춤
SIGTTOU 백그라운드 작업 중인 프로세스가 표준 출력을 하려할  현재 실행 중인 프로세스에 보내는데  시그널을 받으면 멈춤
SIGSYS
잘못된
 시스템 호출을 했을  보내지는데  시그널을 받으면 코어 덤프  종료

 

  • sigaction

sigaction 함수는 signal 보다 다양한 기능을 제공하는 함수이다. 

int	sigaction(int, const struct sigaction * __restrict,
	    struct sigaction * __restrict);

sigaction구조체로 가져올 데이터와 핸들링 함수를 지정할 수 있다.

sigaction 구조체는 <sys/signal.h>에 선언되어 있으며 signal 마스크와 flags를 설정할 수 있다.

struct sigaction {
        void     (*sa_handler)(int);
        void     (*sa_sigaction)(int, siginfo_t *, void *);
        sigset_t   sa_mask;
        int        sa_flags;
        void     (*sa_restorer)(void);
};

sa_handler - signal을 받아 실행시킬 함수에 대한 포인터이다.

sa_sigaction - 시그널 핸들러를 실행하는 다른 방법이다. 추가 매개변수를 받아 siginfo_t* 구조체를 통해 많은 정보를 받아올 수 있다.

sa_mask - 핸들러 실행 중에 블로킹 될 시그널을 설정할 수 있다.

sa_flags -  SA_SIGINFO flag 를 사용하면 sa_sigaction을 사용해야하며 아래와 같은 정보를 받아올 수 있다.

siginfo_t {
    int      si_signo;  /* 시그널 넘버 */
    int      si_errno;  /* errno 값 */
    int      si_code;   /* 시그널 코드 */
    pid_t    si_pid;    /* 프로세스 ID 보내기 */
    uid_t    si_uid;    /* 프로세스를 전송하는 실제 사용자 ID */
    int      si_status; /* Exit 값 또는 시그널 */
    clock_t  si_utime;  /* 소모된 사용자 시간 */
    clock_t  si_stime;  /* 소모된 시스템 시간 */
    sigval_t si_value;  /* 시그널 값 */
    int      si_int;    /* POSIX.1b 시그널 */
    void *   si_ptr;    /* POSIX.1b 시그널 */
    void *   si_addr;   /* 실패를 초래한 메모리 위치 */
    int      si_band;   /* 밴드 이벤트 */
    int      si_fd;     /* 파일 기술자 */
}
  • kill

kill 함수는 시그널을 보낼 수 있는 함수이다.

아래와 같이 선언된다.

int kill(pid_t pid, int sig);
// pid : 시그널을 받을 pid
// signal : 넘버
  • getpid

현재 실행중인 process의 pid를 가져오는 함수

  • pause

pause 함수를 사용하면 signal이 들어올때 까지 대기한다.

while(1){
	pause();
}

 


1. Mandatory Part

  • 여러분께서는 클라이언트와 서버가 서로 통신하는 프로그램을 작성하셔야 합니다.
  • 서버와 클라이언트 중 서버가 먼저 실행되어야 하며, 클라이언트가 런치가 될 때에 PID를 표시해야 합니다.
  • 클라이언트가 실행될 때 다음의 매개변수를 받습니다 :
    • 서버 PID
    • 전송할 문자열
  • 클라이언트는 매개변수로 전달한 문자열을 서버로 통신해야 합니다. 서버는 문자열이 수신되면 해당 문자열을 표시해야 합니다.
  • 여러분이 작성하신 서버와 클라이언트의 통신은 오직 UNIX signal을 이용하여야만 합니다.
  • 서버는 문자열을 매우 빠른 속도로 표시할 수 있어야 합니다. 즉, 표시되는 시간이 너무 길다고 생각된다면, 그건 너무 길다고 여겨야 합니다. (힌트 : 100개의 문자로 이루어진 문자열을 표시하는 데 1초가 걸린다면 그건 어마어마하게 긴 것입니다.)
  • 서버가 재시작할 필요없이 여러 클라이언트로부터 문자열을 연속으로 수신할 수 있어야 합니다.
  • SIGUSR1 SIGUSR2 두 신호만 사용할 수 있습니다.

과제에서 요구하는 SIGUSR1,  SIGUSR2 두가지 신호만 사용하여 server에게 문자열을 전송하는 방식을 고민하다  char를 이진법으로 바꾼 후 bit로 0과 1로 구분하여 전송한 뒤 server에서 받아와 유니코드로 변환 후 char 하나씩 출력하는 방식으로 구현하였다.

 

비트를 순차적으로 받아올때 쉬프트 연산을 사용하여 비트를 더해가며 8개의 bit를 만들었으며 static 변수를 사용해 함수를 재호출할때에도 소멸되지 않고 유지될 수 있게 하였다.

서버를 실행시키면 pid를 출력, 계속해서 시그널 대기를 하고 클라이언트는 메세지를 보낼 pid와 문자열을 받아 전송한다.