프로세스와 관련한 더 많은 정보와 예제를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.
https://reakwon.tistory.com/233
wait, waitpid 함수는 아래의 header파일을 include해야합니다. 이 두 함수는 부모프로세스가 자식프로세스를 기다리는 공통점이 있습니다만 waitpid는 어떤 자식을 기다릴지 조금 더 구체적으로 지정할 수 있습니다.
이 두 함수의 반환값은 성공시 process id를 반환하고 오류시 -1을 반환합니다.
#include <sys/types.h>
#include <sys/wait.h>
wait 함수
pid_t wait(int *wstatus);
성공시 프로세스의 pid를 반환합니다. wstatus는 종료상태를 알 수 있는데, 굳이 종료상태를 알 필요가 없다면 NULL을 전달해주세요.
wstatus를 알 수 있는 방법은 <sys/wait.h>에 정의되어있는 매크로들을 사용하면 됩니다. 아래는 그 매크로들과 설명입니다.
매크로 | 설명 |
WIFEXITED(wstatus) | returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main(). |
WEXITSTATUS(wstatus) | returns the exit status of the child. This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main(). This macro should be employed only if WIFEXITED returned true. |
WIFSIGNALED(wstatus) | returns true if the child process was terminated by a signal. |
WTERMSIG(wstatus) | returns the number of the signal that caused the child process to terminate. This macro should be employed only if WIFSIGNALED returned true. |
WCOREDUMP(wstatus) | returns true if the child produced a core dump. This macro should be employed only if WIFSIGNALED returned true. This macro is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS). Therefore, enclose its use inside #ifdef WCOREDUMP ... #endif. |
WIFSTOPPED(wstatus) | returns true if the child process was stopped by delivery of a signal; this is possible only if the call was done using WUNTRACED or when the child is being traced (see ptrace(2)). |
WSTOPSIG(wstatus) | returns the number of the signal which caused the child to stop. This macro should be employed only if WIFSTOPPED returned true. |
WIFCONTINUED(wstatus) | (since Linux 2.6.10) returns true if the child process was resumed by delivery of SIGCONT |
그렇다면 이제 예제를 보도록 하겠습니다.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
int status, cpid, endpid;
if((cpid=fork())==0){
printf("\tchild process:%d\n",getpid());
sleep(10);
printf("\tchild end\n");
exit(10);
}
endpid=wait(&status);
printf("end pid : %d\n",endpid);
printf("WIFEXITED : %d\n",WIFEXITED(status));
printf("WEXITSTATUS : %d\n",WEXITSTATUS(status));
printf("WIFSIGNALED : %d\n",WIFSIGNALED(status));
printf("\n");
printf("parent end\n");
}
이후 아래와 같이 컴파일하고 정상적인 결과를 보도록 해봅시다.
# gcc process_wait.c
# ./a.out
child process:5031
child end
end pid : 5031
WIFEXITED : 1
WEXITSTATUS : 10
WIFSIGNALED : 0
parent end
EXITSTATUS는 10임을 알 수 있는데 이는 위 코드에서 exit(10)을 사용했기 때문입니다. 시그널을 받지 않고 정상적인 종료를 한 케이스는 이렇게 나오는군요.
이제 background로 프로세스를 돌리고 kill해서 시그널을 보내보도록 하겠습니다.
# ./a.out &
[4] 5060
# child process:5061
# kill 5061
# end pid : 5061
WIFEXITED : 0
WEXITSTATUS : 0
WIFSIGNALED : 1
parent end
[4]+ Done ./a.out
그렇다면 WIFEXITED는 false이고 WIFSIGNALED는 true네요. 이처럼 위의 매크로로 자식 프로세스의 종료 상태를 알 수 있습니다.
그런데 wait은 종료되는 자식 프로세스 중 먼저 종료되는 프로세스가 있다면 바로 호출되어지는데 만약 우리가 특정 자식프로세스만을 기다린다할때는 아래의 waitpid함수를 사용해야합니다.
waitpid
pid_t waitpid(pid_t pid, int *wstatus, int options);
역시 호출 성공시 그 프로세스의 pid, 아니라면 -1을 반환합니다.
pid: 인자로 pid는 기다릴 프로세스 식별자인데 양수일 경우만 해당합니다. 아래는 pid에 따라서 어떻게 동작하는지를 설명합니다.
pid | 설명 |
< -1 | meaning wait for any child process whose process group ID is equal to the absolute value of pid. |
-1 | meaning wait for any child process. |
0 | meaning wait for any child process whose process group ID is equal to that of the calling process. |
> 0 | meaning wait for the child whose process ID is equal to the value of pid. |
wstatus : wstatus는 위의 wait과 같이 종지상태를 얻어옵니다.
option : option은 조금 더 구체적인 동작을 지정합니다. option의 종류는 아래와 같습니다.
OPTION | 설명 |
WNOHANG | return immediately if no child has exited. |
WUNTRACED | also return if a child has stopped (but not traced via ptrace(2)). Status for traced children which have stopped is provided even if this option is not specified. |
WCONTINUED | (since Linux 2.6.10) also return if a stopped child has been resumed by delivery of SIGCONT. |
wait(&status)은 waitpid(-1, &wstatus, 0)를 호출한 것과 같습니다.
waitpid 예제
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
int state, cpid1, cpid2, endpid;
if((cpid1=fork())==0){
printf("\tchild1 process:%d\n",getpid());
sleep(2);
printf("\tchild1 end\n");
exit(0);
}
if((cpid2=fork())==0){
printf("\tchild2 process:%d\n",getpid());
sleep(4);
printf("\tchild2 end\n");
exit(0);
}
endpid=waitpid(cpid1,&state,0);
printf("pid : %d\n",endpid);
endpid=waitpid(cpid2,&state,0);
printf("pid : %d\n",endpid);
printf("parent end\n");
}
여기서 자식 프로세스를 2개 생성하고 기다리는데 waitpid로 첫번째 자식 프로세스, 두번째 자식 프로세스를 기다립니다. 만약 여기서 두 번째 자식 프로세스를 기다리고 싶지 않다면 두 번째 waitpid를 하지 않으면 되겠죠.
결과는 아래와 같습니다.
#gcc process_waitpid.c
# ./a.out
child2 process:5614
child1 process:5613
child1 end
pid : 5613
child2 end
pid : 5614
parent end
만약 두번째 프로세스가 끝났을때는 그대로 종료하지만 작업이 끝나지 않았을때는 A라는 작업을 해야하는 상황이라면 어떻게 해야할까요?
wait은 계속 자식 프로세스가 끝날때까지 block되었는데 waitpid는 옵션을 사용하면 됩니다.
아래가 그 예제입니다.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(){
int state, cpid1, cpid2, endpid;
if((cpid1=fork())==0){
printf("\tchild1 process:%d\n",getpid());
sleep(2);
printf("\tchild1 end\n");
exit(0);
}
if((cpid2=fork())==0){
printf("\tchild2 process:%d\n",getpid());
sleep(4);
printf("\tchild2 end\n");
exit(0);
}
while(1){
endpid=waitpid(cpid2,&state,WNOHANG);
if(endpid==0){ //child not finished if pid = 0
printf("child2 not finished\n");
sleep(1);
}else{
break;
}
}
printf("parent end\n");
}
위 코드에서는 첫번째 자식 프로세스는 기다리지 않습니다. waitpid에 WNOHANG 옵션을 주어서 자식이 종료되었는지 아닌지에 따라 작업을 구분지을 수 있습니다. 이때 waitpid는 지정된 프로세스가 끝나지 않았다면 0을 반환하고 끝났다면 그 pid를 반환하게 됩니다.
결과는 아래와 같습니다.
# gcc process_waitpid2.c
# ./a.out
child2 not finished
child2 process:5670
child1 process:5669
child2 not finished
child1 end
child2 not finished
child2 not finished
child2 end
parent end
waitpid가 wait에서 제공하지 못하는 3가지를 제공합니다.
1. waitpid함수로 특정 pid를 기다릴 수 있습니다. wait은 임의의 자식에 대해서 종료상태를 알려주지요.
2. waitpid는 wait과는 달리 자식이 종료될때까지 기다리지 않아도 됩니다. 바로 호출을 할 수 있습니다.
3. WUNTRACED와 WCONTINUED 옵션을 통해서 작업 제어를 할 수 있습니다.