从C++程序启动可执行文件并继续

Start an executable from C++ program & continue

本文关键字:继续 可执行文件 启动 C++ 程序      更新时间:2023-10-16

我有一个用C++编写的程序,打算在Linux操作系统上运行。忽略程序的大部分内容,它可以归结为:在一段时间后启动X个可执行文件(为了简单起见,让我们使用5秒)。

目前,我使用system(path/to/executable/executable_name)来实际启动可执行文件,这对于启动可执行程序来说很好。

我还试图为每个可执行文件维护一个状态(再次为了简单起见,假设状态为"UP"或"DOWN"(正在运行或未运行))。我已经能够做到这一点。。。有点

只要稍微备份一点,当我的程序被告知启动可执行文件时,逻辑看起来像这样:

pid = fork()
if (pid < 0) exit 0; //fork failed
if (pid == 0) {
   system(path/to/executable/executable_name)
   set executable's status to DOWN
} else {
   verify executable started
   set executable's status to UP
}

这就是我的问题所在。fork()导致生成一个子进程,这正是我认为原始进程继续启动其他可执行文件所需要的我不想等待一个可执行文件停止,然后再启动另一个

但是,可执行文件在另一个子进程中启动。。。它与父进程是分开的。。。当系统返回时,如果我试图在子进程中将可执行文件的状态设置为DOWN,则父进程并不知道…

我对我可能需要做的事情有一些想法:

  • 使用线程而不是fork:创建一个新的线程来调用系统,但是父/主线程会知道新线程会改变可执行文件的状态吗
  • 使用fork和exec:但我不确定这会比我现有的更好(我已经阅读了fork和exec的手册页,但我想我对如何最好地利用exec仍然有点模糊)

有什么建议吗?

编辑1我想我最好再为这个逻辑提供一点上下文:

void startAll() {
    for each 'executable'
    call startExecutable(executable_name)
}

void startExecutable (executable_name) {
    pid = fork()
    if (pid < 0) exit 0; //fork failed
    if (pid == 0) {
        system(path/to/executable/executable_name)
        set executable's status to DOWN
        exit (1); <-- this is because once the child process's system returns, I don't want it to return to the above loop and start starting executables
    } else {
        verify executable started
        set executable's status to UP
    }
}

编辑2正如开头提到的,这是假设一个简化的设置(如果您愿意的话,这是第一次运行)。该计划不仅要处理"向上"或"向下"状态,还要处理第三种状态,即向我的程序启动的可执行文件发送消息的"STANDBY"。我最初省略了这一部分,以避免使解释复杂化,但现在我发现包含这一部分是危险的。

您需要了解fork时到底发生了什么。您正在做的是创建一个子流程,它是分叉流程的精确克隆。当前内存中的所有变量都被精确复制,并且子进程可以访问所有这些变量的所有副本。

但它们是副本,所以正如您所注意到的,fork和exec/system本身并不能处理进程间通信(IPC)。在其中一个进程中设置内存值不会改变任何其他进程(包括其父进程)中的变量,因为内存空间不同。

此外,systemexec非常相似,但对文件描述符和执行环境的控制要少得多。你实际上已经在做一个fork和exec了,这是你应该做的。

当您正确地分叉时(正如您在示例中所做的那样),您现在有两个进程,而这两个进程都不在等待另一个进程——它们只是在完全不同的代码路径中运行。你基本上想要的是让父母什么都不做,只是坐在那里等待新的程序打开,偶尔检查孩子们的状态,而孩子们想跑多久就玩多久。

有IPC解决方案,如管道和消息FIFO队列,但在您的情况下,这太过分了。在您的案例中,您只是在寻找流程管理。父对象被赋予子对象的pid。保存并使用它。你可以调用waitpid等待孩子结束,但你不希望这样。您只想让家长检查孩子的状态。一种方法是检查if kill(childPid,0) == 0。如果没有,则pid已退出,即它不再运行。您还可以查看/proc/childPid以获取各种信息。

如果你的状态没有你的问题所暗示的那么简单,你会想在分叉和执行之后研究管道。否则,您所需要的只是过程监控。

根据您的EDIT2,您仍然属于流程管理领域,而不是IPC。kill命令向进程发送信号(如果该命令为非0)。您想要的是拥有父级kill(childPid, SIGTSTP)。在子方面,您只需要使用signal命令制作一个信号处理程序。在许多其他参考文献中,请参见http://www.yolinux.com/TUTORIALS/C++Signals.html。基本上,你想要:

void sigTempStopHandler(int signum) { /* ... */ }
signal(SIGTSTP, sigTempStopHandler);

以在子代码中执行。当然,父级会知道此状态何时发送,因此可以更改状态。必要时可以使用其他信号恢复。

何时管道与信号:

管道是你可以使用的最强大的IPC——它可以让你从一个进程向另一个进程发送任何数量的数据,并且可以指向你想要的任何方向。如果你想让你的父母给孩子发"你是个很坏的男孩",它可以,孩子也可以给父母发"但总有一天我会选择你的疗养院"。(不那么轻率,您可以将任何数据,无论是文本还是二进制数据,从一个进程传递到另一个进程,包括您序列化的对象,或者如果不依赖于内存,则仅传递对象的原始数据,例如int。)

到目前为止,您所描述的是将简单的命令结构从父级发送到子级,kill非常适合这一点。孩子几乎可以很容易地发送信号,只是需要知道父母的pid才能发送信号。(这并不难——在分叉之前,保存pid:int pid = getPid();,现在子代知道了父代。)信号没有数据,它们只是非常原始的事件,但到目前为止,这听起来像是你想要的。