[linux] wait() , waitpid() 함수

wait() , waitpid() 함수

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

: 프로세스 종료 후 메모리 상에서 사라지지 않는 프로세스

: 자식 프로세스가 종료하면서 SIGCHLD 시그널을 커널을 통해 부모 프로세스에 전달한 후, 부모가 wait()이나 waitpid()로 자식의 리턴값(성공적으로 종료하면 값은 0)을 받아야 자식 프로세스를 소멸시킬 수 있다.

하지만 반환값을 부모 프로세스에 전달하지 못한 경우 자식 프로세스는 좀비로 존재하고 소멸되지 않는다.(좀비프로세스로 있다가 부모 프로세스가 종료 후에야 소멸된다.)

부모 프로세스가 커널에게 종료된 자식 프로세스의 리턴값을 전달 요청을 해야만 커널은 자식의 리턴값을 부모에게 전달 가능함.(wait(), waitpid()을 통해 전달요청 가능)

좀비 프로세스가 생기는 경우

      # include <stdio.h>
      # include <stdlib.h>
      # include <unistd.h>
      
      int main(){
      
        pid_t fork_ret;
      
        fork_ret = fork();
      
        if(fork_ret >0){
          printf("Parent_my pid: %d Child_pid: %d \n", getpid(), fork_ret);
          sleep(60);
          exit(0);
        }else if(fork_ret == 0){
          printf("Chile_my pid: %d Parent_pid: %d \n", getpid(), getppid());
          exit(0);
        }else{
          perror("fork fail!");
          exit(1);
        }
      
        return 0;
      }
    //실행내용:
    //Parent_my pid: 18195 Child_pid: 18196 
    //Chile_my pid: 18196 Parent_pid: 18195
    //=> 에서 부모 프로세스(sleep(60))을 계속 기다린다. 
    
    //확인해보면 좀비 프로세스가 생겼음을 알 수 있다. 
    //kimdo    17813  0.0  0.0      0     0 pts/1    Z+   16:48   0:00 [zombie] <defunct>

⇒ 좀비 프로세스가 생기지 않으려면 wait()이나 waitpid()를 통해 부모 프로세스가 자식의 리턴값을 받아야 한다.

1. wait()

    # include <sys/types.h>
    # include <sys/wait.h>
    
    pid_t wait(int *status);

⇒ 성공시 종료된 자식 프로세스 ID, 실패시 -1 반환.

wait()의 해결책

       #include <unistd.h>
       #include <sys/wait.h>
       #include <stdio.h>
       #include <stdlib.h>
       
       // 자식 프로세스는 자신이 종료될 때, 부모 프로세스에게 SIGCHLD 시그널을 전달
        한다.
       void foo(int signum) {
         printf("자식 죽음...\n");
         int status;
        wait(&status);
        printf("status: %d\n", status);
       }
      
       int main() {
        signal(SIGCHLD, foo);
        int i;
        pid_t pid;
      
        pid = fork();
        if (pid > 0) {  // 부모 프로세스
          // int status;
          // wait(&status);
         // printf("status: %d\n", status);
      
          for (i = 0; i < 30; ++i) {
            printf("parent process..\n");
            sleep(1);
          }
          exit(0);
      
        } else if (pid == 0) { // 자식 프로세스
          for (i = 0; i < 3; ++i) {
            printf("child process..\n");
            sleep(1);
          }
          exit(0);
      
        } else {
          perror("fork");
          exit(1);
        }
        // fork()가 반환될 때, 자식 프로세스도 fork() 함수를 반환한다.
        printf("hello, world...\n");
      
       }

그러나 또 문제점

     #include <unistd.h>
     #include <sys/wait.h>
     #include <stdio.h>
     #include <stdlib.h>
       
     // 시그널의 한계
     //  => 시그널이 동시에 발생하면, 하나만 처리된다.
     //  => 시그널 핸들러 안에서는 절대 블록되는 함수를 호출하면 안된다.
     //   : 시그널 핸들러안에서 블록되면, 시그널 핸들러를 호출한 프로세스가 대기하게 된다.
     
     void foo(int signum) {
        int status;
        wait(&status);
        printf("status: %d\n", status);
     }
      
      
     int main() {
       signal(SIGCHLD, foo);
       int i;
       pid_t pid;
      
       for (int j = 0 ; j < 30; ++j) {
         pid = fork();
         if (pid == 0) {
            for (i = 0; i < 3; ++i) {
              printf("child process..\n");
              if (j > 5) {
                sleep(1);
              }
            }
            exit(0);
       }
      
       for (i = 0; i < 30; ++i) {
          printf("parent process..%d\n", i);
          sleep(1);
        }
        exit(0);
      
      }

해결책

     void foo(int signum) {
        int status;
        while (wait(&status) > 0) {
          printf("status: %d\n", status);
        }
     }

또 문제점

실행결과

    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    status: 0
    child process..
    status: 0
    status: 0
    child process..
    child process..
    child process..
    status: 0
    status: 0
    child process..
    child process..
    child process..
    child process..
    child process..
    status: 0
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    status: 0
    status: 0
    status: 0
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    parent process..0
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    parent process..1
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    parent process..2
    child process..
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    status: 0
    /*
    위의 status의 값이 연달아 나올때 부모가 수행할 시간이 충분히 있음에도 불구하고 wait()함수로 인해 
    리턴값을 받을 때까지 블록킹 되므로 부모가 수행되지 않는다. 
    */
    parent process..3
    parent process..4
    parent process..5
    parent process..6
    parent process..7
    parent process..8
    parent process..9
    parent process..10
    ^C

2. waitpid()

    # include <sys/types.h>
    # include <sys/wait.h>
    
    pid_t waitpid(pid_t pd, int *status, int options);

⇒ 성공시 종료된 자식 프로세스 ID(경우에 따라 0), 실패시 -1 리턴

     #include <unistd.h>
     #include <sys/wait.h>
     #include <stdio.h>
     #include <stdlib.h>
       
       
      
     void foo(int signum) {
       int status;
       while (waitpid(0, &status, WNOHANG) > 0) {
          printf("status: %d\n", status);
        }
     }
      
     int main() {
       signal(SIGCHLD, foo);
       int i;
       pid_t pid;
      
       for (int j = 0 ; j < 30; ++j) {
          pid = fork();
          if (pid == 0) { //자식 프로세스 
            for (i = 0; i < 3; ++i) {
              printf("child process..\n");
              if (j > 5) {
                sleep(1);
              }
            }
            exit(0);
       }
       // 부모 프로세스 
       for (i = 0; i < 30; ++i) {
          printf("parent process..%d\n",i);
          sleep(1);
       }
        exit(0);
      
    }

실행내용

    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    status: 0
    status: 0
    status: 0
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    status: 0
    status: 0
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    status: 0
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    parent process..0
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    parent process..1
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    child process..
    parent process..2
    child process..
    child process..
    child process..
    status: 0
    status: 0
    status: 0
    status: 0
    parent process..3
    status: 0
    parent process..4
    status: 0
    parent process..5
    status: 0
    parent process..5
    status: 0
    parent process..6
    status: 0
    parent process..7
    status: 0
    status: 0
    parent process..8
    status: 0
    parent process..9
    status: 0
    status: 0
    status: 0
    parent process..10
    status: 0
    parent process..11
    status: 0
    parent process..12
    status: 0
    status: 0
    parent process..13
    status: 0
    parent process..14
    status: 0
    parent process..15
    status: 0
    status: 0
    parent process..16
    status: 0
    /*
    위의 status의 값이 연달아 나올때 부모가 수행할 시간이 충분히 있어서 waitpid()는 리턴값을 받을때까지 
    기다리지않고 바로 0을 리턴해 부모가 사이사이 수행됨을 볼 수 있다. 
    위의 wait() 실행내용과 확연히 다른 양상을 보임을 알 수 있다.  
    */
    parent process..17
    parent process..18
    parent process..19
    parent process..20
    ^C