Suhwanc

이번에 배울 시그널은 특정 시스템에 한정된 용어가 아닌 운영체제 전반에서 쓰이는 용어이니 잘 알아두자.

 

우선 이전에 배운 것들을 다시 상기해보자.

 

우리가 지금까지 배운 컴퓨터 시스템 안에서의 상호작용

 

  • Assembly language : 명령어 사이의 상호작용
  • Procedure calls : 함수 사이의 상호작용
  • Linking : 오브젝트 파일 사이의 상호작용
  • Exceptions : OS와의 상호작용 (application -> OS)

 

결론부터 말하자면, 시그널은 application과의 상호작용을 의미하는데, 이는 application에 제어권을 넘긴다는 것을 의미한다.

말은 어려운 것 같지만 이후 나올 내용은 앞에서 배운 어떤 것보다 우리에게 친숙하게 느껴질 것이니 끝까지 잘 공부해보자.

 

시그널이란?

시그널은 실행 중인 프로그램(프로세스)에게 "시스템 안에 무언가 이벤트가 발생했어요!"라고 알리는 아주 작은 메시지이다.

예를 들어 명령 프롬프트 창에서 프로그램 실행 중에 사용자가 Ctrl + c를 누르면 우리 눈엔 그냥 프롬프트 창 기능이겠거니 하지만, 사실 강력한 메시지를 application에게 보내 프로그램을 즉시 종료(kill)시키는 것을 의미한다.

 

 

시그널의 특징은 다음과 같다.

 

  • 시그널은 종류마다 정수형으로 ID가 지정되어있다. (어떤 시그널은 1, 어떤 시그널은 30.. 이런 식으로 말이다.)
  • 시그널은 exception과 인터럽트와 유사하다.
  • 시그널은 커널에서 프로세스로 전송된다.

 

 

시그널을 보내는 방법

시그널을 직접 보내는 건 바로 "커널"인데 커널은 목적 프로그램(시그널을 보낼)의 내용 안에 무언가 변화를 줌으로써 시그널을 보낼 수 있게 된다.

 

커널 추가

커널이란? 컴퓨터의 운영 체제의 핵심이 되는 프로그램 중 하나로, 전반적인 시스템을 통제하는 역할을 합니다.

 

 

Why?

그렇다면 시그널을 보낼 때 커널을 사용하는 이유은 무엇일까?

 

커널을 사용하는 이유는 크게 두 가지가 있다.

 

  • 커널은 0으로 나누는 것(divide-by-zero) 또는 하위 프로그램을 제거하는 시스템 이벤트를 감지할 수 있다.
  • 다른 프로세스가 커널에게 시스템 종료 호출을 요청할 수 있다.

 

처리 방식

시그널을 처리하는 방식도 두 가지로 나눌 수 있다.

 

1) Pending

Pending은 보냈는데, 아직 받지 못한 상태를 의미한다. 예를 들어 A가 B에게 메일을 보냈는데, 아직 B의 메일함에 메일이 도착하지 않았다면, A가 보낸 메일은 Pending 상태라고 부른다.

 

Pending의 가장 큰 특징은 "not queued" 즉 저장이 안된다는안 된다는 점이다. 이 부분에서 좀 오해의 여지가 있는데, 여기서 저장이 안 된다는 부분은 시그널의 종류가 중첩되지 않는다는 것을 의미한다.

즉, 시그널 ID마다 최대 1개씩만 Pending 상태일 수 있고 동일 시그널이 올 시 버린다.

 

2) Block

Block은 시그널을 보냈는데 무시해버리는 것을 의미한다. 추가로 Block은 어떤 시그널에 대해서만 선택적으로 Blocking이 가능하다.

 

좀 더 구체적인 처리 방식

그렇다면 위 방식을 커널은 어떻게 수행하는가? 에 대해 알아보자.

 

1) Pending

만약 k라는 시그널을 보낼 때, 커널은 k라는 비트를 설정해둔다.(set)

만약 k라는 시그널이 도착했다면, 커널은 k라는 비트를 지운다.(clear)

 

2) Blocked

블록 상태는 지정된 마스크 기능을 이용해 set, claer 작업을 한다.

 

이번엔 처리 방식을 순차적으로 살펴보겠다.

 

1. 커널은 pnb 를 계산합니다. (pnb란 pending & ~blocked인데, 이 두 비트를 & 연산한 거라고 일단 생각하자.)

 

2. 만약 pnb가 0이라면 다음 명령어로 이동한다.

여기서 pnb가 0이라는건 pending과 non blocked 연산을 했더니 어떤 비트도 둘 다 1인 비트가 없었다는 것인데,

만약 pnb의 어떤 비트가 0이라면 두 가지 경우를 예측해볼 수 있다. pending을 했으나, block인 경우 또는, pending을 안 한 경우이다.

 

3. 반면 pnb의 비트 중 하나라도 1인 비트가 있다면 시그널 발생!

이 경우 모든 nonzero 인 pnb 비트에 대해 무언가 action (해당 시그널에 맞는)을 하게 된다.

모든 비트를 다 처리하였다면, 다음 명령어로 이동한다.

 

 

시그널 종류 확인법

 

위 그림과 같이 kill -l 을 입력하면 모든 시그널을 볼 수 있다.

 

대표적으로 몇 가지만 살펴보자면

 

  • SIGINT : 프로그램을 종료한다(ctrl + c를 누르면 발생)
  • SIGSTP : 프로그램을 일시 중지한다(ctrl + z를 누르면 발생)
  • SIGKILL : 프로그램을 "무조건" 종료한다.
  • SIGSEGV : 세그먼트 폴트가 발생한 경우이다.
  • SIGALRM : 타이머 시그널이다.(타이머도 시그널에 포함된다)

 

SIGINT와 SIGKILL의 차이?

두 시그널은 하는 역할은 동일하지만, 강제성 부분에 차이가 있다.

SIGINT는 우선 종료할지 물어보고 끝을 내는데, 물어봐서 안 되는 경우 작동하지 않을 수 있다.

하지만 SIGKILL은 시스템 레벨에서 작동하는 시그널이기 때문에 무조건 종료가 된다.(비상 상황일 때 사용)

 

 

다음 포스팅에서는 컴퓨터가 시그널을 보내는 것 말고 시그널 핸들링을 이용해

사용자가 직접 시그널을 호출해서 무슨 시그널이 있는지 c언어 코드를 이용해 실습해보자!