Criar processos com fork em C++
-
Utilize
fork()
para criar dois processos dentro do programa em C++ -
Utilize
fork()
eexecve
para Criar Processos Múltiplos em C++ -
Utilize
fork()
eexecve
para criar processos múltiplos com a função de limpeza automática de crianças em C++
Este artigo explicará vários métodos de como criar processos com uma chamada de sistema fork()
em C++.
Utilize fork()
para criar dois processos dentro do programa em C++
A função fork
é a chamada de sistema compatível com POSIX disponível na maioria dos sistemas operativos baseados em Unix. A função cria um novo processo, que é uma duplicata do programa de chamada original. Este último processo chama-se parent
e um recentemente criado - child
. Estes dois processos podem ser vistos como os dois threads executados em espaços de memória separados. Note-se que a actual implementação do Linux não tem qualquer conceito de thread internamente, pelo que os threads são estruturas semelhantes aos processos, excepto que partilham regiões de memória. A função fork
pode implementar execução simultânea dentro do mesmo programa ou executar um novo executável a partir do sistema de ficheiros (demonstrado nos exemplos posteriores).
No exemplo seguinte, utilizamos o fork
para demonstrar o multiprocessamento dentro do mesmo programa. O fork
não aceita argumentos e retornos em ambos os processos. O valor de retorno é o PID da criança no processo dos pais, e 0
é devolvido no processo da criança. No caso da chamada falhar, -1
é devolvido no processo dos pais. Assim, podemos construir declarações if
baseadas na avaliação do valor de retorno, e cada bloco if
é executado pelo processo correspondente, resultando na execução simultânea.
#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;
}
Resultado:
printed from parent process 27295
printed from child process 27297
Utilize fork()
e execve
para Criar Processos Múltiplos em C++
Uma utilização mais prática da chamada à função fork
é criar múltiplos processos e executar diferentes programas dentro destes processos. Note-se que, neste exemplo, precisamos de dois ficheiros de código fonte: um para o processo dos pais e outro para os processos dos filhos. O código de processo infantil é o simples loop infinito que se acrescenta ao inteiro único e pode ser parado enviando o sinal SIGTERM
.
O programa pai declara um nome de ficheiro que precisa de ser executado pelos processos de criança bifurcada e depois chama a função spawnChild
6 vezes. A função spawnChild
envolve as chamadas fork
/execve
e devolve o ID do processo recentemente criado. Note que o execve
requer um nome de programa e uma lista de argumentos como argumentos para lançar um novo código de programa dentro dos processos da criança. Uma vez criados os processos de 6 filhos, o pai continua no cicli while
onde chama a função wait
. A função wait
pára o processo dos pais e espera até que qualquer um dos processos da criança termine.
Note que é necessário terminar cada processo de criança para que o pai saia normalmente. Se interromper o processo dos pais, os processos dos filhos continuarão a correr, e os seus pais tornam-se um processo do sistema.
#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;
}
O código fonte do processo infantil (ficheiro diferente):
#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);
}
Utilize fork()
e execve
para criar processos múltiplos com a função de limpeza automática de crianças em C++
O código do exemplo anterior tem um comportamento desajeitado se o processo dos pais foi terminado antes de todas as crianças terem saído. Neste caso, adicionamos a função de manipulador de sinal ao processo dos pais que terminará automaticamente todos os processos das crianças assim que o sinal SIGQUIT
for recebido. Utilize o comando kill -SIGQUIT pid_num_of_parent
para enviar o sinal.
Note-se que algumas das variáveis globais que precisam de ser acedidas no manipulador do sinal são declaradas como tipos std::atomic
, que é o requisito rigoroso para a correcção do programa.
#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