Unix 프로그래밍및실습 7 장. 시그널 - 과제보충
응용과제 1 부모프로세스는반복해서메뉴를출력하고사용자로부터주문을받아자식프로세스에게주문내용을알린다. (SIGUSR1) ( 일단주문을받으면음식이완료되기전까지 SIGUSR1 을제외한다른시그널은모두무시 ) timer 자식프로세스는주문을받으면조리를시작한다. ( 일단조리를시작하면음식이완성되기전까지 SIGALARM 을제외한다른시그널은모두무시 ) 일정조리시간이지내면 (Alarm 이용 ) 음식이완성되고, 이를부모프로세스에게알린다. (SIGUSR1) 부모프로세스는음식이완성되면손님에게알린다. ( 메시지출력 ) 부모 ( 주문 / 서빙 ) SIGUSR1 SIGUSR1 자식 ( 조리 ) 주문이없는상태에서 SIGINT 를받는경우메시지출력하고자식을먼저종료시키고부모종료 손님
과제 1 예시 부모 fork while 루프 SIGINT 들어오면자식도종료하도록핸들러등록 화면에주문안내출력 주문이들어오면 signal 처리함수들등록 SIGUSR1 핸들러 SIGUSR1 핸들러등록 SIGINT 무시하도록설정 SIGUSR1 이들어오면결과출력 SIGINT 핸들러설정 SIGINT 핸들러 자식에게 SIGINT 전달 wait 후메시지출력 자식 exec로몸체바꾸기 signal 처리함수들등록 SIGUSR1 핸들러등록 SIGUSR1 핸들러 SIGALRM 핸들러등록 SIGINT 무시하도록설정 조리시간에맞추어 alarm 호출 SIGALRM 핸들러 SIGUSR1 보내기 SIGINT 허용하도록설정
부모프로세스 (1) 1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <signal.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 int child_pid = -1; 8 9 void sigint_handler(int signo) 10 { 11 kill(child_pid, SIGINT); 12 wait(); 13 printf(" 자식종료 \n"); 14 exit(0); 15 } 16 17 void sigusr1_handler(int signo) 18 { 19 printf(" 요리완료 \n"); 20 signal(sigusr1, SIG_DFL); 21 signal(sigint, sigint_handler); 22 } 23 signal 대신 sigaction 사용!
부모프로세스 (2) 24 int main(void) 25 { 26 char ch; 27 28 child_pid = fork(); 29 30 if (child_pid < 0) { 31 perror("fork"); 32 exit(1); 33 } 34 else if (child_pid == 0) { 35 if (execl("./cook.out", "cook.out", NULL) < 0) { 36 perror ("execl"); 37 exit(2); 38 } 39 }
부모프로세스 (3) 40 else { 41 signal(sigint, sigint_handler); 42 43 while (1) { 44 printf(" 주문하시겠습니까? (y)"); 45 if (scanf("%c", &ch) == 1) { 46 if (ch == 'y') { 47 signal(sigint, SIG_IGN); 48 signal(sigusr1, sigusr1_handler); 49 kill (child_pid, SIGUSR1); 50 printf(" 주문전달 \n"); 51 pause(); 52 } 53 } 54 } 55 } 56 } signal 대신 sigaction 사용!
자식프로세스 1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <signal.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 #define COOK_TIME 10 8 9 void sigusr1_handler(int signo); 10 void sigalrm_handler(int signo); 11 12 void sigusr1_handler(int signo) 13 { 14 printf("child: 주문도착 \n"); 15 signal(sigusr1, SIG_IGN); 16 signal(sigint, SIG_IGN); 17 signal(sigalrm, sigalrm_handler); 18 alarm(cook_time); 19 } 20 21 void sigalrm_handler(int signo) 22 { 23 printf("child: 요리완료 \n"); 24 kill (getppid(), SIGUSR1); 25 signal(sigint, SIG_DFL); 26 signal(sigalrm, SIG_DFL); 27 signal(sigusr1, sigusr1_handler); 28 } 29 30 int main(void) 31 { 32 signal(sigusr1, sigusr1_handler); 33 34 while (1) { 35 sleep(60); 36 } 37 } 38
응용과제 2 응용 #1 의경우자식프로세스는한번조리가시작되면다음주문을못받는상황임. 조리가시작된후에도주문을계속받아조리를할수있도록개선하시오. 힌트 SIGALARM 을받기전에 alarm(0) 을하는경우남은시간 ( 초단위 ) 이반환됨. Linked list 를이용하여각요리의남은시간들을관리 첫주문 60 10초후 2번째주문 50 10 30초후 3번째주문 20 10 30 20초후 SIGALARM 수신 10 30
과제 2 예시 부모 자식 fork exec 로몸체바꾸기 while 루프 signal 처리함수들등록 SIGINT 들어오면자식도종료하도록핸들러등록화면에주문안내출력주문이들어오면이미조리중인음식수확인기존에조리중인음식이없는경우 SIGUSR1 핸들러등록 SIGINT 무시하도록설정조리중음식수 1 증가 SIGUSR1 핸들러등록 SIGUSR1 핸들러 SIGALRM 핸들러등록 SIGINT 무시하도록설정 조리시간에맞추어 alarm 호출 SIGALRM 핸들러 SIGUSR1 보내기 SIGINT 허용하도록설정 SIGUSR1 보내기 SIGINT 핸들러 SIGUSR1 이들어오면결과출력 조리중음식수감소 조리중인음식이없는경우 SIGINT 핸들러설정, SIGUSR1 핸들러는기본동작 SIGINT 핸들러 자식에게 SIGINT 전달 wait 후메시지출력
부모프로세스 (1) 1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <signal.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 int child_pid = -1; 8 int active_no = 0; 9 10 void sigint_handler(int signo) 11 { 12 kill(child_pid, SIGINT); 13 wait(); 14 printf(" 자식종료 \n"); 15 exit(0); 16 } 17 18 void sigusr1_handler(int signo) 19 { 20 printf(" 요리완료 \n"); 21 active_no--; 22 fprintf(stderr, "active : %d\n", active_no); 23 if (active_no == 0) { 24 sigset(sigint, sigint_handler); 25 sigset(sigusr1, SIG_DFL); 26 } 27 } 28 sigset 대신 sigaction 사용!
부모프로세스 (2) 29 int main(void) 30 { 31 char ch; 32 33 child_pid = fork(); 34 35 if (child_pid < 0) { 36 perror("fork"); 37 exit(1); 38 } 39 else if (child_pid == 0) { 40 if (execl("./cook.out", "cook.out", NULL) < 0) { 41 perror ("execl"); 42 exit(2); 43 } 44 }
부모프로세스 (3) 45 else { 46 sigset(sigint, sigint_handler); 47 48 while (1) { 49 printf(" 주문하시겠습니까? (y)"); 50 if (scanf("%c", &ch) == 1) { sigset 대신 51 if (ch == 'y') { 52 if (active_no == 0) { sigaction 사용! 53 sigset(sigint, SIG_IGN); 54 sigset(sigusr1, sigusr1_handler); 55 } 56 active_no++; 57 fprintf(stderr, "active : %d\n", active_no); 58 kill (child_pid, SIGUSR1); 59 printf(" 주문전달 \n"); 60 } 61 } 62 } 63 } 64 }
자식프로세스 (1) 1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <signal.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 #define COOK_TIME 10 8 9 typedef struct remaining_list { 10 int secs; 11 struct remaining_list *next; 12 } remaining_list_t; 13 14 remaining_list_t *header = NULL; 15 16 void sigusr1_handler(int signo); 17 void sigalrm_handler(int signo); 18
자식프로세스 (2) 19 void sigusr1_handler(int signo) 20 { 21 remaining_list_t *tmp; 22 remaining_list_t *prev; 23 remaining_list_t *new_el; 24 25 printf("child: 주문도착 \n"); 26 27 new_el = malloc(sizeof(remaining_list_t)); 28 new_el->secs = COOK_TIME; 29 new_el->next = NULL; 30 31 if (header == NULL) { 32 sigset(sigint, SIG_IGN); 33 sigset(sigalrm, sigalrm_handler); 34 header = new_el; 35 } 36 else { 37 header->secs = alarm(0); 38 39 for (tmp = header; tmp; prev = tmp, tmp = tmp->next) { 40 new_el->secs -= tmp->secs; 41 } 42 prev->next = new_el; 43 } 44 fprintf(stderr, "new secs: %d\n", new_el->secs); 45 fprintf(stderr, "new alarm: %d\n", header->secs); 46 alarm(header->secs); 47 } 48
자식프로세스 (3) 49 void sigalrm_handler(int signo) 50 { 51 remaining_list_t *tmp; 52 53 printf("child: 요리완료 \n"); 54 55 tmp = header; 56 if (header->next) { 57 header = header->next; 58 alarm(header->secs); 59 } 60 else { 61 header = NULL; 62 sigset(sigint, SIG_DFL); 63 sigset(sigalrm, SIG_IGN); 64 } 65 66 kill (getppid(), SIGUSR1); 67 free(tmp); 68 } 69 70 int main(void) 71 { 72 sigset(sigusr1, sigusr1_handler); 73 74 while (1) { 75 sleep(60); 76 } 77 }
과제 2 수정 주문이폭주하는경우, 주문간격이 1 초가안되는상황이발생할수있고, 이경우자식프로세스 40 번째줄에서 0 이되는상황이발생할수있음 이를방지하기위해 0 이되는경우 1 초로강제설정하는식으로개선 19 void sigusr1_handler(int signo) 20 {... 생략... 37 else { 38 header->secs = alarm(0); 39 40 for (tmp = header; tmp; prev = tmp, tmp = tmp->next) { 41 new_el->secs -= tmp->secs; 42 } 43 if (new_el->secs <= 0) /* edit for rapid orders */ 44 new_el->secs = 1; 45 46 prev->next = new_el; 47 }... 생략...