리눅스 프로그래밍 - 9주차

Lellow_Mellow·2022년 10월 26일
1
post-thumbnail

🔔 학교 강의를 바탕으로 개인적인 공부를 위해 정리한 글입니다. 혹여나 틀린 부분이 있다면 지적해주시면 감사드리겠습니다.

Signal and Signal Processing

6.1 Signal

Signal Concept

signalsoftware interrupts로, 비동기 이벤트를 처리한다.

$ gcc verybigprog.c
^C /* Ctrl-C, interrupt key) */

사용자가 ctrl+c를 입력하면 kernel이 process들에게 SIGINT라는 signal을 보내며, gcc가 이 신호를 받으면 SIGINT와 관련된 기본 작업을 수행하고 종료한다. signal은 특정한 일이 발생했음을 알려주는 용도이며, error와는 다르다는 점을 명심해야한다.

Signal Name & Generation

모든 signal에는 사용 목적에 따른 이름이 존재하며, SIG로 시작한다. 이는 모두 <signal.h>에 양의 정수로 정의되며, 다양한 조건이 signal을 생성할 수 있다. 이에 대한 간단한 예는 아래와 같다.

  • ctrl+C : 터미널 생성 신호
  • 0으로 divide 등 하드웨어 예외
  • process에서 system call 종료
  • shell에서의 kill

kill -l command로 signal 목록 확인이 가능하며, 31까지는 동일하고 이후는 system에 따라 달라진다. 총 64개로 이루어진다.

Signal 예시

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

signal은 event process에 대한 software 알림이다. 특정 신호를 발생시키는 event가 발생하면 signal이 발생하며, 이를 알리는 역할을 한다. process가 signal을 받으면, 이에 따른 특정 행동을 취해야 하며, signal의 수명은 생성과 전달 사이의 간격에 해당한다.

생성되었지만 아직 전달되지 않은 signal은 block된 것이며, 이를 pending되었다고 한다. 다른 signal을 처리하는 도중에 signal이 오면, 이전 signal을 처리하기 전까지 block된다. program은 사용자가 작성한 function 이름으로 sigaction을 호출하여 signal handler를 install한다.

Exit Status Macro

이전에 학습하였던 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하므로, WIFSTOPPEDfh 0이 아님을 확인하면 WSTOPSIG로 child process를 중지시킨 signal을 판단한다.

WIFCONTINUED

WIFCONTINUED는 child가 계속되는 경우 0이 아닌 값을 return한다.

이에 대한 간단한 예시는 아래와 같다.

Normal and Abnormal Termination

#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)로 저장되며, WCOREDUMPcore 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”);
}

6.2 Signal Handling

Signal Handling

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은 불가능하다.

Signal Handling : User-Defined Action

catch된 signal이 process에 의해 처리될 때는 process는 잠시 중단되며, signal handler 이후 실행을 재개하게 된다. 아래서 sig_handler는 kernel이 아닌 해당 process 안에 있는 동작이며, 해당 과정에서 다른 signal이 들어오면 block된다.

signal handler에서는 signal이 catch되었을 때, 해당 process가 어디에서 실행되고 있는지 알 수 없으며, sigal handler 역시 언제 호출될지 모르기 때문에 general하게 정의되어야 한다.

Signal Set

signal set은 사용자가 원하는 signal의 list를 하나의 그룹으로 지정하는 것이다. 이는 sigset_t로 정의되며, <signal.h>에 정의되어있다. 모든 signal이 담겨있는 상태 혹은 비어있는 상태에서 시작할 수 있으며, 원하거나 원하지 않는 signal들을 더하고 뺄 수 있다.

Functions for Signal Set

#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이다.

sigismembertrue일 경우 return 1, false일 경우 return 0, error 발생 시 return -1이다.

각 함수의 이름에서 알 수 있듯이 각 가능은 아래와 같다.

  • sigemptyset : 비어있는 signal set 생성
  • sigfillset : 모든 signal을 담고 있는 signal set 생성
  • sigaddset : 특정 signal을 set에 추가
  • sigdelset : 특정 signal을 set에서 삭제
  • sigismember : 특정 signal이 set에 존재하는지 확인

이에 대한 간단한 예시는 아래와 같다.

profile
festina lenta

0개의 댓글