signal이란 상황을 알리는 정보이다. 프로세스가 종료 되었다던지. 프로세스가 이상한 짓을 한다던지 하는 상황을 os에게 알리기 위한 정보이다. OS는 프로세스들을 관리해야 되고 프로세스들이 어떤 짓을 하고 있는지 지켜봐야 한다. 그래서 signal이라는 규칙을 정해서 사용한다.
자식 프로세스가 끝나거나 런타임 에러로 중단되거나 키보드로 종료해달라는 명령을 주는 등 다양한 상황에 따른 시그널이 미리 정의되고 그것을 통해 상황을 처리하는 규칙이 있는 셈이다. OS는 시그널 정보를 이용하여 위와같은 상황을 처리하도록 한다.
특정 상황이 발생하면 SIGNAL이 어딘가에 저장되고 OS가 감지할 수 있도록 해야한다.
프로세스 내부에서 수식의 계산에서 0으로 나누는 등 이상한 짓을 한 경우에는 자신의 프로세스가 이상한 짓을 한다는 정보를 OS에게 알려주어야 한다. 이를 위해서 자신 프로세스의 PCB에 종료해주세요 라는 시그널을 적어두게 된다. 그래야 OS가 보고 처리를 해줄 것이다.
(*프로세스 내부에서 런타임 에러가 발생했을 때 시그널이 PCB에 저장되는 과정은 추후에 공부해서 정리하도록 하겠다.)
PCB에는 signal 관련 정보들이 저장되어 있다. PCB안에 테이블이 여러개 있다고 생각하면 편하다. 하나는 table - signal handler pointer, 하나는 table-pending_signal
1. table - signal handler
signal 종류 별로 어떻게 처리할지 포인터를 가지고 있다.
SIGNAL 1번에 대한 처리 지침이 table의 1번 칸에 포인터로 들어있는 것이다.
이 프로세스를 끝내야겠어요 라는 행동을 해주는 녀석을 여기에 적어두는 것이다.
number | signal handler pointer |
SIGTERM | function pointer of SIGTERM |
SIGINT | fp of SIGINT |
< PCB의 signal handler 예시 >
공통으로 쓰는 부분 같은데 왜 각 프로세스별로 있냐 하는 질문을 할 수 있다.
이 signal handler pointer는 사용자가 구현한 함수를 가리키게 할 수도 있다. 이런 경우를 위해서 각 프로세스 별로 존재한다.
실제 코드 상에서 이 handler pointer를 변경하는 함수는 sigaction() 이 대표적이다.
태어나서 처음으로 main 함수 안에 sigaction()이 나오면 이게 뭐하는 것인가 싶을 것이다.
이것은 별게 아니고 지금 실행되는 프로세스의 PCB에 signal handler table 부분을 수정해주세요. 라는 명령을 하는 것이다.
나는 linux 콘솔에서 이 프로세스를 ctrl c를 눌러서 꺼지게 하는게 아니라 ctrl c가 눌렸다는 것만 말하고 싶어. 생각할 수 있다.
이 때는 ctrl c가 어떤 SIGNAL을 사용하는지 찾아본다.(검색하면 나온다. what signal does ctrl c make in unix 와 같은 문장 )
그럼 ctrl c에 해당하는 SIGNAL 종류를 알게되고 이 SIGNAL을 처리하기 위해 기본적으로 제공하는 것은 꺼지게 하는 것이라는 것을 파악하게 된다. 당연히 해당 프로세스를 끄려고 만든 키니까 그 프로세스를 끄게 해야지 이상한 짓을 하면 안될 것 아닌가.
그럼 꺼지게 안하고 눌렸다는 것을 표현하는 것으로 바꿔치기 해주면 된다.
처리방법을 바꿔치기 해주는 예제들은 매우 많으므로 한번 따라치기만 하면 이해가 된다.
정리하면 sigaction(SIGTERM, (void *)myfun); 을 한 줄 써주면 PCB에 SIGTERM에 대응되는 포인터가 myfun을 가리키게 된다.
그럼 SIGTERM을 처리해야하는 상황이 되었을 때 OS는 기본 제공되는 것을 사용하는 것이 아니라 내가 구현한 것을 실행시켜준다.
이런 예제들은 검색해보면 많다.
OS를 위해 PCB에 왜 시그널 관련 정보들을 저장해야 하는지에 포커스를 맞춰서 이해하는 것이 중요하다.
OS가 관리하는 상황에서 프로세스들에 어떤 처리들을 진행해야 하는지 알아야 하기 떄문이다.
쉽게 생각하면 os가 곰플레이어 하나만 계속 지켜보고 있을 수는 없지 않은가.
선생님 한명이 학생 몇십명을 다 보고 있을 수는 없으니 학생별로 요청사항에 이것저것 잘 적어두면 선생님은 돌아가면서 안까먹고 이거를 다 처리해줄 것이다.
2.table - pending_signal
signal type | pending value |
SIGTERM | 0 or 1 |
SIGINT | 0 or 1 |
< PCB의 signal pending 부분 예시 >
signal 처리는 프로세스가 하는 것이 아니라 os가 user모드로 돌아가기 직전에 처리해준다.
이 때 os는 process의 PBC에 가서 table-pending_signal을 살펴본다.
table의 값이 0으로 되어 있으면 처리할 시그널이 없는 것이고 1로 되어 있으면 처리할 시그널이 있는 것을 의미한다.
1로 되어있는 signal 값들을 파악한 후 이에 해당하는 핸들러를 사용하여 signal의 값을 1에서 0으로 바꿔주고 처리가 끝난다.
참고로 pcb의 table- pending_signal이 signal당 한 칸씩 되어 있다.
이 말은 동일한 시그널을 여러 개 쌓아두지 못한다는 것을 의미한다.
예를 들면 SIGTERM에 대한 값이 1 아니면 0 이기 때문에 세개의 SIGTERM을 처리해야 한다는 정보를 저장할 수 없는 것이다.
이것은 signal handler 함수가 호출된 것과 종료 되는 사이에 현재 처리되고 있는 signal이 100개 들어온다고 해도 100으로 PCB에 저장할 수 없기 때문에 99개의 signal은 무시되고 1개의 signal만 저장된다. 이 때 살아남은 1개의 signal은 block 되었다고 한다.
block의 의미는 아마도 현재 signal handler가 수행 중일 때 동일 시그널이 아니라 다른 시그널이 오면 인터럽트가 발생하여 새로운 처리를 진행하게 된다. 하지만 동일 시그널은 수행중인 핸들러를 제대로 마치고 나서 다시 수행하기 때문에 block한다고 표현하는 것으로 생각된다.
'컴퓨터공학 > 리눅스 시스템 프로그래밍(linux system programming)' 카테고리의 다른 글
makefile 사용법 (0) | 2019.09.28 |
---|