2011년7월14일_프로세스 복사함수 fork( )를 사용해 자식프로세스를 생성하고 PID와 상태를 확인하고 부모프로세서에서 자식프로세서처리를 하지 않은 경우 좀비프로세서가 되는 이유에 대해 알아보자.



# man 2 fork

이름    
          fork – 자식 프로세스를 만든다.
사용법
          #include <unistd.h>
          pid_t fork(void);
설명
          fork는 부모 프로세스와는 단지 PID와 PPID만이 다른 자식 프로세스를 만든다.
          그리고 자원 사용량을 0으로 맞춘다. 파일락(lock)과 시그널 펜딩(pending)은 상속받지 않는다.
           
          리눅스에서, fork는 copy-on-wite 페이지들을 사용하여 수행되며, fork의 유일한 단점은,
          부모 프로세스의 페이지테이블을 복사하고 자식 프로세스에 대한 task구조체를 만들기 위해,
          필요한 시간과 메모리이다.
반환값
          성공시, 자식 프로세스의 PID가 부모에게 리턴되며, 자식에게는 0이 리턴된다.
          실패할 경우, -1이 부모에게 리턴되며, 자식은 생성되지 않는다….이하 생략



image  

● 메모리공간의 별개 존재
● 부모와 자식 프로세스간 소켓, 파일 공유
● 프로그램 코드 공유

프로그램 코드가 공유되므로 두 프로세스의 처리를 제어문을 사용해 분리할 필요가 있다. 아래와 같이 if문을 추가하자…

   1:  pid_t fork_ret;
   2:   
   3:  fork_ret = fork();         //fork의 리턴값은 성공시 자식프로세스의 PID이고, 실패시 -1이다.
   4:   
   5:  if(fork_ret < 0)             // 실패시 -1을 리턴
   6:  else if(0 == fork_ret)       //자식프로세스인 경우
   7:  else                         //부모프로세스인 경우


● 프로세스의 식별값을 얻는 함수들

# man 2 getpid

이름
          getpid, getppid – 프로세스 식별값(Identification)을 얻는다.
사용법
          #include <unistd.h>
          pid_t getpid(void);
          pid_t getppid(void);
설명
          getpid는 현재 프로세스의 프로세스 ID를 돌려준다.
          (이것은 유일한 임시 파일 이름을 만드는 루틴에서 종종 사용된다.)
          getppid는 현재 프로세스의 부모 프로세스 ID를 돌려준다…이하 생략



● fork_test.c

   1:  // fork_test.c
   2:  #include <sys/types.h>
   3:  #include <unistd.h>
   4:  #include <sys/wait.h>
   5:   
   6:  int global_var = 0;            //전역 변수 선언
   7:   
   8:  int main()
   9:  {
  10:      //pid_t pid;                //pid -> ret로 바꾸어 테스트해보자.
  11:      pid_t fork_ret;
  12:      pid_t child;
  13:      int local_var = 0;            //지역 변수 선언
  14:   
  15:      fork_ret = fork();
  16:   
  17:      if(fork_ret < 0)
  18:      {
  19:          printf("fork() error\n");
  20:          exit(1);
  21:      }
  22:      else if(0 == fork_ret)        //자식프로세스인 경우
  23:      {
  24:          global_var++;
  25:          local_var++;
  26:          printf(" CHILD - my PID : %d  parent's PID : %d \n", getpid(), getppid());
  27:      }
  28:      else                        // 부모 프로세스인 경우
  29:      {
  30:          sleep(10);                //테스트를 위한 10초 대기(없애고 테스트해볼 것)
  31:                                      //sleep()이 없으면 부모프로세스가 먼저 실행되고 죽음.
  32:                                      //부모PID를 못 찾음
  33:          global_var = global_var + 5;
  34:          local_var = local_var + 5;
  35:          printf("PARENT - my PID : %d  child's PID : %d \n", getpid(), fork_ret);
  36:      }
  37:   
  38:      printf("\t global_var : %d \n", global_var);
  39:      printf("\t local_var : %d \n", local_var);
  40:   
  41:      return 0;
  42:  }
  43:          


<실행결과>

image

<소스분석>

image


<질문>

① Q. sleep(10)을 제거하면?
    A. 부모프로세스가 먼저 수행되고 먼저 종료되어, 자식프로세스는 부모프로세스의 PID를 얻을 수 없다.

image  

② # ./fork_test &    ← ‘&’를 붙여 실행하면 실행하는 도중에 명령을 칠 수 있게 된다.
   # ps –u             ← ps명령으로 프로세스의 상태를 알 수 있다.

● sleep(10)이 없는 경우 프로세서가 종료되어 실행되었는지 확인 불가

image


● sleep(10)이 있는 경우 자식프로세서가 좀비프로세서상태로 10초간 존재한다.

image


● 좀비 프로세스
- 프로세스 종료 후 메모리상에서 사라지지 않은 프로세스
● 좀비 프로세스 생성 이유
- 자식 프로세스가 종료하면서 반환된 값 0을 커널이 부모 프로세스에 전달한 후 자식 프로세스를 소멸시킴.
- 반환값을 부모 프로세스에 전달하지 못한 경우 자식 프로세스는 좀비로 존재.
- 부모 프로세스가 커널에게 종료된 자식 프로세스의 리턴값을 전달 요청을 해야만 커널이 리턴값 전달 가능함.

image

자식프로세스가 종료되머 return 0; 커널에 0을 건네주고 좀비프로세스가 된다.
부모프로세스가 커널로부터 그 리턴값을 받는 처리를 하면 자식프로세스는 소멸되어 메모리에 남아 있지 않게 된다.

이런 좀비프로세스를 없애기 위해…


● fork_wait.c

   1:  // fork_wait.c
   2:  #include <sys/types.h>
   3:  #include <unistd.h>
   4:  #include <sys/wait.h>
   5:   
   6:  int global_var = 0;            //전역 변수 선언
   7:   
   8:  int main()
   9:  {
  10:      //pid_t pid;                //pid -> ret로 바꾸어 테스트해보자.
  11:      pid_t fork_ret;
  12:      pid_t child;
  13:      int local_var = 0;            //지역 변수 선언
  14:      int state;
  15:   
  16:      fork_ret = fork();
  17:   
  18:      if(fork_ret < 0)
  19:      {
  20:          printf("fork() error\n");
  21:          exit(1);
  22:      }
  23:      else if(0 == fork_ret)        //자식프로세스인 경우
  24:      {
  25:          global_var++;
  26:          local_var++;
  27:          printf(" CHILD - my PID : %d  parent's PID : %d \n", getpid(), getppid());
  28:      }
  29:      else                        // 부모 프로세스인 경우
  30:      {
  31:          global_var = global_var + 5;
  32:          local_var = local_var + 5;
  33:          printf("PARENT - my PID : %d  child's PID : %d \n", getpid(), fork_ret);
  34:   
  35:          child = wait(&state);    //자식 프로세스의 종료 대기
  36:          printf("\t Child PID = %d \n", child);
  37:          printf("\t return value = %d \n", WEXITSTATUS(state));
  38:   
  39:          sleep(10);        //좀비 프로세스가 있는지 확인할 시간을 벌기 위한 대기
  40:      }
  41:   
  42:      printf("\t global_var : %d \n", global_var);
  43:      printf("\t local_var : %d \n", local_var);
  44:   
  45:      return 0;
  46:  }
  47:          


<실행결과>

image

좀비프로세서가 소멸되었다.



<소스분석>


● 종료된 자식 프로세스의 리턴값 요청 함수

image