🔔 학교 강의를 바탕으로 개인적인 공부를 위해 정리한 글입니다. 혹여나 틀린 부분이 있다면 지적해주시면 감사드리겠습니다.
signal
은 software interrupts로, 비동기 이벤트를 처리한다.
$ gcc verybigprog.c
^C /* Ctrl-C, interrupt key) */
사용자가 ctrl+c
를 입력하면 kernel
이 process들에게 SIGINT
라는 signal을 보내며, gcc
가 이 신호를 받으면 SIGINT
와 관련된 기본 작업을 수행하고 종료한다. signal
은 특정한 일이 발생했음을 알려주는 용도이며, error
와는 다르다는 점을 명심해야한다.
모든 signal
에는 사용 목적에 따른 이름이 존재하며, SIG
로 시작한다. 이는 모두 <signal.h>
에 양의 정수로 정의되며, 다양한 조건이 signal
을 생성할 수 있다. 이에 대한 간단한 예는 아래와 같다.
ctrl+C
: 터미널 생성 신호- 0으로 divide 등 하드웨어 예외
- process에서 system call 종료
- shell에서의
kill
kill -l
command로 signal 목록 확인이 가능하며, 31까지는 동일하고 이후는 system에 따라 달라진다. 총 64개로 이루어진다.
SIGABRT
abort
function을 호출하여 생성됨
SIGALRM
- process에서 alarm으로 설정된 타이머가 만료되면 생성됨
SIGCHLD
- process가 종료되거나 중지되었을 경우, parent에게 전송됨
SIGCONT
- process가 계속될 때 정지된 process에게 전송됨
SIGFPE
- 0으로 나누거나 부동 소수점 overflow와 같은 exception에서 생성됨
SIGILL
- process가 잘못된 hardware 명령을 실행했음을 알림
SIGINT
- 사용자가 interrupt key
ctrl+C
를 입력할 때 terminal에 의해 생성, foreground process group의 모든 process에게 전송됨
SIGKILL
- process를 종료하며 무시할 수 없음
SIGPIPE
- 이미 종료한 pipe에 process가 write하는 경우 생성됨
SIGSEGV
- process가 잘못된 memory 참조를 했을 경우를 의미 (core dumped)
SIGTERM
kill
에 의해 전송되는 종료 신호로,SIGKILL
과 달리 무시가 가능
SIGTSTP
- 사용자가 interrupt key
ctrl+Z
를 입력할 때 terminal에 의해 생성, foreground process group의 모든 process에게 전송됨, 무시가 가능
SIGUSR1
- 사용자 정의 signal
SIGSTOP
- process를 중지하며, 무시가 불가능함 (job control signal)
signal
은 event process에 대한 software 알림이다. 특정 신호를 발생시키는 event가 발생하면 signal이 발생하며, 이를 알리는 역할을 한다. process가 signal을 받으면, 이에 따른 특정 행동을 취해야 하며, signal의 수명은 생성과 전달 사이의 간격에 해당한다.
생성되었지만 아직 전달되지 않은 signal은 block된 것이며, 이를 pending
되었다고 한다. 다른 signal을 처리하는 도중에 signal이 오면, 이전 signal을 처리하기 전까지 block된다. program은 사용자가 작성한 function 이름으로 sigaction
을 호출하여 signal handler를 install한다.
이전에 학습하였던 WIFEXITED
, WEXITSTATUS
와 같이 다양한 marcro들이 존재한다.
WIFEXITED
,WEXITSTATUS
WIFEXITED
는 child가 정상적으로 종료되면0
이 아닌 값이 return되므로,WIFEXITED
로 0이 아님을 확인하면WEXITSTATUS
는 child가 return한 하위 8bit를 확인한다.
WIFSIGNALED
,WTERMSIG
,WCOREDUMP
WIFSIGNALED
는 catch되지 않은 signal로 인해 child가 종료되면0
이 아닌 값을 return하므로,WIFSIGNALED
로 0이 아님을 확인하면WTERMSIG
로 종료를 유발한 signal을 판단한다.
WIFSTOPPED
,WSTOPSIG
WIFSTOPPED
는 child가 현재 중지되어있으면0
이 아닌 값을 return하므로,WIFSTOPPED
fh 0이 아님을 확인하면WSTOPSIG
로 child process를 중지시킨 signal을 판단한다.
WIFCONTINUED
WIFCONTINUED
는 child가 계속되는 경우0
이 아닌 값을 return한다.
이에 대한 간단한 예시는 아래와 같다.
#include <sys/wait.h>
...
if ((pid=wait(&status)) == -1) {
perror(“wait failed”);
exit(1);
}
if (WIFEXITED(status)) {
exit_status = WEXITSTATUS(status);
printf(“Exit status from %d was %d\n”, pid, exit_status);
}
if (WIFSIGNALED(status)) {
sig_no = WTERMSIG(status);
printf(“Signal number %d terminated child %d\n”, sig_no, pid);
}
if (WIFSTOPPED(status)) {
sig_no = WSTOPSIG(status);
printf(“Signal number %d stopped child %d\n”, sig_no, pid);
}
WIFSIGNALED
를 통해 signal에 의한 종료인지를 판단하고, WIFSTOPPED
를 통해 stop된 것인지 확인하는 코드이다. 위처럼 parent는 전달된 종료 상태를 통해 child에게 어떤 일이 일어났는지 파악할 수 있다.
일반적으로 비정상적인 종료는 core dump
로 이어지며, process의 memory에 있는 data는 해당 process의 working directory에 core
로 저장된다. core
은 process가 죽기 전에 남긴 memory 사용 스냅샷이라고 할 수 있으며, Linux에서는 core.pid(pid 위치에 process id)
로 저장되며, WCOREDUMP
로 core dump
가 있는지 판단이 가능하다.
if (WIFSIGNALED(status)) {
sig_no = WTERMSIG(status);
printf(“Signal number %d terminated child %d\n”, sig_no, pid);
if (WCOREDUMP(status))
printf(“...core dump created\n”);
}
process가 signal을 받으면 아래와 같이 3가지 행동을 하게 된다.
1. Default action
- 아무것도 처리하지 않았을 때에 해당하며, 각 signal은 default action이 존재한다. 일반적으로 process를 종료하는 것이며,
SIGUSR1
,SIGUSR2
는 signal을 무시하는 것이 default이다.
2. Ingnore action- process는 signal을 무시하고 계속 실행하며, 이는 예상하지 못한 문제가 발생할 수도 있다.
SIGKILL
,SIGSTOP
은 무시할 수 없는 signal이다.
3. User-defined action- 사용자가 직접 signal에 대해 직접 정의한 function을 call하도록 할 수 있다. 단,
SIGKILL
,SIGSTOP
은 불가능하다.
catch된 signal이 process에 의해 처리될 때는 process는 잠시 중단되며, signal handler 이후 실행을 재개하게 된다. 아래서 sig_handler
는 kernel이 아닌 해당 process 안에 있는 동작이며, 해당 과정에서 다른 signal이 들어오면 block된다.
signal handler에서는 signal이 catch되었을 때, 해당 process가 어디에서 실행되고 있는지 알 수 없으며, sigal handler 역시 언제 호출될지 모르기 때문에 general하게 정의되어야 한다.
signal set
은 사용자가 원하는 signal의 list를 하나의 그룹으로 지정하는 것이다. 이는 sigset_t
로 정의되며, <signal.h>
에 정의되어있다. 모든 signal이 담겨있는 상태 혹은 비어있는 상태에서 시작할 수 있으며, 원하거나 원하지 않는 signal들을 더하고 뺄 수 있다.
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
sigemptyset
, sigfillset
, sigaddset
, sigdelset
은 성공 시 return 0
, error 발생 시 return -1
이다.
sigismember
은 true
일 경우 return 1
, false
일 경우 return 0
, error 발생 시 return -1
이다.
각 함수의 이름에서 알 수 있듯이 각 가능은 아래와 같다.
sigemptyset
: 비어있는 signal set 생성sigfillset
: 모든 signal을 담고 있는 signal set 생성sigaddset
: 특정 signal을 set에 추가sigdelset
: 특정 signal을 set에서 삭제sigismember
: 특정 signal이 set에 존재하는지 확인
이에 대한 간단한 예시는 아래와 같다.