使用system()创建独立的子进程

Use system() to create independent child process

本文关键字:独立 子进程 创建 system 使用      更新时间:2023-10-16

我编写了一个程序,在该程序中,我在main中创建了一个线程,并使用system()从该线程启动另一个进程。此外,我还使用主函数中的system()启动了相同的过程。从线程启动的进程似乎仍然活着,即使父进程死了。但是从主函数调用的一个函数将随父函数一起消亡。知道为什么会发生这种情况吗。

请在下面找到代码结构:

void *thread_func(void *arg)
{
system(command.c_str());        
}
int main()
{
pthread_create(&thread_id, NULL, thread_func, NULL);
.... 
system(command.c_str());
while (true)
{
....
}
pthread_join(thread_id, NULL);
return 0;
}

我的建议是:不要做你所做的。如果你想创建一个独立运行的子进程,请研究forkexec家族函数。这就是system将在"引擎盖下"使用的内容。

线程并不像进程那样真正独立。当"主"进程结束时,所有线程也将结束。在您的特定情况下,线程似乎继续运行,而主进程似乎因为pthread_join调用而结束,它只需等待线程退出。如果删除联接调用,线程(和您的"命令"(将终止。

有一些方法可以分离线程,这样它们就可以更独立地运行(例如,你不必加入分离的线程(,但主进程仍然无法结束,相反,你必须结束主线程,只要有分离的线程在运行,这将使进程保持运行。


使用forkexec实际上很简单,但不是很复杂:

int pid = fork();
if (pid == 0)
{
// We are in the child process, execute the command
execl(command.c_str(), command.c_str(), nullptr);
// If execl returns, there was an error
std::cout << "Exec error: " << errno << ", " << strerror(errno) << 'n';
// Exit child process
exit(1);
}
else if (pid > 0)
{
// The parent process, do whatever is needed
// The parent process can even exit while the child process is running, since it's independent
}
else
{
// Error forking, still in parent process (there are no child process at this point)
std::cout << "Fork error: " << errno << ", " << strerror(errno) << 'n';
}

要使用的exec的确切变体取决于command。如果它是可执行程序的有效路径(绝对路径或相对路径(,则execl运行良好。如果是PATH中的"命令",则使用execlp

这里有两点我认为你错过了:

首先,system是一个同步呼叫。这意味着,您的程序(或者至少是调用system的线程(等待子进程完成。因此,如果command是长时间运行的,那么主线程和工作线程都将被阻塞,直到它完成。

其次,您正在"加入"main末尾的工作线程。这是正确的做法,因为除非你连接或分离线程,否则你会有未定义的行为。然而,这并不是你真正想要做的。最终的结果并不是在你的主进程结束后,子进程继续。。。你的主要过程仍然存在!它在pthread_join调用上被阻止,该调用正试图包装仍在运行command的工作线程。

一般来说,假设你想生成一个与主进程完全无关的新进程,线程不是实现这一点的方法。即使你要分离线程,它仍然属于你的进程,你仍然需要在进程终止之前让它完成。不能使用线程从进程分离。

相反,您需要forkexec等操作系统功能(或围绕此功能的友好C++包装器,如Boost.Subprocess(。这是从程序中真正生成新进程的唯一方法。

但是,你可以作弊!如果command是一个shell命令,并且您的shell支持后台作业,那么您可以将&放在命令的末尾(这是Bash语法的一个示例(,以进行system调用:

  • 要求shell启动一个新进程
  • 等待它做那件事
  • 新进程现在将继续在后台运行

例如:

const std::string command = "./myLongProgram &";
//                                           ^

然而,同样,这是一种黑客攻击,并且驻留在中的适当的fork机制应该是您的程序逻辑的首选,以获得最大的可移植性和可预测性。