Signal & Inter-Process Communication Department of Computer Engineering Kyung Hee University. Choong Seon Hong 1
좀비프로세스 2
좀비프로세스 (zombie process) 좀비프로세스란프로세스종료후메모리상에서사라지지않는프로세스 좀비프로세스의생성이유. 자식프로세스는부모프로세스에게실행결과에대한값을반환해야한다. 0 0 자식프로세스커널부모프로세스 좀비 0 0 자식프로세스커널부모프로세스 소멸 3
프로그램예제 zombie.c 좀비프로세스의생성예 int main(int argc, char **argv) { pid_t pid; int data=10; pid=fork(); if(pid<0) printf("fork 실패프로세스 id : %d \n", pid); printf("fork 성공프로세스 id : %d \n", pid); if(pid==0) /* 자식프로세스라면 */ data+=10; else /* 부모프로세스라면 */ { data-=10; sleep(20); /* 20 초동안정지상태에들어간다 */ printf("data : %d \n", data); return 0; 4
좀비프로세스의소멸 1 소멸방법 부모프로세스에서커널에게자식프로세스의반환값을요구한다 wait 함수의사용장점 : 사용하기간단하다. 단점 : 무한대기상태에빠질수있다 #include <sys/types.h> #include <sys/wait.h> pid_t wait(int * status) 함수호출시종료된자식프로세스가있으면그프로세스가리턴한값을읽어들인다.( 함수호출시전달되는포인터를통해 ) 5
좀비프로세스의소멸 1 종료상태를확인할수있는매크로함수 status 포인터가가리키는변수에저장된값을통해서원하는정보만리턴받을수있도록구현되어있는매크로함수 매크로함수 리턴값 WIFEXITED(status) 정상종료를했을경우 0 을반환한다. WEXITSTATUS(status) 종료시에 return 하거나 exit 함수의인자로넘겨진값을반환한다. 6
프로그램예제 wait.c 좀비프로세스소멸의예 1 pid=fork(); if(pid<0) printf("fork 실패프로세스 id : %d \n", pid); printf("fork 성공프로세스 id : %d \n", pid); if(pid==0) /* 자식프로세스라면 */ data+=10; else /* 부모프로세스라면 */ { data-=10; child=wait(&state); /* 자식프로세스의종료대기 */ printf(" 자식프로세스 ID = %d \n", child); printf(" 리턴값 = %d \n", WEXITSTATUS(state)); sleep(20); /* 프로세스상태확인을위해서 */ 7
좀비프로세스의소멸 2 소멸방법 부모프로세스에서자식프로세스의반환값을요구한다 waitpid 함수의사용 wait 함수가지니고있는무한대기상태의문제점을해결 #include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid, int * status, int options) pid : 종료확인을원하는자식프로세스의 ID, -1 이들어가면 wait 함수처럼작동 options : sys/wait.h에정의, WNOHANG 상수를인자로전달하게되면이미종료한자식프로세스가없는경우에대기상태로들어가지않고바로리턴 8
좀비프로세스소멸의예 2 int main(int argc, char **argv) { ㅇ pid_t pid, child; int data=10; int state; pid=fork(); 프로그램예제 waitpid.c if(pid<0) printf("fork 실패, 프로세스 id : %d \n", pid); printf("fork 성공, 프로세스 id : %d \n", pid); if(pid==0) /* 자식프로세스라면 */ { data+=10; sleep(10); /* 종료를 10 초지연 */ else /* 부모프로세스라면 */ { data-=10; do{ sleep(3); puts("3 초대기 "); child=waitpid(-1, &state, WNOHANG); while(child == 0); /* 종료한자식프로세스상태정보출력 */ printf("child process id = %d, return value = %d \n\n", child, WEXITSTATUS(state)); printf("data : %d \n", data); return 0; 9
시그널핸들링과좀비프로세스 10
시그널이란? 시그널 (Signal) 핸들링 시스템내의특정상황발생을알리기위해서커널이전달하는신호프로세스에서어떤이벤트가발생한것을다른프로세스에게알리는도구 시그널핸들러 적절한처리를해주는함수 시그널핸들링 시그널이발생함에따라이에대한적절한처리를해주는것. 즉, 시그널발생을감지하여그시그널에적합한처리 ( 함수호출 ) 를해주는과정 Operating System 1. 특정상황발생 Signal 2. 시그널전송 3. 시그널처리함수호출 Process 11
시그널 (Signal) 의종류 시그널 발생상황 SIGALRM 시간을예약 (alarm 함수사용 ) 해놓고그시간이되었을경우발생. SIGINT 인터럽트 (interrupt) 발생을알린다. 여기서인터럽트는 Ctrl-C 를누른경우발생한다. SIGCHLD 자식프로세스가종료된경우발생한다. 12
signal 함수 signal 함수를이용한시그널핸들링 시그널과시그널핸들러를연결해주는기능을한다 #include <signal.h> void (*signal(int signum, void (*func)(int)))(int); 인자 signum에해당하는시그널을수신시이를처리하기위한함수를 handler 인자에등록 Handler 인자의종류 signal_handler : 시그널핸들러함수명 SIG_IGN: 시그널을무시하도록설정 13
예제확인 1 프로그램예제 sigint.c void handler(int sig); int main(int argc, char **argv) { int state; int num=0; signal(sigint, handler); while(1) { printf("%d : 대기중 \n", num++); sleep(2); if(num>5) break; return 0; /* 시그널처리함수 */ void handler(int sig) { signal(sigint, handler); printf(" 전달된시그널은 %d \n", sig); 14
sigaction 함수 sigaction 함수를이용한시그널핸들링 시그널과시그널핸들러를연결해주는기능을한다 #include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); signum: signal 함수와마찬가지로시그널의종류를인자로전달 act: 새로등록할시그널핸들러정보로초기화된 sigaction 구조체변수의포인터를인자로전달 oldact : 이전에등록되었던시그널핸들러의포인터를얻고자할때사용하게되는인자 15
sigaction 함수를이용한시그널핸들링 sigaction 구조체 struct sigaction { void (*sa_handler)(int) sigset_t sa_mask; int sa_flags; sa_handler : 함수포인터. 이곳에시그널을처리하는시그널핸들러의포인터를대입 sa_mask : 시그널핸들러함수가실행되는동안에블로킹될시그널들을설정하는요소 sa_flags : 시그널핸들링하는데있어서필요한옵션을설정, 기본적으로 0 16
SIGALRM 시그널핸들링예제 alarm 함수 #include <unistd.h> unsigned int alarm(unsigned int seconds) 반환값 0 혹은 SIGALRM 시그널이발생하기까지남아있는초단위시간반환 seconds SIGALRM 시그널발생을초단위로예약. 0을입력할경우이전예약을취소 17
예제확인 2 프로그램예제 sigint2.c sigalarm.c zombie_handler.c 실행결과 18
프로세스간통신 19
프로세스간통신 프로세스간통신의정의독립된프로세스간에데이터를주고받는행위를의미함 프로세스간통신의문제점독립된프로세스는공유하는메모리가존재하지않기때문에메모리를공유해서데이터를주고받는것은불가능! 해결책운영체제는서로독립된프로세스들이데이터를주고받을수있도록 파이프 라는것을제공한다. 20
파이프생성함수 #include <unistd.h> int pipe(int fd[2]); Parent Process (or Child) fd[1] PIPE fd[0] Child Process (or Parent) 21
pipe 생성예제 pipe1.c state = pipe(fd); if(state == -1) { puts("pipe() error"); exit(1); pid = fork(); if(pid == -1){ puts("fork() error"); exit(1); else if(pid==0){ write(fd[1], "Good\n", 6); else{ read(fd[0], buffer, BUFSIZE); puts(buffer); 22
파이프의특성 파이프는 fork 함수에의해서복사되지않는다파이프의입출력을의미하는파일디스크립터가복사되는것이다 파이프는방향성이존재하지않는다 Parent Process (or Child) fd[1] fd[0] 출구 입구 fd[1] fd[0] Child Process (or Parent) 23
파이프의생성과프로세스간통신 커널영역 1. pipe() fd[1] 부모프로세스 4. fork() 자식프로세스 3. fd[0], fd[1] P I P E 2. pipe 생성 fd[0] 프로세스영역 24
파이프문제점 Parent Process fd[1] fd[0] 출구 입구 fd[1] fd[0] Child Process 25
pipe 생성예제 2 pipe2.c state = pipe(fd); if(state == -1){ puts("pipe() error"); exit(1); pid = fork(); if(pid == -1){ puts("fork() error"); exit(1); else if(pid==0){ /* 자식프로세스의경우 */ write(fd[1], "Good!", 6); sleep(2); <- 주석처리한다면 read(fd[0], buffer, BUFSIZE); printf(" 자식프로세스출력 : %s \n\n", buffer); else{ /* 부모프로세스의경우 */ read(fd[0], buffer, BUFSIZE); printf(" 부모프로세스출력 : %s \n", buffer); write(fd[1], "Really Good", 12); sleep(3); /* 큰의미없음 : 출력좋게하려고 */ 26
양방향통신을위한파이프의생성 하나의파이프는하나의용도로만사용한다 A 프로세스에서 B 프로세스로데이터전송하기위한파이프하나 B 프로세스에서 A 프로세스로데이터전송하기위한파이프하나 fd1[1] fd1[0] Parent Process PIPE PIPE Child Process fd2[0] fd2[1] 27
pipe 생성예제 3 pipe3.c if(pipe(fd1)==-1 pipe(fd2)==-1) { puts("pipe() error"); exit(1); pid = fork(); Parent Process if(pid ==-1){ puts("fork() error"); exit(1); else if(pid==0){ write(fd1[1], "Good!", 6); read(fd2[0], buffer, BUFSIZE); printf(" 자식프로세스출력 : %s \n\n", buffer); else{ read(fd1[0], buffer, BUFSIZE); printf(" 부모프로세스출력 : %s \n", buffer); write(fd2[1], "Really Good", 12); sleep(1); Really Good fd2[1] fd1[0] PIPE PIPE fd2[0] fd1[1] Child Process 28
pipe 생성예제 3 프로그램예제 pipe3.c 실행결과 29