Linux C++ 프로그래밍에서 fork(), waitpid() 예제를 소개합니다. waitpid()를 호출하면 child process가 종료될 때까지 block되는데, Timeout을 적용하여 Child의 응답이 없을 때 기다리지 않는 예제도 소개하려고 합니다.
Linux system을 깊이 알지 못하기 때문에 잘못된 부분이 있을 수 있습니다.
fork()
fork()는 Parent의 프로세스를 복제하여 Child process를 생성하는 API입니다.
fork()를 사용하는 간단한 예제입니다.
#include <iostream>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed!\n");
exit(-1);
}
if (pid == 0) {
// child process
printf("(%ld) I am the child, pid: %d, getpid(): %d\n",
time(0), pid, getpid());
sleep(2);
printf("(%ld) Done from child\n", time(0));
exit(0);
} else {
// parent process
printf("(%ld) I am the parent, pid=%d\n", time(0), pid);
sleep(4);
printf("(%ld) Done from parent\n", time(0));
exit(0);
}
}
실행 결과입니다. 로그 맨 앞은 시스템 시간이며 단위가 초 입니다.
(1578224091) I am the parent, pid=24139
(1578224091) I am the child, pid: 0, getpid(): 24139
(1578224093) Done from child
(1578224095) Done from parent
fork()가 성공하면 parent process는 child의 pid가 리턴하며, child process는 0이 리턴됩니다. 메모리 부족 등으로 fork()가 실패하면 -1이 리턴됩니다.
waitpid()
waitpid()는 parent가 child가 종료되었는지 기다릴 때 사용합니다.
다음은 parent가 waitpid()로 child process가 종료될 때까지 기다리는 예제입니다.
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed!\n");
exit(-1);
}
if (pid == 0) {
// child process
printf("(%ld) I am the child, pid: %d, getpid(): %d\n",
time(0), pid, getpid());
sleep(2);
printf("(%ld) Done from child\n", time(0));
exit(0);
} else {
// parent process
printf("(%ld) I am the parent, pid=%d\n", time(0), pid);
int state;
pid_t got_pid = waitpid(pid, &state, 0); // 1
printf("(%ld) got_pid=%d\n", time(0), got_pid); // 2
printf("(%ld) WIFEXITED: %d\n", time(0), WIFEXITED(state)); // 3
printf("(%ld) WEXITSTATUS: %d\n", time(0), WEXITSTATUS(state)); // 4
printf("(%ld) Done from parent\n", time(0));
exit(0);
}
}
- waitpid()의 인자로, 기다리는 child의 pid와, 리턴받을 상태, option을 전달합니다. child 프로세스의 상태가 변경되면(꼭 종료가 아니더라도) waitpid()는 리턴됩니다.
- 정상적으로 리턴되면 child의 pid가 리턴됩니다. 그렇지 않으면 -1이 리턴됩니다.
WIFEXITED
는 state에서 child가 종료되었는지를 확인하는 매크로입니다. 종료되었다면 true를 리턴합니다.WEXITSTATUS
는 exit status를 리턴합니다.
결과
(1578224731) I am the parent, pid=25194
(1578224731) I am the child, pid: 0, getpid(): 25194
(1578224733) Done from child
(1578224733) got_pid=25194
(1578224733) WIFEXITED: 1
(1578224733) WEXITSTATUS: 0
(1578224733) Done from parent
waitpid의 status 및 option에 대한 자세한 내용은 man이나 waitpid를 참고해주세요.
waitpid()에 Timeout 적용 (1)
Signal을 받아 child가 종료되었는지 확인 후, waitpid()를 호출하는 방법이 있습니다.
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
static pid_t fork_child() {
int p = fork ();
if (p == -1) {
exit (1);
}
if (p == 0) {
printf ("(%ld) child: sleeping...\n", time(0));
sleep (10);
printf ("(%ld) child: exiting\n", time(0));
exit (0);
}
return p;
}
int main() {
sigset_t mask;
sigset_t orig_mask;
struct timespec timeout;
timeout.tv_sec = 5;
timeout.tv_nsec = 0;
sigemptyset (&mask);
sigaddset (&mask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
perror ("sigprocmask");
return 1;
}
pid_t pid = fork_child();
do {
if (sigtimedwait(&mask, NULL, &timeout) < 0) {
if (errno == EINTR) {
// Interrupted by a signal other than SIGCHLD.
continue;
} else if (errno == EAGAIN) {
printf ("(%ld) Timeout, killing child\n", time(0));
kill (pid, SIGKILL);
} else {
printf ("(%ld) sigtimedwait\n", time(0));
return 1;
}
}
break;
} while (1);
int state;
if (waitpid(pid, &state, 0) < 0) {
printf("(%ld) WIFEXITED: %d\n", time(0), WIFEXITED(state));
printf("(%ld) WEXITSTATUS: %d\n", time(0), WEXITSTATUS(state));
printf("(%ld) Done from parent\n", time(0));
return 1;
}
}
결과
(1578225826) child: sleeping...
(1578225831) Timeout, killing child
waitpid()에 Timeout 적용 (2)
Alarm을 이용하여 Timeout이 되면 handler에서 child process를 kill하는 방법입니다.
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
int child_pid = 0;
void timer_handler(int pid) {
printf("(%ld) time is over, child will be killed\n", time(0));
kill(child_pid, SIGKILL);
}
int main() {
signal(SIGALRM, timer_handler);
pid_t pid = fork();
child_pid = pid;
if (pid == 0) { // child process
printf("(%ld) I am the child, return from fork=%d\n",
time(0), pid);
sleep(20);
printf("(%ld) Done from child\n", time(0));
} else { // parent process
printf("(%ld) I am the parent, child pid=%d\n",
time(0), pid);
alarm(5);
int state;
waitpid(pid, &state, 0);
printf("(%ld) WIFEXITED: %d\n", time(0), WIFEXITED(state));
printf("(%ld) WEXITSTATUS: %d\n", time(0), WEXITSTATUS(state));
printf("(%ld) Done from parent\n", time(0));
exit(0);
}
}
결과
(1578226328) I am the parent, child pid=27412
(1578226328) I am the child, return from fork=0
(1578226333) time is over, child will be killed
(1578226333) WIFEXITED: 0
(1578226333) WEXITSTATUS: 0
(1578226333) Done from parent
참고
Loading script...
Related Posts
- Ubuntu/Linux - 특정 이름이 들어간 파일 모두 찾고 삭제하기
- Ubuntu/Linux - 특정 이름이 포함된 프로세스 모두 종료
- Ubuntu/Linux - 사용자 계정 이름 확인 (id, whoami)
- Ubuntu/Linux - E: unable to locate package 에러 해결
- Ubuntu 22.04/20.04 - Yarn 설치 방법
- Linux/Ubuntu - grep 명령어로 문자열 검색
- Linux/Ubuntu - 터미널에서 seq로 명령어 N회 반복
- Linux/Ubuntu - wc 명령어로 Line(줄) 개수 세기
- bash: pip: command not found 문제 해결 (Linux/Ubuntu)
- Ubuntu 22.04 - Swap 메모리 늘리기
- Linux - 파일이 수정된 날짜/시간 확인 방법
- Linux - 파일, 디렉토리 삭제 명령어(rm -rf, rmdir)
- 우분투 22.04 - deb 파일 설치하기
- Ubuntu 20.04 - OpenJDK 11 설치, 삭제
- Linux - ps 명령어로 실행 중인 프로세스(PID) 확인
- Ubuntu 20.04 - SSH 설치와 접속 방법
- Ubuntu에서 PPA 저장소 추가, 제거 방법
- Ubuntu 22.04 - Brave 브라우저 설치
- Ubuntu 22.04 - 프로스트와이어(FrostWire) 설치
- Ubuntu 22.04 - Remmina 설치 방법
- Ubuntu 22.04 - Rust 설치하기
- Ubuntu 22.04 - 텔레그램 설치하기
- Ubuntu - Deluge 토렌트 클라이언트 설치
- Ubuntu JDK 17 (OpenJDK) 설치, 삭제
- Ubuntu 20.04 스팀(Steam) 설치 방법
- Ubuntu에서 draw.io 설치하는 방법
- 우분투에 VMware 설치하기
- [Ubuntu] VMware에서 Windows 11 설치
- Vi/Vim에서 1줄 또는 여러줄 삭제 방법
- Vi/Vim에서 라인 번호 표시하 기
- 우분투에서 rar 압축, 압축 풀기
- Ubuntu에서 구글 드라이브 사용하기 (로컬 폴더에 마운트)
- [Ubuntu] apt-cache search, 설치 가능한 패키지 검색
- Ubuntu - DNS 캐시 삭제 방법
- Ubuntu에서 사용할 수 있는 Text Editor 소개