运行环境

  • 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