유닉스프로그래밍및실습 9 장. 시그널
1. 시그널개념 시그널생명주기 시그널이발생한다. 커널이해당시그널을쌓아둔다.( 동일한시그널이오는경우하나만 ) 가능한시점에서커널이적절하게처리한다 커널의처리방법 시그널무시 아무런동작을하지않는다 절대무시할수없는시그널 SIGKILL SIGSTOP 시그널을붙잡아처리 현재코드경로를따라가는실행을중단하고, 시그널마다등록된함수로점프 기본동작 대부분프로세스종료 일부는무시 시그널식별자 SIG로시작하는상징적이름 <signal.h> 에서정의 p.392 ~ p.397 시그널이름과특징확인
2. 기초적인시그널관리 (1) signal() 함수 typedef void (*sighandler_t)(int); sighandler_t signal(int signo, sighandler_t handler); signal() 이성공하면시그널을받았을때수행할현재처리기를제거하고, 대신 handler 로명시된처리기등록 handler() 예 void my_handler(int signo); 현재시그널을무시하거나, 기본값을수행하도록지정할수도있음 SIG_DFL SIG_IGN 모든시그널기다리기 #include <unistd.h> int pause(void); 프로세스를잠들게한후붙잡을수있는시그널이오면깨어남
2. 기초적인시그널관리 (2) 예제 (p.400, p.401) 실행과상속 exec() 수행시새로만들어진프로세스는시그널에대해각각기본동작으로설정 부모가이를무시하도록설정한경우에만그대로상속 fork() 는부모, 자식이주소공간을공유하므로동일한시그널설정상속 시그널번호를문자열에사상하기 정적문자열검색방법 ( 가장최선 ) extern const char * const sys_signal[]; BSD 계열 psignal() 비표준 - strsignal()
3. 시그널보내기 kill pid 0 인경우호출한프로세스가속한프로세스그룹전체에게 pid -1 인경우호출한프로세스가시그널을보낼권한이있는모든프로세스에게 pid -1 보다작은경우프로세스그룹 pid 에속한모든프로세스에게 권한 CAP_KILL 예제 p.406 #include <sys/types.h> int kill(pid_t pid, int signo); 자신에게시그널보내기 int raise(int signo); 프로세스그룹전체에게보내기 int killpg (int pgrp, int signo);
4. 재진입가능성 시그널처리기는관련프로세스가중단되었을때무엇을하고있었는지에대한어떤가정도하지않아야한다. 전역자료를절대손대지않는정책이바람직 일부는재진입불가능 재진입가능함수는정적자료를조작해서는안되며, 스택에할당된자료나호출한쪽에서제공한자료만조작해야한다. 재진입보장함수 p. 409 표 9-2
5. 시그널집합 시그널차단과같은기능은시그널들의집합을대상으로함 시그널집합을관리하는함수필요 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 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) 시그널마스크관리함수 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); SIG_SETMASK SIG_BLOCK SIG_UNBLOCK 대기중인시그널조회하기 int sigpending(sigset_t *set); 일군의시그널기다리기 int sigsuspend(const sigset_t *set);
7. 고급시그널관리 sigaction 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); /* 폐기 */ }; 플래그 sa_flags 에 SA_SIGINFO 를설정하면 sa-sigaction 에함수명시 void my_handler(int signo, siginfo_t *si, void *ucontext); sa_flags 값 (p.416) siginfo_t 구조체 (p.417) si_code (p.419)
8. 페이로드와더불어시그널보내기 페이로드와같이보내기 int sigqueue(pid_t pid, int signo, const union sigval value); union sigval { int sival_int; void * sival_ptr; }; 예제 (p.423)
과제 주문 / 서빙프로세스 반복적으로사용자의주문을받아주방프로세스에게전달 주방프로세스에서요리가완료되었다는시그널을받으면사용자에게메시지출력 주방프로세스 주문 / 서빙프로세스에게시그널을받으면요리시작 요리마다일정시간이경과하면요리완료 주문이완료되기전에새로운주문이들어와도처리가가능해야함 등록된주문이없는경우요리소요시간으로알람설정 요리도중새로운주문이들어오는경우, 남은요리시간저장후알람을끄고, 새로운주문추가 힌트 : 추가할때현재진행중인요리를완성하기위해필요한시간과, 앞서등록된요리들의시간값을합산한후소요시간에서그값을뺀값으로등록 중단된남은소요시간으로다시알람설정
과제 ( 계속 ) 주방프로세스 ( 계속 ) 처리할시그널은 2 종류 주문도착 : SIGUSR1 조리완료 : SIGALARM SIGUSR1 처리 Case 1 : 조리중인요리가없는경우 COOK_TIME으로알람설정후대기 Case 2 : 현재조리중인요리가있는경우 알람을끄고남은시간저장 대기큐에주문추가 남은시간으로다시알람설정 SIGALARM 처리 주문 / 서빙프로세스에게 SIGUSR2 보내기 대기중인요리가있으면큐맨처음에있는것을꺼낸후그안에저장된시간으로알람설정후대기 alarm(cook_time); q_hdr 요리완료처리 alarm(q_hdr->n_time); r_time = alarm(0); insert_queue(q_hdr, COOK_TIME, r_time); alarm(r_time); n_time n_time
참고자료 (1) sigsetjmp 와 siglongjmp 시그널처리한후돌아갈위치설정 시그널처리한후루프맨처음으로이동하는식으로시그널을받은지점이아닌, 다른위치로가도록할수있음 goto 와비슷한성격을지니므로될수있으면이용하지않는것이좋다. 다음샘플코드실행결과비교
참고자료 (2) 공통부분 1 #include <sys/types.h> 2 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 }