410 likes | 811 Views
Linux System Programming. Lecture #7 – 시그널 (Signal) 처리. 신호 (Signal) (1). 신호 (Signal) 정의 : 비동기적인 사건 (event) 들의 발생을 프로세스에게 알려주는 기능 프로세스간의 통신 기법 중에 하나 사용자 프로세스 간에 또는 커널과 사용자 프로세스 간에 특정 사건이 발생하였음을 알리는 방법으로 사용 소프트웨어 인터럽터 개념 신호의 유형 : 프로세스 종료 관련 신호 프로세스 예외 관련 신호 시스템 호출 관련 신호
E N D
Linux System Programming Lecture #7 –시그널(Signal) 처리
신호(Signal) (1) • 신호(Signal) 정의: • 비동기적인 사건(event)들의 발생을 프로세스에게 알려주는 기능 • 프로세스간의 통신 기법 중에 하나 • 사용자 프로세스 간에 또는 커널과 사용자 프로세스 간에 특정 사건이 발생하였음을 알리는 방법으로 사용 • 소프트웨어 인터럽터 개념 • 신호의 유형: • 프로세스 종료 관련 신호 • 프로세스 예외 관련 신호 • 시스템 호출 관련 신호 • 복구할 수 없는 상태 관련 신호 • 예기치 않은 에러 관련 신호 • 사용자 프로세스 유발 신호 • 터미널 작업 관련 신호 • 프로세스 실행 추적 관련 신호 Linux System Programming
신호(Signal) (2) • 신호(Signal) 처리: • 프로세스가 커널 모드에서 사용자 모드로 전환될 때에 커널이 신호 여부를 확인하고 처리한다 • 프로세스가 사용자 모드에서 실행 중에 발생한 신호는 커널이 인터럽터 처리를 종료한 후에 돌아올 때에 커널에 의해 처리된다 Linux System Programming
신호의 종류 (1) • 신호의 종류: • 헤드 파일 <signal.h>에 지원되는 시그널을 정의 Linux System Programming
신호의 종류 (2) • 신호의 종류 –처리 방식에 따른 유형: • 신호의 처리 방식 유형 • Exit –정상 종료 • Core –비정상적인 종료 + 코더 덤프 • Stop –무조건 종료 • Ignore –신호 무시 • 대부분의 신호는 신호가 발생하면 프로세스를 정상 종료시킨다(Exit). • 프로세스가 종료할 때에 종료를 유발한 시그널 종류를 전달하기 위해 프로세스 반환값의 상위 8비트에는 시그널 번호를 , 하위 8비트에는 0을 설정하여 반환한다 • SIGQUIT, SIGILL, SIGTRAP, SIGSYS, SIGFPE 등의 신호는 비정상 종료를 초래하고, 이로 인해 코어 덤프가 생성된다(Core). • 특정 시그널에 대해서는 프로세스 시작 시점에 무시하도록 설정할 수 있다(Ignore) Linux System Programming
신호의 설정과 수신 (1) • 신호의 설정: • 모든 신호에 대해서는 기본적인(default) 처리 방식이 설정되어 있다(Exit/Core/Stop/Ignore) • 프로세스는 시스템 호출을 이용하여 특정 신호에 대해 처리 방식을 변경할 수 있다 • 프로그램 실행 과정에서 예기치 못한 신호로 인해 문제가 발생할 수 있다 • signal, sigset, sigaction 시스템 호출을 사용하여 신호의 세가지 처리 방식(신호의 기본적인 동작(SIG_DFL), 신호의 무시(SIG_IGN), 신호의 수신) 중에서 하나를 지정할 수 있다 • 신호의 수신 모드인 경우에는 새로운 신호 처리 함수(signal handler)을 지정할 수 있다 Linux System Programming
신호의 설정과 수신 (2) • 신호의 설정: signal() Linux System Programming
신호의 설정과 수신 (3) • 예제 7-1 • 신호 SIGINT을 받았을 때에 현재 출력 중인 파일을 닫은 후에 프로그램 종료하도록 한다 #include <stdio.h> #include <signal.h> #define OUTPUT "Primes" #define MAXNUM 10000 int count; FILE *fptr; main() { int number, divisor; void sigcatch(); fptr = fopen(OUTPUT,"w"); signal(SIGINT, sigcatch); Linux System Programming
신호의 설정과 수신 (4) for(number =1; number <= MAXNUM; number++) { for(divisor =2; divisor < number; divisor++) if((number % divisor) == 0) break; if(divisor == number) { fprintf(fptr,"%d\n",number); count++; } } fclose(fptr); } void sigcatch() { printf("%d primes computed\n", count); fclose(fptr); exit(1); } Linux System Programming
신호의 설정과 수신 (5) • 예제 7-2 • 예제 7-1에서 여러 번의 SIGINT 신호를 받을 수 있고, SIGQUIT 신호르 수신하면 프로그램을 종료하도록 수정하여라. : : void sigcatch(sig) int sig; { signal(sig,SIG_IGN); printf("%d primes computed\n", count); if(sig == SIGQUIT) { fclose(fptr); exit(1); } signal(sig,sigcatch); } Linux System Programming
신호의 설정과 수신 (6) • 예제 7-4 • 예제 6-17 프로그램에서 자식 프로세스가 수행되는 동안 부모 프로세스가 SIGINT나 SIGQUIT 신호에 의해 비정상 종료가 되지 않도록 이들 신호를 무시하는 프로그램을 작성하여라. #include <signal.h> command(cmd) /* run a shell command from C program */ char *cmd; { int chpid, fork(); int w, status, wait(); void (*istat) (), (*qstat) (); if ((chpid = fork() ) == 0) { execlp("sh","sh", "-c", cmd, (char *) 0); exit(127); } istat = signal(SIGINT, SIG_IGN); qstat = signal (SIGQUIT,SIG_IGN); Linux System Programming
신호의 설정과 수신 (7) while ((w = wait(&status)) != chpid && w != -1) ; /* null */ signal(SIGINT, istat); signal(SIGQUIT, qstat); return((w != -1) ? (status >> 8) : -1); } main() { printf("%d\n", command("date > Date; cat Date")); printf("%d\n", command("date -u")); printf("%d\n", command("sleep 10; echo done")); } Linux System Programming
신호의 송신 (1) • 신호의 송신: kill() Linux System Programming
신호의 송신 (2) • 예제 7-5 • 여러 개의 자식 프로세스를 생성하여 수행시키고 부모 프로세스가 신호를 보내어 종료시키는 프로그램을 작성하여라 • 자식 프로세스는 난수를 발생시켜 그 시간만큼 수면하도록 한다 // file name: ex7-5a.c // #include <signal.h> #define NUMCHILD 20 main(int argc, char *argv[]) { int child1, child2, fork(); int i, pid, chpid[NUMCHILD], status, wait(); for(i=0; i< NUMCHILD; i++) { if((pid = fork()) == 0) execlp("ex7-5b","ex7-5b",(char *) 0); chpid[i] = pid; } Linux System Programming
신호의 송신 (3) printf("parent: wating for children\n"); while((pid = wait(&status)) != -1) { printf("child %d: terminated, status:%d\n",pid,status); if((status>>8) !=0) { for(i=0; i<NUMCHILD; i++) kill(chpid[i],SIGTERM); } } printf("parent: all children terminated\n"); exit(0); } Linux System Programming
신호의 송신 (4) // file-name: ex7-5b.c // main(int argc, char *argv[]) { int rand(); void srand(); srand((unsigned) getpid()); sleep((rand()*197 %100) +1); exit(1); } Linux System Programming
타이머 신호: alarm (1) • 타이머 신호 지정: alarm() • 프로세스의 알람 타이머를 조작하는 시스템 호출 • 지정된 시간이 경과한 후에 SIGALRM 신호가 발생 Linux System Programming
타이머 신호: alarm (2) • 타이머 신호 지정: alarm() • alarm 시스템 호출은 알람 타이머의 시간을 지정하고 타이머를 동작시킨다. • 이미 알람 타이머가 동작 중인 경우에는 남아 있는 시간을 반환한다 • exec 시스템 호출을 수행하더라도 수행되며, fork 시스템 호출에 의해 생성된 자식 프로세스에는 상속되지 않는다 • alarm 시스템 호출은 누적되지 않으며, 두 번이상 호출되면 마지막 호출에 지정된 시간이 타이머에 새로이 지정된다 • 하나의 프로그램 내에서 어떤 작업이 수행되는 시간에 한계를 두려고 할 때에 유용하다 Linux System Programming
타이머 신호: alarm (3) • 예제 7-7 • 자식 프로세스를 생성하여 리눅스 명령어를 실행시키고 부모 프로세스에서는 알람 시간을 설정하여 감시하도록 하며, 주어진 시간에 자식 프로세스가 종료하지 않으며 SIGKILL 신호를 보내어 강제 종료하도록 하는 프로그램을 작성하여라. #include <signal.h> #include <errno.h> #define TIMEOUT 10 int pid; void sigalarm(); main(int argc, char *argv[]) { extern int errno; int status; if((pid = fork()) == 0) { execvp(argv[1], &argv[1]); perror(argv[1]); exit(127); } Linux System Programming
타이머 신호: alarm (4) signal(SIGALRM, sigalarm); alarm(TIMEOUT); while(wait(&status) == -1) { if(errno == EINTR) { errno = 0; printf("%s: timed out\n",argv[1]); } else { perror(argv[0]); break; } } printf("time remaining: %d\n", alarm(0)); exit(status >>8); } void sigalarm(sig) int sig; { kill(pid,SIGKILL); } Linux System Programming
프로세스의 정지 (1) • 프로세스의 정지: pause() • 프로세스로 하여금 SIGALRM 등의 신호가 도착할 때까지 시스템 자원을 낭비하지 않도록 수행을 중지시키는 시스템 호출 • pause 시스템 호출을 수행한 프로세스는 새로운 신호가 도착하면 중단 지점부터 실행을 재개한다 Linux System Programming
프로세스의 정지 (2) • 예 7-9 • alarm과 pause 시스템 호출을 사용하여 C 라이브러리 함수 sleep()과 같은 기능을 수행하는 프로그램을 작성하여라 • Signal 시스템 호출과 setjmp, longjmp 함수를 이용하여 구현. #include <signal.h> #include <setjmp.h> static jmp_buf env; static void sigcatch(int); int mysleep(int); main(int argc, char *argv[]) { int atoi(), unslept, mysleep(); void sigint(); signal(SIGINT, SIG_IGN); unslept = mysleep(atoi(argv[1])); printf("remaining time: %d\n",unslept); } Linux System Programming
프로세스의 정지 (3) int mysleep(int seconds) { void (*astat)(); int unslept = seconds; astat = signal(SIGALRM, sigcatch); if(setjmp(env) == 0) { alarm(seconds); pause(); } unslept = alarm(0); signal(SIGALRM, astat); return(unslept); } static void sigcatch(int sig) { longjmp(env,1); } Linux System Programming
프로세스의 수면 • 프로세스의 수면: sleep() • 프로세스를 일정 시간 동안에 정지시키는 시스템 호출 Linux System Programming
신호의 관리 (1) • 신호 관리 시스템 호출 • 리눅스 시스템 V Rel. 3부터 새로운 신호 관리 시스템 호출을 지원 Linux System Programming
신호의 관리 (2) • 신호 관리 시스템 호출 Linux System Programming
신호의 관리 (3) • 예제 7-10 • 예제 7-2 프로그램을 sigset 시스템 호출을 개선하여라. #include <stdio.h> #include <signal.h> #define OUTPUT "Primes" #define MAXNUM 10000 int count; FILE *fptr; main() { int number, divisor; void sigcatch(); fptr = fopen(OUTPUT,"w"); sigset(SIGINT, sigcatch); sigset(SIGQUIT, sigcatch); Linux System Programming
신호의 관리 (4) for(number =1; number <= MAXNUM; number++) { for(divisor =2; divisor < number; divisor++) if((number % divisor) == 0) break; if(divisor == number) { fprintf(fptr,"%d\n",number); count++; } } fclose(fptr); } void sigcatch(sig) int sig; { signal(sig,SIG_IGN); printf("%d primes computed\n", count); if(sig == SIGQUIT) { fclose(fptr); exit(1); } } Linux System Programming
신호의 관리 (5) • 예제 7-12 • Sigset()dhk sigrelse() 시스템 호출을 이용하여 비지역 분기(non-local GOTO) 프로그램을 작성하여라 #include <signal.h> #include <setjmp.h> #define MAXLINE 256 jmp_buf env; main() { void sigcatch(); char line[MAXLINE]; sigset(SIGINT, sigcatch); setjmp(env); Linux System Programming
신호의 관리 (6) for(;;) { printf("Prompt: "); gets(line); /* * process the input here */ } } void sigcatch(sig) int sig; { int ch; printf("\nDo you want to exit (y or n)? "); ch = getchar(); getchar(); if(ch == 'y') exit(1); sigrelse(SIGINT); longjmp(env,1); } Linux System Programming
신호의 관리 (7) • 예제 7-13 • 인터럽트 신호(SIGINT)가 수신되기를 기다리는 프로그램을 sigpause 시스템 호출을 이용하여 작성하여라 #include <signal.h> int flag; main() { void sigcatch(); sigset(SIGINT, sigcatch); sigset(SIGQUIT, sigcatch); while(1) { /* * processing here */ sleep(3); Linux System Programming
신호의 관리 (8) sighold(SIGINT); if(flag == 0) { printf("pausing for SIGINT\n"); sigpause(SIGINT); } flag = 0; sigrelse(SIGINT); } } void sigcatch(sig) int sig; { printf("entring sigcatch()\n"); if(sig == SIGQUIT) exit(1); flag ++; } Linux System Programming
신호의 관리 (9) • 예제 7-15 • 여러 개의 파일을 라운드-로빈 순서로 읽는 프로그램을 작성하여라 • 폴링(polling) • 다중화된 입력 기법 • 한 파일로 부터 한 줄을 읽은 후에 다음 파일에서 읽기를 시도하는 방법으로 구현 가능 • 만일 TIME_OUT으로 주어진 시간 동안 읽을 수 없으면 다음 파일을 읽도록 한다 Linux System Programming
신호의 관리 (10) #include <stdio.h> #include <fcntl.h> #include <signal.h> #include <errno.h> #define TIME_OUT 5 /* seconds before switching to a different */ #define MAXOPEN 20 /* maximum number of open files */ int nfiles; char *fname[MAXOPEN]; int fd[MAXOPEN]; int alrmflg; /* set by alrmtrp(), reset on return of read in rdfile */ extern int errno; main(argc, argv) int argc; char *argv[]; { void rdfiles(); if (argc < 2) { fprintf(stderr, "usage: %s filename [filename...]\n", argv[0]); exit(1); } Linux System Programming
신호의 관리 (11) nfiles = 0; while (--argc) { fname[nfiles] = argv[nfiles + 1]; /* +1 to skip command */ if ((fd[nfiles] = open(fname[nfiles], O_RDONLY)) == -1) { fprintf(stderr, "%s: cannot open %s\n", argv[0], fname[nfiles]); exit(2); } ++nfiles; if (nfiles >= 20) { fprintf(stderr, "%s: too mane file names", argv[0]); exit(3); } } rdfiles(); } void wrline(fnam, lp) register char *fnam, *lp; { char buf[256]; sprintf(buf, "%s: %s", fnam, lp); write(1, buf, strlen(buf)); /* print line and file name */ } Linux System Programming
신호의 관리 (12) void rdfiles() { register int i; int opfiles, n; char line[256]; void alrmtrp(); void wrline(); signal(SIGALRM, alrmtrp); opfiles = nfiles; /* number of open files */ while (opfiles) { /* as long as there are open files */ for (i = 0; i < nfiles; i++) { if (fd[i] == -1) continue; alarm(TIME_OUT); line[0] = '\0'; if ((n = read(fd[i], line, sizeof(line))) <= 0) { if (errno == EINTR && alrmflg) { /* alrmflg set in alrmtrp routine */ errno = alrmflg = 0; continue; /* try next line */ } else { close (fd[i]); /* assume eof */ fd[i] = -1; /* don't read anymore */ --opfiles; } } Linux System Programming
신호의 관리 (13) alarm(0); /* reset alarm */ line[n] = '\0'; wrline(fname[i], line); } } } void alrmtrp(sig) int sig; { signal(sig, alrmtrp); alrmflg = 1; } Linux System Programming
신호의 관리 (14) • 예제 7-16 • 예제 6-20 프로그램을 수정하여 인터럽트 신호(SIGINT)에 의해 프로그램이 종료되지 않도록 하여라. • 백그라운드로 수행되는 명령이 SIGINT 또는 SIGQUIT 신호에 의해 종료되지 않도록 수정하여라. • 교재 pp.298~301 프로그램 참조 Linux System Programming