C++에서 포크로 프로세스 생성
-
fork()
를 사용하여 C++로 프로그램 내에서 두 개의 프로세스 생성 -
fork()
및execve
를 사용하여 C++에서 여러 프로세스 생성 -
fork()
및execve
를 사용하여 C++에서 자동 자식 정리 기능으로 여러 프로세스를 만듭니다
이 기사에서는 C++에서fork()
시스템 호출을 사용하여 프로세스를 생성하는 방법에 대한 몇 가지 방법을 설명합니다.
fork()
를 사용하여 C++로 프로그램 내에서 두 개의 프로세스 생성
fork
함수는 대부분의 Unix 기반 운영 체제에서 사용할 수있는 POSIX 호환 시스템 호출입니다. 이 함수는 원래 호출 프로그램의 복제 본인 새 프로세스를 만듭니다. 후자의 프로세스를 parent
라고하고 새로 생성 된 child
프로세스입니다. 이 두 프로세스는 별도의 메모리 공간에서 실행되는 두 개의 스레드로 볼 수 있습니다. 현재 Linux 구현에는 내부적으로 스레드 개념이 없으므로 스레드는 메모리 영역을 공유한다는 점을 제외하면 프로세스와 유사한 구조입니다. fork
함수는 동일한 프로그램 내에서 동시 실행을 구현하거나 파일 시스템에서 새 실행 파일을 실행할 수 있습니다 (나중의 예제에서 설명).
다음 예에서는 fork
를 사용하여 하나의 프로그램 내에서 다중 처리를 보여줍니다. fork
는 인수를 취하지 않고 두 프로세스 모두에서 반환합니다. 반환 값은 부모 프로세스에서 자식의 PID이고 자식 프로세스에서는 0
이 반환됩니다. 호출이 실패하면 부모 프로세스에-1
이 반환됩니다. 따라서 우리는 반환 값의 평가를 기반으로if
문을 구성 할 수 있으며 각if
블록은 해당 프로세스에 의해 실행되어 동시 실행이 발생합니다.
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>
using std::cout;
using std::endl;
int main() {
pid_t c_pid = fork();
if (c_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (c_pid > 0) {
cout << "printed from parent process " << getpid() << endl;
wait(nullptr);
} else {
cout << "printed from child process " << getpid() << endl;
exit(EXIT_SUCCESS);
}
return EXIT_SUCCESS;
}
출력:
printed from parent process 27295
printed from child process 27297
fork()
및execve
를 사용하여 C++에서 여러 프로세스 생성
fork
함수 호출의보다 실용적인 사용은 여러 프로세스를 만들고 이러한 프로세스 내에서 다른 프로그램을 실행하는 것입니다. 이 예에서는 두 개의 소스 코드 파일이 필요합니다. 하나는 상위 프로세스 용이고 다른 하나는 하위 프로세스 용입니다. 자식 프로세스 코드는 단일 정수에 추가되는 간단한 무한 루프이며 SIGTERM
신호를 전송하여 중지 할 수 있습니다.
부모 프로그램은 분기 된 자식 프로세스에서 실행해야하는 파일 이름을 선언 한 다음spawnChild
함수를 6 번 호출합니다. spawnChild
함수는fork
/execve
호출을 래핑하고 새로 생성 된 프로세스 ID를 반환합니다. execve
는 자식 프로세스 내에서 새 프로그램 코드를 시작하기위한 인수로 프로그램 이름과 인수 목록이 필요합니다. 6 개의 자식 프로세스가 생성되면 부모는wait
함수를 호출하는while
루프에서 계속됩니다. wait
는 부모 프로세스를 중지하고 자식 프로세스가 종료 될 때까지 기다립니다.
부모가 정상적으로 종료하려면 각 자식 프로세스를 종료해야합니다. 상위 프로세스를 중단하면 하위 프로세스가 계속 실행되고 상위 프로세스가 시스템 프로세스가됩니다.
#include <sys/wait.h>
#include <unistd.h>
#include <atomic>
#include <filesystem>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::filesystem::exists;
constexpr int FORK_NUM = 6;
pid_t spawnChild(const char* program, char** arg_list) {
pid_t ch_pid = fork();
if (ch_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (ch_pid > 0) {
cout << "spawn child with pid - " << ch_pid << endl;
return ch_pid;
} else {
execve(program, arg_list, nullptr);
perror("execve");
exit(EXIT_FAILURE);
}
}
int main() {
string program_name("child");
char* arg_list[] = {program_name.data(), nullptr};
vector<int> children;
children.reserve(FORK_NUM);
if (!exists(program_name)) {
cout << "Program file 'child' does not exist in current directory!\n";
exit(EXIT_FAILURE);
}
for (int i = 0; i < FORK_NUM; ++i)
children[i] = spawnChild(program_name.c_str(), arg_list);
cout << endl;
pid_t child_pid;
while ((child_pid = wait(nullptr)) > 0)
cout << "child " << child_pid << " terminated" << endl;
return EXIT_SUCCESS;
}
하위 프로세스 소스 코드 (다른 파일) :
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>
volatile sig_atomic_t shutdown_flag = 1;
void GracefulExit(int signal_number) { shutdown_flag = 0; }
int main() {
// Register SIGTERM handler
signal(SIGTERM, GracefulExit);
unsigned int tmp = 0;
while (shutdown_flag) {
tmp += 1;
usleep(100);
}
exit(EXIT_SUCCESS);
}
fork()
및execve
를 사용하여 C++에서 자동 자식 정리 기능으로 여러 프로세스를 만듭니다
이전 예제 코드는 모든 자식이 종료되기 전에 부모 프로세스가 종료되면 서투른 동작을합니다. 이 경우 SIGQUIT
시그널이 수신되면 모든 하위 프로세스를 자동으로 종료하는 시그널 핸들러 함수를 부모 프로세스에 추가합니다. kill -SIGQUIT pid_num_of_parent
명령을 사용하여 신호를 보냅니다.
신호 처리기에서 액세스해야하는 일부 전역 변수는 프로그램 정확성에 대한 엄격한 요구 사항 인std::atomic
유형으로 선언됩니다.
#include <sys/wait.h>
#include <unistd.h>
#include <atomic>
#include <filesystem>
#include <iostream>
using std::cout;
using std::endl;
using std::string;
using std::filesystem::exists;
constexpr std::atomic<int> FORK_NUM = 6;
constexpr std::atomic<int> handler_exit_code = 103;
std::atomic<int> child_pid;
std::atomic<int> *children;
void sigquitHandler(int signal_number) {
for (int i = 0; i < FORK_NUM; ++i) {
kill(children[i], SIGTERM);
}
while ((child_pid = wait(nullptr)) > 0)
;
_exit(handler_exit_code);
}
pid_t spawnChild(const char *program, char **arg_list) {
pid_t ch_pid = fork();
if (ch_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (ch_pid > 0) {
cout << "spawn child with pid - " << ch_pid << endl;
return ch_pid;
} else {
execve(program, arg_list, nullptr);
perror("execve");
exit(EXIT_FAILURE);
}
}
int main() {
string program_name("child");
char *arg_list[] = {program_name.data(), nullptr};
if (!exists(program_name)) {
cout << "Program file 'child' does not exist in current directory!\n";
exit(EXIT_FAILURE);
}
children = reinterpret_cast<std::atomic<int> *>(new int[FORK_NUM]);
signal(SIGQUIT, sigquitHandler);
for (int i = 0; i < FORK_NUM; ++i) {
children[i] = spawnChild(program_name.c_str(), arg_list);
}
cout << endl;
while ((child_pid = wait(nullptr)) > 0)
cout << "child " << child_pid << " terminated" << endl;
return EXIT_SUCCESS;
}
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn Facebook