9장. 시그널
유닉스 프로그래밍 및 실습
1. 시그널 개념
시그널 생명 주기
시그널이 발생한다.
커널이 해당 시그널을 쌓아둔다.(동일한 시그널이 오는 경우 하나만)
가능한 시점에서 커널이 적절하게 처리한다
커널의 처리 방법
시그널 무시
아무런 동작을 하지 않는다
절대 무시할 수 없는 시그널
SIGKILL
SIGSTOP
시그널을 붙잡아 처리
현재 코드 경로를 따라가는 실행을 중단하고, 시그널마다 등록된 함수로 점프
기본 동작
대부분 프로세스 종료
일부는 무시
시그널 식별자
SIG로 시작하는 상징적 이름
<signal.h>에서 정의
p.392 ~ p.397 시그널 이름과 특징 확인
2. 기초적인 시그널 관리(1)
signal() 함수
signal()이 성공하면 시그널을 받았을 때 수행할 현재 처리기를 제거하고, 대신 handler로 명시된
처리기 등록
handler() 예
현재 시그널을 무시하거나, 기본 값을 수행하도록 지정할 수도 있음
SIG_DFL
SIG_IGN
모든 시그널 기다리기
프로세스를 잠들게 한 후 붙잡을 수 있는 시그널이 오면 깨어남
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signo, sighandler_t handler);
void my_handler(int signo);
#include <unistd.h>
int pause(void);
2. 기초적인 시그널 관리(2)
예제 (p.400, p.401)
실행과 상속
exec() 수행시 새로 만들어진 프로세스는 시그널에 대해 각각
기본동작으로 설정
부모가 이를 무시하도록 설정한 경우에만 그대로 상속
fork()는 부모, 자식이 주소공간을 공유하므로 동일한 시그널 설정
상속
시그널 번호를 문자열에 사상하기
정적 문자열 검색 방법(가장 최선)
BSD 계열 – psignal()
비표준 - strsignal()
extern const char * const sys_signal[];
3. 시그널 보내기
kill
pid 0인 경우 호출한 프로세스가 속한 프로세스 그룹 전체에게
pid -1인 경우 호출한 프로세스가 시그널을 보낼 권한이 있는 모든 프로세스에게
pid -1보다 작은 경우 프로세스 그룹 –pid에 속한 모든 프로세스에게
권한 – CAP_KILL
예제 p.406
자신에게 시그널 보내기
프로세스 그룹 전체에게 보내기
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int signo);
#include <signal.h>
int raise(int signo);
#include <signal.h>
int killpg (int pgrp, int signo);
4. 재진입 가능성
시그널 처리기는 관련 프로세스가 중단되었을 때 무엇을 하고 있었는지에 대한 어떤 가정도 하지 않아야 한다.
전역 자료를 절대 손대지 않는 정책이 바람직
일부는 재진입 불가능
재진입 가능 함수는 정적 자료를 조작해서는 안되며, 스택에 할당된 자료나 호출한 쪽에서 제공한 자료만 조작해야 한다.
재진입 보장 함수
p. 409 표 9-2
5. 시그널 집합
시그널 차단과 같은 기능은 시그널들의 집합을 대상으로 함
시그널 집합을 관리하는 함수 필요
추가적인 시그널 집합 함수
리눅스의 비표준 함수
#include <signal.h>
int sigempty(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);
#define _GNU_SOURCE
#include <signal.h>
int sigisempty(sigset_t *set);
int sigorset(sigset_t *dest, sigset_t *left, sigset_t *right);
int sigandset(sigset_t *dest, sigset_t *left, sigset_t *right);
6. 시그널 차단하기
임계 영역(critical section)
일시적인 시그널 차단(blocking)
시그널 마스크 관리 함수
SIG_SETMASK
SIG_BLOCK
SIG_UNBLOCK
대기 중인 시그널 조회하기
일군의 시그널 기다리기
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
#include <signal.h>
int sigpending(sigset_t *set);
#include <signal.h>
int sigsuspend(const sigset_t *set);
7. 고급 시그널 관리
sigaction
플래그 sa_flags에 SA_SIGINFO를 설정하면 sa-sigaction에 함수 명시
sa_flags 값 (p.416)
siginfo_t 구조체 (p.417)
si_code (p.419)
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int); /* 시그널 처리기 혹은 동작 */
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask; /* 차단할 시그널 */
int sa_flags; /* 플래그 */
void (*sa_restorer)(void); /* 폐기 */
};
void my_handler(int signo, siginfo_t *si, void *ucontext);
8. 페이로드와 더불어 시그널 보내기
페이로드와 같이 보내기
예제(p.423)
#include <signal.h>
int sigqueue(pid_t pid, int signo, const union sigval value);
union sigval {
int sival_int;
void * sival_ptr;
};
과제
주문/서빙 프로세스
반복적으로 사용자의 주문을 받아 주방 프로세스에게 전달
주방 프로세스에서 요리가 완료되었다는 시그널을 받으면 사용자에게 메시지 출력
주방 프로세스
주문/서빙 프로세스에게 시그널을 받으면 요리 시작
요리마다 일정 시간이 경과하면 요리 완료
주문이 완료되기 전에 새로운 주문이 들어와도 처리가 가능해야 함
등록된 주문이 없는 경우 요리 소요 시간으로 알람 설정
요리 도중 새로운 주문이 들어오는 경우, 남은 요리시간 저장 후 알람을 끄고, 새로운 주문 추가
힌트 : 추가할 때 현재 진행 중인 요리를 완성하기 위해 필요한 시간과, 앞서 등록된 요리들의 시간 값을 합산한 후 소요시간에서 그 값을 뺀 값으로 등록
중단된 남은 소요시간으로 다시 알람 설정
과제(계속)
주방 프로세스 (계속)
처리할 시그널은 2종류
주문 도착 : SIGUSR1
조리 완료 : SIGALARM
SIGUSR1 처리
Case 1 : 조리 중인 요리가 없는 경우
COOK_TIME으로 알람 설정 후 대기
Case 2 : 현재 조리 중인 요리가 있는
경우
알람을 끄고 남은 시간 저장
대기 큐에 주문 추가
남은 시간으로 다시 알람 설정
SIGALARM 처리
주문/서빙 프로세스에게 SIGUSR2 보내기
대기 중인 요리가 있으면 큐 맨 처음에 있는 것을 꺼낸 후 그 안에 저장된 시간으로 알람 설정 후 대기
alarm(COOK_TIME);
r_time = alarm(0);
insert_queue(q_hdr, COOK_TIME, r_time);
alarm(r_time);
요리완료 처리
alarm(q_hdr->n_time);
q_hdr
n_time n_time
참고자료 (1)
sigsetjmp와 siglongjmp
시그널 처리한 후 돌아갈 위치 설정
시그널 처리한 후 루프 맨 처음으로 이동하는 식으로 시그널을 받은 지점이 아닌, 다른
위치로 가도록 할 수 있음
goto와 비슷한 성격을 지니므로 될 수 있으면
이용하지 않는 것이 좋다.
다음 샘플 코드 실행 결과 비교
참고자료 (2)
공통부분
1 #include <sys/types.h>
2 #include <signal.h>
3 #include <setjmp.h>
4 #include <stdio.h>
5
6 void main_menu();
7 void goback(int);
8
9 sigjmp_buf position;
10
11 /********************************************************************/
12 void main_menu() 13 {
14 int choice;
15
16 printf("######################\n");
17 printf("### 1. Test1 ###\n");
18 printf("### 2. Test2 ###\n");
19 printf("### 3. Test3 ###\n");
20 printf("### 4. Test4 ###\n");
21 printf("######################\n");
22 printf("Choice ? : ");
23 scanf("%d", &choice);
24 printf("Choice = %d\n", choice);
25 printf("$$$$$$$$$$$\n");
26 } 27
참고자료 (3)
28 /***************************************/
29 void goback(int signo) 30 {
31 fprintf(stderr, "\nCatch INT\n");
32
33 siglongjmp(position, 1);
34 } 35
36 /***************************************/
37 int main() 38 {
39 static struct sigaction act;
40
41 act.sa_handler = goback;
42 sigaction(SIGINT, &act, NULL);
43 44
45 while(1) {
46 sigsetjmp(position, 1);
47 main_menu();
48 } 49
50 }
28 /***************************************/
29 void goback(int signo) 30 {
31 fprintf(stderr, "\nCatch INT\n");
32 33 } 34
35 /***************************************/
36 int main() 37 {
38 static struct sigaction act;
39
40 act.sa_handler = goback;
41 sigaction(SIGINT, &act, NULL);
42
43 while(1) {
44 main_menu();
45 } 46
47 }