运行环境
- win11 linux子系统Ubuntu2204
- g++ 11.3.0
Linux DESKTOP-XXXXX 5.15.79.1-microsoft-standard-WSL2 #1 SMP Wed Nov 23 01:01:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
开启子进程的方式
有三种方式可以开启子进程,下文只使用fork,不使用exec函数族
- pid_t fork();
- pid_t vfork();
- exec函数族
fork示例
// 创建子进程
pid_t pid = fork();
if (0 == pid)
{
// 子进程
}
else if (0 < pid)
{
// 父进程
}
else
{
// fork 失败
}
父进程crash或者被kill
父进程被杀死,子进程可以接受信号SIGKILL,然后跟随一起退出,代码如下
#include <iostream>
#include <thread>
#include <signal.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
// 创建子进程
pid_t pid = fork();
if (0 == pid)
{
// 子进程
prctl(PR_SET_PDEATHSIG, SIGKILL);// 父进程死亡,自动给子进程发送SIGKILL信号
}
else if (0 < pid)
{
// 父进程
}
else
{
// fork 失败
}
return 0;
}
子进程crash或者被kill
父进程检测到子进程异常退出并重新拉起来,主要是向系统注册信号SIGCHLD事件
void onSignal(int signal)
{
std::cout << "onSignal, signal=" << signal << std::endl;
if (SIGCHLD == signal)
{
// 回收子进程资源
int status = -1;
while (waitpid(-1, &status, WNOHANG) > 0);
// 重新fork创建子进程,或者do something else
}
}
int main(int argc, char* argv[])
{
// 创建子进程
pid_t pid = fork();
if (0 == pid)
{
// 子进程
begin_child_proc(argc, argv);
}
else if (0 < pid)
{
// 父进程
signal(SIGCHLD, onSignal);
}
else
{
// fork 失败
}
return 0;
}
完整代码
/*************************************************************************
> File Name: proc_demo.cpp
> Author: livio
> Mail: yuanfeng@outlook.com
> Created Time: Tue 13 Dec 2022 04:57:07 PM CST
************************************************************************/
#include <iostream>
#include <thread>
#include <signal.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/wait.h>
//using namespace std;
struct MAINARGS
{
int argc;
char **argv;
};
static MAINARGS gs_args;
void print_args(int argc, char* argv[]);
void print_pid();
int begin_child_proc(int argc, char* argv[]);
void onSignal(int signal);
int main(int argc, char* argv[])
{
// 记录主函数参数
gs_args.argc = argc;
gs_args.argv = argv;
// 创建子进程
pid_t pid = fork();
if (0 == pid)
{
// 子进程
begin_child_proc(argc, argv);
}
else if (0 < pid)
{
// 父进程
print_args(argc, argv);
print_pid();
signal(SIGCHLD, onSignal);
}
else
{
// fork 失败
return -1;
}
auto ch = getchar();
while (true)
{
if (ch == 'q')
{
std::cout << "parent exit\n";
break;
}
auto ch = getchar();
}
return 0;
}
void print_args(int argc, char *argv[])
{
for (int i = 0; i < argc; ++i)
{
std::cout << argv[i] << ' ';
}
std::cout << "\n";
}
void print_pid()
{
std::cout << "pid=" << getpid();
std::cout << "\tppid=" << getppid();
std::cout << '\n';
}
int begin_child_proc(int argc, char* argv[])
{
print_args(argc, argv);
print_pid();
// 父进程死亡,自动给子进程发送SIGKILL信号
prctl(PR_SET_PDEATHSIG, SIGKILL);
// 休眠5秒后退出
int i = 5;
while (i-- > 0)
{
std::cout << "i = " << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
//子进程正常退出不会被拉起来,crash或者被kill,都会被父进程拉起来
throw std::runtime_error{ "child process error." };
// std::cout << "child exit\n";
return 0;
}
void onSignal(int signal)
{
std::cout << "onSignal, signal=" << signal << std::endl;
if (SIGCHLD == signal)
{
// 回收子进程资源
int status = -1;
while (waitpid(-1, &status, WNOHANG) > 0);
// 重新拉起子进程
if (0 == fork())
{
// 子进程
begin_child_proc(gs_args.argc, gs_args.argv);
}
}
}
运行结果
./a.out
pid=2598 ppid=11
./a.out
pid=2599 ppid=2598
i = 4
i = 3
i = 2
i = 1
i = 0
terminate called after throwing an instance of 'std::runtime_error'
what(): child process error.
onSignal, signal=17
./a.out
pid=2600 ppid=2598
i = 4
i = 3
i = 2
i = 1
i = 0
terminate called after throwing an instance of 'std::runtime_error'
what(): child process error.
onSignal, signal=17
./a.out
pid=2601 ppid=2598
i = 4
i = 3
i = 2