6. 프로세스 생성과 실행
6-1.개요
유닉스에서 프로세스는 사용자가 명령행에서 직접 프로세스를 실행해 생성하는 경우도 있지만, 프로그램 안에서 다른 프로그램을 실행해 생성하는 경우도 있다. 이렇게 프로그램에서 다른 프로그램을 실행해 새로운 프로세스를 실행할 때는 system, fork, vfork함수를 사용한다.
기능 | 함수원형 |
---|---|
프로그램 실행 | int system(const char *string); |
프로세스 생성 | pid_t fork(void); pid_t vfork(void); |
유닉스는 프로세스가 종료되면 해당 프로세스가 어떻게 종료되었는지를 나타내는 종료 상태(exit status)를 저장한다. 자식 프로세스는 부모 프로세스에 자신이 어떻게 종료되었는지 알리는 종료 상태값을 리턴할 수 있다. 일반적으로 종료 상태값이 0이면 정상적으로 종료했음을 의미한다. 종료 상태값이 0이 아니면 오류가 발생했음을 의미한다. 프로그램을 종료할 때는 exit, atexit, _exit함수를 사용한다.
기능 | 함수원형 |
---|---|
프로세스 종료 | void exit(int status); |
종료 시 수행할 작업 지정 | int atexit(void (*func)void)); |
함수명이 exec로 시작하는 exec함수군이 있다. exec함수군은 인자로 받은 다른 프로그램을 자신을 호출한 프로세스의 메모리에 덮어쓴다.
따라서 프로세스가 수행중이던 기존 프로그램은 중지되어 없어지고 새로 덮어쓴 프로그램이 실행된다. exec함수군은 fork함수와 연결해 fork로 생성한 자식 프로세스가 새로운 프로그램을 실행하도록 할 때도 사용한다.
<프로세스 수행>
int execl(const char *path, const char *argo, ..., const char *argn, (char *)0;
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp9const char *file, const char *arg0, ..., const char *argn, (char *)0;
int execvp(const char *file, char *const argv[]);
fork 함수로 자식 프로세스를 생성하면 부모 프로세스와 자식 프로세스는 순서에 관계 없이 실행되고, 먼저 실행을 마친 프로세스는 종료한다. 이때 좀비 프로세스zombie procss같은 불안정 상태의 프로세스가 발생하는데 이를 방지하려면 프로세스 동기화 함수를 수행해서 부모 프로세스와 자식 프로세스를 동기화 시켜야한다.
<프로세스 동기화 함수>
기능 | 함수원형 |
---|---|
임의의 자식 프로세스의 상태값 구하기 | pid_t wait(int *stat_loc); |
특정 프로세스의 상태값 구하기 | pid_t waitpid(pid_t pid, int *stat_loc, int options); |
6-2.프로세스 생성
프로세스는 실행 중인 프로그램을 의미한다.
프로세스는 사용자가 명령행에서 직접 실행해서 생성하는 경우도 있지만, 프로그램 안에서 다른 프로그램을 실행해 생성하는 경우도 있다.
예를 들어 기능 별로 별도의 프로그램으로 구성되어 있는 업무용 소프트웨어에서 보고서 작성을 위해 출력 프로그램을 실행할 수 있다.
인터넷을 통해 서비스를 제공하도록 다른 서비스 프로그램을 동작시키려면 새로운 프로세스를 생성해야한다.
이렇게 새로운 프로세스를 생성할 때 system, fork, vfork 함수를 사용한다.
6-2-1. 간단한 방법
system함수는 프로그램 안에서 새로운 프로그램을 실행하는 가장 간단한 방법이다.
그러나 이 함수는 명령을 실행하기 위해 쉘까지 동작시키므로 비효율적이다.
- 프로그램 실행: system(3)
#include <stdlib.h>
int system(const char *string);
* string : 실행할 명령이나 실행파일명
이 함수는 기존 명령이나 실행 파일명을 인자로 받아 쉘에 전달한다.
쉘은 내부적으로 새 프로세스를 생성해 인자로 받은 명령을 실행하고,
해당명령의 실행이 끝날 때까지 기다렸다가 종료 상태를 리턴한다
#include <stdlib.h>
#include <stdio.h>
int main(void){
int a;
//파이프로 연결된 ps -ef | grep han > han.txt 명령을 실행하도록
//system 함수를 호출한다.
//인자로 전달된 명령은 현재 실행 중인 프로세스들에서 'han'을 포함한
//내용을 찾아 han.txt파일에 저장한다.
a = system("ps -ef | grep han > han.txt");
print("return value: %d\n", a);
return 0;
}
* cat명령으로 han.txt파일을 살펴보면 system함수로 실행한 명령인
"sh -c ps -ef | grep han > han.txt 가 실행되고 있다.
(system 함수로 명령을 실행하면 본 쉘로 실행되고 -c옵션이 지정된다.)
6-2-2. 프로세스 생성
유닉스에서 프로세스를 생성해 프로그램을 실행하는 방법은 fork 함수를 사용하는 것이다.
fork함수는 새로운 프로세스를 생성한다. 이 함수가 생성한 새 프로세스는 child process라고 한다.
fork함수를 호출한 프로세스는 parent process가 된다.
fork함수가 리턴하면 부모 프로세스와 자식 프로세스가 동시에 동작하는데, 어느 프로세스가 먼저 실행될지는 알수 없다.
유닉스 운영체제의 스케쥴링(scheduling)에 따라 처리 순서가 달라진다.
fork함수 실행시 처리 과정
- fork 함수 호출
- 새로운 프로세스(자식 프로세스) 생성
- fork함수로 생성한 자식 프로세스의 메모리 공간은 부모 프로세스의 메모리 공간을 그대로 복사해 만든다.
- 이 함수는 부모 프로세스에는 자식 프로세스의 PID를 리턴하고, 자식 프로세스에는 0을 리턴한다.
- fork 함수 호출
자식 프로세스가 상속 받는 대표적인 속성 목록
- 실제 사용자ID(RUID), 유효사용자ID(EUID), 실제그룹ID(RGID), 유효그룹ID(EGID)
- 환경 변수
- 열린 파일 기술자
- 시그널 처리 설정
- setuid, setgid 설정
- 현재 작업 디렉토리
- umask 설정값
- 사용 가능한 자원 제한
- 실제 사용자ID(RUID), 유효사용자ID(EUID), 실제그룹ID(RGID), 유효그룹ID(EGID)
자식 프로세스와 부모 프로세스와의 다른 점 (fork함수 결과 생성된 자식프로세스)
- 자식프로세스는 유일한 프로세스ID를 갖는다.
- 자식 프로세스는 부모프로세스를 PPID로 설정한다.
- 자식 프로세스는 부모 프로세스가 연 파일 기술자의 사본을 가지고 있으므로 부모 프로세스와 자식 프로세스가 같은 파일의 오프셋을 공유하는 상태가 되므로 읽거나 쓸때 주의해야한다.
- 자식 프로세스는 부모 프로세스가 설정한 프로세스 잠금, 파일 잠금, 기타 메모리 잠금 등은 상속하지 않는다.
- 자식 프로세스의 tms 구조체 값은 0으로 초기화 된다. 프로세스 실행 시간을 측정하는 기준 값이 새로 설정된다.
<fork 함수 사용 예제>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void){
pid_t pid;
switch(pid = fork()){
case -1 : /* fork failed */
perror("fork");
exit(1);
break;
case 0 : /* child process */
printf("child process - my PID : %d, may parent's PID: %d\n", (int)getpid(), (int)getppid());
break;
default : /* parent process */
printf("parent process - my PID: %d, My parent's PID: %d, my child's PID:%d\n", (int)getpid(), (int)getppid(), (int)pid);
break;
}
printf("end of fork\n");
return 0;
}
- 프로세스 생성 : vfork(2)
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
* return 값 : 성공시 부모 프로세스에는 자식 프로세스의 ID, 자식 프로세스에는 0.
실패시 -1.
#include <unistd.h>
pid_t vfork(void);
vfork함수도 fork처럼 새로운 프로세스를 생성한다.
그러나 부모 프로세스의 메모리 공간을 모두 복사하지는 않는다.
vfork함수는 표준이 아니므로 시스템에서 지원하는지 확인하고 사용해야한다.
6-3.프로세스 종료
유닉스는 프로세스가 종료되면 해당 프로세스가 어떻게 종료되었는지를 나타내는 종료 상태(exit status)를 저장한다. 부모 프로세스는 저장된 종료 상태 정보를 사용해 자식 프로세스가 어떻게 종료되었는지 알 수 있다.
자식 프로세스는 부모 프로세스에 자신이 어떻게 종료되었는지 알리는 종료 상태값 을 리턴할 수 있다.
종료 상태값이 0이면 프로세스가 정상 종료했음을 알수 있다.
오류가 발생하면 1을 리턴하며, 프로그래머에 따라 다양한 상태값을 이용해 종료상태를 알려주기도 한다.
6-3-1.프로세스 종료 함수
exit, atexit, _exit함수는 프로세스 종료와 관련이 있다. exit함수는 프로세스를 종료할 때 사용하는 기본 함수다. atexit함수를 사용하면 프로세스를 종료할 때 수행할 작업을 지정할 수 있다. _exit 함수는 일반적으로 프로그램에서 직접 호출하지 않는다.
- 프로그램 종료 : exit(2)
#include <stdlib.h>
void exit(int status);
* status : 상태 종료값
exit 함수는 프로세스를 종료시키고 부모 프로세스에 종료 상태값을 전달한다. 이때 atexit 함수로 예약한 함수들을 지정된 순서와 역순으로 모두 실행한다.
만약 atexit함수로 예약한 함수가 수행도중 문제 발생하여 리턴하지 못할 경우는 exit함수의 나머지 과정도 수행되지 않는다.
exit함수는 프로세스가 사용중이던 모든 표준 입출력 스트림에 데이터가 남아있으면 이를 모두 기록하고 열려 있는 스트림을 모두 닫는다.
그 다음 tmpfile함수로 생성한 임시 파일을 모두 삭제하고, _exit함수를 호출한다.
_exit함수는 시스템 호출로, 프로세스가 사용하던 모든 자원을 반납한다.
이는 exit 함수가 C표준 함수이기 때문에 시스템에 독립적인 기능만 수행하고
시스템과 관련된 기능은 시스템 호출에서 처리하도록 해야하기 때문이다.
- 프로그램에서 종료시 수행할 작업 예약 : axexit(3)
#include <stdlib.h>
int atexit(void (*func) (void));
* func : 종료시 수행할 작업을 지정한 함수명
axexit 함수는 프로세스가 종료할 때 수행할 기능을 예약한다. atexit함수는 인자로 함수의 포인터를 받는다.
atexit의 인자로 지정하는 함수는 인자와 리턴값이 없는 함수다.
이 함수로 예약할 수 있는 함수의 개수는 여유 메모리에 따라 달라지며,
sysconf 함수의 _SC_ATEXIT_MAX 항목으로 검색할 수 있다.
<exit와 aexit함수 사용예제>
#include <stdlib.h>
#include <stdio.h>
//프로세스 종료시 수행할 작업을 지정한 함수들의 정의다.
//실제로는 프로세스 종료시 반드시 수행해야할 작업들을 지정해야한다.
//로그를 남기거나, 관리자에게 메시지를 보내거나 데이터를 저장하는 등의 작업을 수행할 수 있다.
void cleanup1(void){
printf("cleanup 1 is called\n");
}
void cleanup2(void){
printf("cleanup 2 is called \n");
}
int main(void){
atexit(cleanup1);
atexit(cleanup2);
exit(0);
}
* 결과
실행결과는 함수가 예약 순서와 반대로 호출되었음을 알 수 있다.
# 실행파일.out
cleanup 2 is called;
cleanup 1 is called;
- 프로그램 종료 : _exit(2)
#include <unistd.h>
void _exit(int status);
* status : 종료 상태값
_exit 함수는 프로그램에서 직접 사용하지 않고, exit함수에서 내부적으로 호출한다. 이 함수는 시스템 호출로, 프로세스 종료시 다음과 같은 과정을 통해 시스템 관련 자원을 정리한다.
- 모든 파일 기술자를 닫는다.
- 부모 프로세스에 종료 상태를 알린다.
- 자식 프로세스들에 SIGHUP 시그널을 보낸다.
- 부모 프로세스에 SIGCHLD 시그널을 보낸다.
- 프로세스 간 통신에 사용한 자원을 반납한다.
6-4. exec 함수군 활용
함수명이 exec로 시작하는 함수들이 있는데 이를 exec함수군이라고 부른다.
exec함수군을 이용해 명령이나 실행 파일을 만들 수 있다.
이들은 인자로 받은 다른 프로그램을 자신을 호출한 프로세스의 메모리에 덮어쓴다.
따라서 프로세스가 수행중이던 기존 프로그램은 없어지고, 새로 덮어쓴 프로그램이 실행된다.
이들을 호출한 프로세스 자체가 바뀌므로 exec 함수군 호출 성공시 리턴값은 없다.
exec함수군은 fork함수와 연결해 fork로 생성한 자식 프로세스가 새로운 프로그램을 실행하도록 할 때 유용하다.
6-4-1. exec 함수군의 함수 형태
exec(2) 함수군은 다음과 같이 6가지 함수 형태가 있다. 각 형태에 따라 지정하는 인자가 약간씩 차이가 있으므로 주의해야한다.
exec함수군은 path나 file에 지정한 명령이나 실행 파일을 실행한다.
arg, envp로 시작하는 인자를 path나 file에 지정한 파일의 main함수에 전달한다.
#include <unistd.h>
int execl(const char *path, const char *arg0, ..., const char *argn, (char * ) 0);
* path : 지정한 경로명
* arg0 ~ argn을 인자로 전달한다. (arg0에는 실행파일명을 관례로 지정함)
* execl 의 마지막 인자에는 인자의 끝을 의미하는 NULL포인터((char *)0)을 지정해야한다.
int execv(const char *path, char *const argv[]);
* argv : 포인터 배열(이 배열의 마지막에는 널 문자열을 저장해야한다.)
int execl(const char *path, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]);
* arg0~argn : 포인터로 지정하며 마지막 값은 NULL 포인터로 지정해야한다.
* evnp : 새로운 환경 변수 설정. 포인터 배열이며 이 배열의 마지막에는
널 문자열을 저장해야한다.
int execve(const char *path, char *const argv[], char *const envp[]);
* argv, envp 는 포인터 배열이며 마지막에는 NULL문자열을 저장할 것.
int execlp(const char *file, const char *arg0, ..., const char *argn, (char *)0);
* 파일은 이 함수를 호출한 프로세스의 검색 경로(환경 변수 PATH에 정의된 경로)에서 찾는다.
int execvp(const char *file, char *const argv[]);
* file에 지정한 파일을 실행하며 argv를 인자로 전달한다.
argv는 포인터 배열이다. 이 배열의 마지막에는 NULL문자열을 저장해야 한다.
6-4-2. exec 함수군의 활용
- execlp함수를 사용해 현재 프로그램을 다른 명령(ls -a)로 바꿔서 실행하는 예
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void){
printf("-->Before exec function\n");
if(execlp("ls", "ls", "-a", (char *)NULL) == -1) {
perror("execlp");
exit(1);
}
printf("--> After exec function \n");
return 0;
}
위 예제의 결과에서 --> After메세지는 출력되지 않고 ls명령만 출력됨을 알 수 있다.
execlp 함수를 만나 프로세스의 이미지가 ls명령의 이미지로 바뀌었으므로 이후의 명령은 수행되지 않는다.
- execv 함수를 사용해 현재 프로그램을 다른 명령(ls -a)으로 바꿔 실행하는 예제
#include <unistd.h>
#include <stdlib.h>
#inlcude <stdio.h>
int main(void) {
//포인터 배열 선언
char *argv[3];
printf("Before exec function \n");
argv[0] = "ls"; //실행파일명
argv[1] = "-a"; //옵션
argv[2] = NULL; //옵션이나 인자가 없으면 null 지정
//execv함수로 명령의 경로와 포인터 배열 argv를 인자로 지정함
if(execv("/usr/bin/ls", argv) == -1 ){
perror("execv");
exit(1);
}
printf("After exec function \n");
return 0;
}
* 수행 결과 After~ 메세지는 출력하지 않고 ls 명령을 수행함을 알 수 있다.
- argv와 envp를 모두 지정해 execve 함수 호출
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void){
char *argv[3];
char *argv[2];
printf("Before exec function \n");
argv[0] = "arg.out";
argv[1] = "100";
argv[2] = NULL;
envp[0] = "MYENV=aaa";
envp[1] = NULL;
//arg.out은 다음 프로그램 소스를 컴파일해 만든 것이다.
//이 프로그램은 인자로 받은 argv와 envp 값을 출력한다.
if(execve("./arg.out", argv, envp) == -1) {
perror("execve:);
exit(1);
}
printf("After exec function \n");
return 0;
}
* arg.out 의 컴파일전 프로그램 소스
#include <stdio.h>
int main(int argc, char **argv, char **envp) {
int n;
char **env;
printf("argc = %d\n", argc);
for(n =0; n < argc; n++)
printf[%d] = %s\n", n, argv[n]);
env = envp;
while(*env){
printf(%s\n", *env);
env++;
}
return 0;
}
6-4-3. exec 함수군과 fork 함수
자식 프로세스에서 exec함수군을 호출하면 자식프로세스는 부모 프로세스로부터 복사한 프로그램과는 다른 명령이나 프로그램을 실행할 수 있다.
예를 들어 쉘에서 어떤 명령이나 파일을 실행하면, 쉘은 fork함수로 자식 프로세스를 만들고
사용자가 입력한 명령이나 파일을 exec함수군을 사용해 자식 프로세스에서 실행한다.
이렇게 부모 프로세스와 자식 프로세스가 각기 다른 작업을 수행해야할 때 fork와 exec 함수를 같이 사용해야한다.
- fork함수로 자식 프로세스를 만들고, exec함수를 사용해 자식 프로세스는 ls -a 명령을 수행하도록 만드는 예제
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void){
pid_t pid;
//fork함수를 호출해 새로운 프로세스를 생성한다.
switch(pid == fork(){
case -1 : /* fork failed */
perror("fork");
exit(1);
break;
//자식 프로세스가 수행할 부분으로 execlp 함수를 호출한다.
//이렇게 하면 자식 프로세스가 가지고 있던 부모 프로세스의 이미지를 ls명령의 이미지가 덮어쓴다. 즉, 부모 프로세스와 자식 프로세스는 각기 다른 프로그램을 실행한다.
case 0 : /* child process */
printf(" --> child process\n");
if(execlp("ls", "ls", "-a", (char *)NULL) == -1){
perror("execlp");
exit(1);
}
exit(0);
break;
default : /* parent process */
printf("-->parent process - MY pid : %d/n", (int)getpid());
break;
}
return 0;
}
* 실행결과를 보면 자식 프로세스는 -->Child Process~라는 메세지를 출력한 후,
ls명령을 수행하고, -->parent process~ 메세지를 출력했음을 알 수 있다.
다시 실행해보면 ls명령이 부모 프로세스 다음에 실행될 수도 있다.
6-5. 프로세스 동기화
fork함수로 자식 프로세스를 생성하면 부모 프로세스와 자식 프로세스는 순서에 상관없이 실행되고, 먼저 실행을 마친 프로세스는 종료한다.
그러나 부모 프로세스와 자식 프로세스 사이의 종료 절차가 제대로 진행되지 않을 때가 있다.
이때 zombie process 와 같은 불안정 상태의 프로세스가 발생하는데 이를 방지하려면 부모 프로세스와 자식 프로세스를 동기화해야한다.
- wait함수 사용 예제
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#inlcude <stdio.h>
int main(void){
int status;
pid_t pid;
//fork함수를 호출해 새로운 프로세스를 생성한다.
switch(pid = fork()){
case -1 : /* fork failed */
perror("fork");
exit(1);
break;
//자식프로세스는 출력을 하나 하고, 바로 exit함수로 종료한다.
//종료 상태값으로 2를 지정한다.
case 0 : /* child process */
printf("--> Child process \n");
exi(2);
break;
default : /* parent process */
//부모 프로세스가 수행되는 부분에 wait함수를 추가한다.
//이렇게 하면 자식 프로세스가 -->Chile process 메세지 출력을 수행하고 종료할 때까지 부모 프로세스가 기다린다. 자식 프로세스가 보낸 종료 상태값은 Status에 저장된다.
while (wait(&status) != pid)
continue;
printf("-->Parent process \n");
printf("Status : %d, %x\n", status, status);
printf("Child process Exit Status : %d\n", status >> 8);
break;
}
return 0;
}
* 자식프로세스가 종료할 때까지 기다리기 때문에 (wait 함수, while구문) 항상 자식 프로세스의 결과가 먼저 출력되고 --> Parent process... 의 결과가 출력된다.
* 실행결과를 보면 status가 512(10진수), 0x0200(16진수)으로 출력됨을 알 수 있다. 자식 프로세스가 전달한 값은 부모 프로세스에 왼쪽으로 한 바이트 이동해 전달된다. 이를 제대로 출력하려면 오른쪽으로 8비트 이동시켜야한다. 그렇게 이동시켜서 출력한 결과는 종료 상태값이 2가 된다.
6-5-1. 좀비 프로세스
정상적인 프로세스 종료 과정이라면, 자식 프로세스가 종료를 위해 부모 프로세스에 종료상태 정보를 보내고, 부모 프로세스는 이 정보를 받으면 프로세스 테이블에서 자식 프로세스를 삭제한다.
그러나 자식 프로세스가 자원을 모두 반납했어도 부모 프로세스가 종료 상태 정보를 얻어가지 않거나 자식 프로세스보다 먼저 종료하는 경우가 발생한다.
실행을 종료한 후 자원을 반납한 자식 프로세스의 종료 상태 정보를 부모 프로세스가 얻어가지 않는 경우에 좀비 프로세스가 발생한다.
좀비 프로세스는 프로세스 테이블에만 존재한다.
좀비 프로세스는 일반적인 방법으로 제거할 수 없으며, 부모 프로세스가 wait 관련 함수를 호출해야 사라진다.
만일 자식 프로세스보다 부모 프로세스가 먼저 종료되면 자식 프로세스들은 고아 프로세스가 된다. 이들 고아 프로세스는 init(PID 1) 프로세스의 자식 프로세스로 등록된다.
6-5-2. 프로세스 동기화
부모 프로세스와 자식 프로세스를 동기화하려면 부모 프로세스는 자식 프로스가 종료할 때까지 기다려야한다.
자식 프로세스의 실행이 완전히 끝나기를 기다렸다가 종료 상태를 확인하는 함수는 wait, waitpid이다.
- 프로세스 동기화 : wait(3)
#include <sys/type.h>
#include <sys/wait.h>
pid_t wait(int *stat_loc);
* stat_loc : 상태 정보를 저장할 주소
wait 함수는 자식 프로세스가 종료할 때까지 부모 프로세스를 기다리게 한다 자식 프로세스의 종료 상태는 stat_loc에 지정한 주소에 저장된다.
stat_loc에 NULL을 지정할 수도 있다.
만약 부모 프로세스가 wait 함수를 호출하기 전에 자식 프로세스가 종료하면 wait함수는 즉시 리턴한다.
wait함수의 리턴값은 자식프로세스의 ID이다.
wait 함수의 리턴값이 -1이면 살아있는 자식 프로세스가 하나도 없다는 의미이다.
- 특정 자식 프로세스와 동기화 : waitpid(3)
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *stat_loc, int options);
* pid : 종료를 기다리는 PID
* -1 보다 작은 경우 : pid의 절댓값과 같은 프로세스 그룹 ID에 속한 자식 프로세스 중 임의의 프로세스의 상태값을 요청한다.
* -1 : wait 함수처럼 임의의 자식 프로세스의 상태값
* 0 : 함수를 호출한 프로세스와 같은 프로세스 그룹에 속한 임의의 프로세스의 상태값을 요청한다.
* 0보다 큰 경우 : 지정한 pid에 해당하는 프로세스의 상태값을 요청한다.
* stat_loc : 종료 상태값을 저장할 주소
* options : waitpid 함수의 리턴 조건. <sys/wait.h>에 정의된 값을 사용한다. OR연산으로 연결해 지정할 수 있다.
* WCONTINUED : 수행중인 자식 프로세스의 상태값이 리턴
* WNOHANG: pid로 지정한 자식 프로세스의 상태값을 즉시 리턴받을 수 없어도 이를 호출한 프로세스의 실행을 블록하지 않고, 다른 작업을 수행하게 한다.
* WNOWAIT : 상태값을 리턴한 프로세스가 대기 상태로 머물수 있도록 한다.
* WUNTRACED : 실행을 중단한 자식 프로세스의 상태 값을 리턴한다. 실행이 중단되었으므로 더이상 상태값을 리턴하지 않는다.
wait함수는 자식프로세스가 여러개일 경우 아무 자식프로세스나 종료하면 리턴한다 waitpid함수는 특정 PID의 자식 프로세스가 종료하기를 기다린다. 이 함수는 pid 로 지정한 자식 프로세스가 종료하기를 기다리며, 자식 프로세스의 종료 상태 값을 stat_loc에 저장하고 , option의 조건에 따라 리턴한다.
- waitpid 함수 사용 예제
<waitpid함수를 이용해 fork함수로 생성한 자식 프로세스가 종료하기를 기다리게 만드는 프로그램>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#incldue <stdio.h>
int main(void){
int status;
pid_t pid;
if((pid = for()) <0) { /* fork failed */
perror("fork");
exit(1);
}
//자식프로세스에서 sleep함수를 호출해 곧바로 종료하지 않고 시간을 지연한다.
if(pid == 0) { /* child process * /
printf("--> Child process\n");
sleep(3);
exit(3);
}
printf("--> parent process\n");
//부모프로세스는 waitpid함수를 수행해 자식 프로세스가 종료하기를 기다린다.
//WNOHANG옵션을 지정했으므로 waitpid 함수는 블록되지 않고 printf문과 sleep 함수를 반복적으로 실행한다.
while(waitpid(pdi, &status, WNOHANG) == 0) {
printf("parent still wait ... \n");
sleep(1);
}
printf("child exit status : %d\n", status>>8);
return 0;
}
* 실행결과 를 보면 parent still wait 구문이 반복적으로 출력되며, 자식 프로세스의 종료 상태값으로 3이 출력된다.