迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > C++ >

在 C++ 中用 Fork 创建进程

作者:迹忆客 最近更新:2024/01/02 浏览次数:

本文将为大家讲解几种在 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 个子进程创建完毕,父进程继续在 while 循环中调用 wait 函数。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;
}

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

如何在 C++ 中实现毫秒级的睡眠

发布时间:2024/01/02 浏览次数:124 分类:C++

本文介绍了在 C++ 中使用不同方法暂停程序执行,实现睡眠的方法。本文介绍了在 C++ 中睡眠毫秒的方法。使用 std::this_thread::sleep_for 方法在 C++ 中睡眠

如何在 C++ 中将双精度数四舍五入到整数上

发布时间:2024/01/02 浏览次数:88 分类:C++

本文演示了如何在 C++ 中把双精度数四舍五入到整数中。本文将为大家讲解几种在 C++ 中如何将双精度数四舍五入为整数的方法。使用 round() 函数将双精度数四舍五入到整数

如何在 C++ 中以毫秒为单位获取时间

发布时间:2024/01/02 浏览次数:60 分类:C++

本文介绍了如何在 C++ 中获取以毫秒为单位的时间。本文将介绍多种 C++ 方法,介绍如何以毫秒为单位获取时间。

如何在 C++ 中把 Char 数组转换为 Int

发布时间:2024/01/02 浏览次数:90 分类:C++

本文演示了在 C++ 中把 char 数组转换为 int 类型的方法。本文将介绍将 char 数组转换为 int 类型的 C++ 方法。使用 std::strtol 函数将 char 数组转换为 int 类型

如何在 C++ 中将 ASCII 码转换为字符

发布时间:2024/01/02 浏览次数:183 分类:C++

本文介绍了在 C++ 中如何将 ASCII 值转换为 char 的方法。本文将演示关于如何在 C++ 中把 ASCII 值转换为字符的多种方法。在 C++ 中使用赋值运算符将 ASCII 值转换为字符

如何在 C++ 中把十进制转换为二进制

发布时间:2024/01/02 浏览次数:71 分类:C++

本文介绍如何在 C++ 中把十进制数转换成二进制数。本文将介绍几种在 C++ 中如何将十进制数转换为二进制表示的方法。在 C++ 中使用自定义定义的函数将十进制数转换为二进制数

如何在 C++ 中把枚举型转换为字符串

发布时间:2024/01/02 浏览次数:138 分类:C++

本文演示了如何在 C++ 中把枚举转换为字符串。本文将解释几种在 C++ 中把枚举类型转换为 string 变量的方法。使用 const char*数组将枚举类型转换为字符串

在 C++ 创建一个字典

发布时间:2024/01/02 浏览次数:191 分类:C++

本文介绍了如何在 C++ 中创建一个字典。使用初始化列表构造函数在 C++ 中创建字典 在 C++ 标准容器库中,有一个名为 std::map 的字典,它实现了具有唯一键的排序键值对。

如何在 C++ 中将文本追加到文件

发布时间:2024/01/02 浏览次数:95 分类:C++

本文介绍了如何用 C++ 将文本追加到文件中的方法。使用 std::ofstream 和 open() 方法将文本追加到文件中

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便