在 c 中用 fork 创建进程
本文将为大家讲解几种在 c 中使用 fork()
系统调用创建进程的方法。
使用 fork()
在 c 程序中创建两个进程
fork
函数是大多数基于 unix 的操作系统中可用的 posix 兼容系统调用。该函数创建了一个新的进程,它是原始调用程序的副本。后一个进程称为 parent
,新创建的进程-child
。这两个进程可以看作是在不同内存空间执行的两个线程。需要注意的是,目前 linux 的实现内部没有线程的概念,所以线程除了共享内存区域外,其他结构与进程类似。fork
函数可以实现同一程序内的并发执行,也可以从文件系统中运行一个新的可执行文件(在后面的例子中演示)。
在下面的例子中,我们利用 fork
来演示一个程序内的多进程。fork
不接受参数,并在两个进程中返回。返回值是父进程中子进程的 pid,子进程中返回 0
。如果调用失败,在父进程中返回 -1
。因此,我们可以根据返回值的评估来构造 if
语句,每个 if
块都会被相应的进程执行,从而实现并发执行。
#include #include #include 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 #include #include #include #include #include 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 #include #include 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 #include #include #include #include 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 进行申请,经作者同意之后,转载请以链接形式注明出处
本文地址:
相关文章
arduino 复位
发布时间:2024/03/13 浏览次数:315 分类:c
-
可以通过使用复位按钮,softwarereset 库和 adafruit sleepydog 库来复位 arduino。
发布时间:2024/03/13 浏览次数:181 分类:c
-
可以使用简单的方法 toint()函数和 serial.parseint()函数将 char 转换为 int。
发布时间:2024/03/13 浏览次数:151 分类:c
-
可以使用 arduino 中的循环制作计数器。