Signal

김두현·2024년 5월 29일
0

CS 이것저것...

목록 보기
4/8

42 minitalk 과제를 진행하기 앞서 시그널에 대한 공부가 필요한 거 같아 간단하게 정리해보는 시간을 가지겠다!!

What is Signal

시그널은 유닉스 환경에서 30년 동안 사용된 전통적인 기법 중 하나로 프로세스에 어떤 event가 발생했음을 알리는 간단한 메세지를 비동기적으로 보내는 것이라고 할 수 있다. process는 간단히 말해 실행중인 프로그램(메모리에 적재된)이라 할 수 있는데 실행중인 processor에 특정 이벤트가 발생함을 알릴 수 있는 기법 중 하나다. 그리고 시그널이 발생했을 때 프로세스가 실행되는 동작을 처리? 정의하는 곳을 signal handler라고 한다. 이 시그널 핸들러는 기본적으로 os가 지정된 동작이 정의되어 있는데 특정 시그널을 제외하면 프로세스 내에서 새롭게 핸들러는 정의할 수 있다. 이외에도 시그널의 특징은 다음과 같다.

  • 시그널은 IPC(Inter-Process Communication) 기법 중 하나
  • 굉장히 작은 값
  • aka 소프트웨어 인터럽트

Signal 원리

프로그래머가 임의로 발생하는 시그널 이외에도 os, 커널 측에서 프로세스에게 시그널을 보내는 경우가 있다.

  1. 하드웨어 예외: 하드웨어의 결함 or 디바이스 드라이버 동작 등…
  2. 소프트웨어 이벤트
    1. 파일 디스크럽터에 입력이 발생
    2. 터미널 윈도우의 크기 조절
    3. 타이머 만료,
    4. 해당 프로세스의 자식 프로세스가 종료

시그널 보내는 방법

kernel이 보내는 시그널 이외에도 프로그래머(사용자)가 필요에 따라 특정 프로세스에게 시그널을 보내는 방법은 크게 3가지 정도가 있다.

  1. shell을 통해 kill command 사용
  2. 라이브러리 이용(kill, raise, abort)
  3. 키보드 입력(ctrl + c, ctrl + z …)

kill 명령어 사용 예

#include <stdio.h>
#include <unistd.h>

int main()
{
	int i = 0;
	
	printf("my pid: %d\n", getpid());
	while (1)
	{
		printf("time: %d\n", i++);
		sleep(1);
	}
}

  • while 문으로 무한 반복을 수행하는 프로그램을 kill 명령어를 통해 9번 시그널을 보냄
  • 9번 시그널은 SIGKILL로 프로세스를 확실한 종료를 수행

키보드 입력 예

  • 간단하게 ctrl + c 입력을 통해 SIGINT 시그널을 발생해 프로세스 종료

아래의 그림과 같이 os kernel 자체에서 processor에세 signal을 보내거나 어떤 프로세스가 kernel에 요청을 해 보내는 방법 2가지가 존재한다.


시그널 받기, 처리

시그널의 동작은 위의 그림이 잘 설명하고 있는데, 1) 프로세스가 시그널을 받으면, 2) 시그널 핸들러로 컨트롤(제어권)을 넘기고 3) 핸들러에 정의된 동작을 수행하고 4) 시그널 핸들러는 다음 명령어를 실행(코드로 치면 다음 줄 명령어?)하는 과정을 거친다.


시그널 종류

시그널은 크게 2 종류로 구분할 수 있는데, 1) 리눅스의 표준 시그널과 2) 실시간 시그널 두 가지로 나눌 수 있다. 표준 시그널은 커널이 프로세스에게 이벤트를 알릴 때 사용하는 시그널로 1 ~ 31의 상수값으로 구분된다. 실시간 시그널은 응용 프로그램에 정의된 목적을 위해 사용될 수 있는 시그널로 32 ~ 63까지의 번호가 매겨진 시그널을 사용한다.

각 os에서 시그널 리스틀 확인하기 위해 kill -l 명령어를 사용해 확인할 수 있다.

자주 사용되는 시그널 몇 가지를 정리해보자.

번호시그널설명
1SIGHUPHangup: 접속을 끊을 때 → 터미널과 연결이 끊어졌을 때 발생
2SIGINTInterrupt: 현재 작동 중인 프로그램의 동작을 멈춤
3SIGQUITQuit: 사용자가 터미널에서 종료키를 누를 때
6SIGABRTAbort: 비정상적인 함수에 의해 발생
9SIGKILLKill: 실행 중인 프로세스를 강제 종료할 때
11SIGSEGVSegmentation Violation: 메모리 액세스가 잘 못 되었을 때 발생
13SIGPIPE종료된 소켓에 쓰기를 시도할 때
14SIGALAM알람 타이머 만료 시에 발생
15SIGTERMTerminate: 정상적인 종료 방법
17SIGCHLD자식 프로세스가 종료할 때 사용
18SIGCONT중지된 프로세스 실행할 때 사용
19SIGSTOPSIGCONT 시그널 받을 때 사용
20SIGTSTP프로세스 대기로 전환할 때 사용

이외에도 프로그래머가 임의로 사용할 수 있는 시그널로 SIGUSR1(10), SIGUSR2(12)도 존재한다.


시그널 핸들러

앞서 말했듯이 이미 커널이 특정 시그널이 발생했을 때 정의된 동작이외 다른 동작을 프로그래머가 구현할 수 있다. 시그널이 발생했을 때 프로세스가 동작하는 부분을 시그널 핸들러라고 하는데 c에서는 system call인 signal, sigaction 두 메소드를 통해 시그널 핸들러를 조작할 수 있다.

함수 원형: sighandler_t signal(int signum, sighandler_t handler)

  • signum: 시그널 번호
    변수는 프로세스에게 발생된 시그널 번호를 의미
  • handler: void(* func)(int)
    signum 번호에 맞는 시그널이 들어왔을 때 취해야할 동작을 정의하는 함수 포인터
    handler로 지정할 수 있는 것은 3가지가 존재
  1. 새로운 함수 지정
  2. SIG_IGN: 시그널 무시
  3. SIG_DFL: 기존 정의된 행동 취함

예를 들어 signal(SIGUSR1, sig_handler); 와 같이 코드를 구현하면 SIGUSR1 시그널이 발생하면 해당 프로세스는 sig_handler 함수를 실행한다는 의미이다. 다음 코드는 c에서 signal handler를 간단하게 구현한 예이다.

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

void	sigusr1_handler(int signo)
{
	printf("SIGNAL: %d\n", signo);
	printf("새롭게 정의된 SIGUSR1");
	exit(1);
}

int main()
{
	int i = 0;
	
	signal(SIGUSR1, sigusr1_handler);
	printf("my pid: %d\n", getpid());	
	while (1)
	{
		printf("time: %d\n", i++);
		sleep(1);
	}
}

SIGUSR1와 같이 사용자 시그널 이외에도 기존에 kernel에 정의된 signal 또한 handler를 정의할 수 있다.

💡 SIGKILL, SIGSTOP 시그널은 프로그래머가 못 건드린다!

위와 같이 기존 SIGINT 시그널 또한 재정의할 수 있다!

💡 이외에도 c에서 제공하는 signal 관련 라이브러리(sigaction, sigemptyset, …)가 많은데 추후 다음 포스팅에서 다뤄보겠다

비동기적 시그널

처음에 시그널을 설명할 때 시그널은 프로세스에게 어떤 이벤트가 발생함을 작은 데이터를 통해 알린다고 설명했다. 여기서 추가적으로 덧붙일 내용은 데이터를 보낼 때 비동기적으로 보낸다는 것이다.

비동기적이라는 개념을 간단히 설명하면 말 그대로 동시에 일어나지 않는다를 의미하는데, 하나의 요청에 따른 다른 응답을 즉시 처리하지 않아도, 그 대기 시간동안 다른 요청을 처리 가능하다는 의미이다.

시그널이 비동기적으로 발생한다는 것은 두 가지 측면으로 해석이 가능하다.

  1. Dest Process: 프로세스에게 어떤 시그널이 발생하여 핸들러가 실행되는 중에(아직 처리 X) 시그널이 또 발생할 수 있다는 것을 의미한다.
  2. Src Process: 수신 측 프로세스가 시그널을 처리할 때까지 기다리지 않고 다른 작업(시그널을 또 보낸다거나)을 계속 수행함을 의미한다.

Dest Process 측에서 시그널이 발생하여 핸들러가 실행되는 중에 시그널이 또 발생되면 새로운 시그널은 시그널 큐라는 공간에서 대기하다가 핸들러 동작이 끊난 후에 실행된다.

  • 핸들러 실행 중 여러 시그널이 들어오면 큐에 특성에 맞게 먼저 들어온대로…

위와 같이 시그널이 발생했으나 아직 처리하지 못 한, 전달되지 않은 시그널을 pending signal이라고 한다. 여기서 주의해야 할 점은 같은 번호의 시그널이 signal queue에 계속 들어오면 쌓이는 것이 아닌 단 한 개의 signal만 큐에서 대기한다. 달리 말하면 특정 시그널이 pending 상태일때 해당 시그널이 발생하면 큐에 넣지 않고 discard한다!

💡 왜 저런 성질을 가지냐하면 bit mask인가 뭔가 때문인데 이것도 다음 포스팅에서 다뤄보겠다.

Reference

Linux : Signal이란?

[OS] signal

시그널 (signal)- 동작 원리, 주요 시그널, 프로세스와 관계

profile
끄적끄적

0개의 댓글

관련 채용 정보