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와 문자열을 받아 전송한다.
'42Seoul' 카테고리의 다른 글
[42Seoul] Philosophers : 뮤텍스와 세마포어 (0) | 2021.08.14 |
---|---|
[42Seoul] Push_Swap : 정렬 알고리즘 구현 (0) | 2021.06.16 |
[42Seoul] ft_server (Docker + LEMP) (0) | 2021.03.01 |
[42Seoul] ft_printf - 나의 printf 구현하기 (0) | 2021.02.06 |
[42Seoul] Netwhat - 네트워크 및 시스템 관리 (0) | 2021.01.23 |