如何在 C 中的特定时间限制后准确终止 child 进程?
How to accurately kill child process after certain time limit in C?
我一直在尝试编写一个程序,可以在 child 超过某个 运行ning 时间后准确地终止分叉进程。该进程应该从磁盘上的可执行文件中产生。我在这里使用 linux。让我们甚至不考虑简单地在 Windows 上分叉一个进程的丑陋(我为必须维护 Windows 源代码的人感到遗憾。)
我当前的解决方案是在 child 进程中使用 setrlimit
函数,然后继续调用 execvp
以用可执行文件覆盖 child。 setrlimit
函数可以与参数 RLIMIT_CPU
一起使用,以限制进程的 CPU 运行 宁时间。本质上,分叉的 child 限制了它自己的 运行ning 时间,然后使用 execvp
启动可执行文件。
我写了一个结构 proc_timer,它存储 运行 可执行文件所需的信息:
typedef struct _proc_timer {
double limit;
char** args;
char* name;
pid_t proc;
int argc;
} proc_timer;
此结构已通过名为 make_timer
的函数正确分配和初始化。但这不是问题。
当前代码如下所示:
int status;
const pid_t proc = fork();
if (proc < 0)
return FORK_FAIL;
else if (proc == 0) {
struct rlimit rlim;
rlim.rlim_cur = rlim.rlim_max = (rlim_t)timer->limit + 1; // An extra second, since rlim_t is in seconds.
if (setrlimit(RLIMIT_CPU, &rlim) < 0)
_exit(TIME_FAILURE);
execvp(timer->name, timer->args);
_exit(NOT_FOUND);
}
timer->proc = proc; // save the PID, probably not useful though.
waitpid(proc, &status, 0); // wait for the child.
// If true, then the termination was caused by an exceeded time limit.
if (WTERMSIG(status) == SIGXCPU) {
fprintf(stderr, "We crossed the time limit!");
}
注意在上面的代码中,我必须给child进程超过limit
秒,因为struct rlimit
只能取rlim_t
类型的限制, 这是一个整数。现在,如果用户想要一个浮点数,我仍然必须向 child 进程提供整数秒数。这就是为什么我被迫写:
rlim.rlim_cur = rlim.rlim_max = (rlim_t)timer->limit + 1; // An extra second, since rlim_t is in seconds.
这显然过于近似,但我还没有考虑其他可行的选择。
以下解决方案无论如何都行不通,因为 ualarm 测量的是实际时间,而不是 CPU 时间,我在写这个问题时并不知道这一点。我只对CPU时间感兴趣。
不过,我找到了一个可能的解决方案。它涉及在调用 waitpid
之前使用 ualarm
系统调用。由于 ualarm
以微秒为单位接受参数,因此它比 setrlimit
解决方案准确得多。然后,在 waitpid
之后,我会立即通过调用 ualarm(0, 0)
来禁用警报。这样,如果parent还在等待child,超过了时间限制,parent就会被发送SIGALRM
,我就可以处理了。然而,这个解决方案有很多问题,我永远不会考虑使用它。处理信号的唯一方法是创建一个函数作为处理程序。这可以使用 sigaction
或简单地调用 signal
函数来安装处理程序来完成。但是,处理程序只能是一个接受一个参数的函数,即信号的整数 sent.There 处理程序无法知道要杀死哪个进程!
解决这个问题的唯一方法是设置一个全局变量来存储 pid,但这可能会导致问题,因为此代码将用作 GUI 应用程序的后台代码,用户应该在其中理论上可以一次多次执行此过程。为所有不同进程存储多个全局变量会使这样的解决方案无法工作。
所以,这里的最优方案是使用child中的setrlimit
函数。但是,我不能在这里准确地使用浮点数!还有其他更准确的解决方案吗?
忽略我在评论中说的:我相信你正在寻找setitimer
和ITIMER_VIRTUAL
。您可以在 execve
之前的 child 中调用它。它可以在一定的 CPU 时间过去后触发致命信号,分辨率以微秒为单位,并且(与 timer_create
和 ualarm
不同)被记录为存活 execve
。但是请注意,没有什么可以阻止该过程清除计时器本身。
您可以为每个要启动和终止的进程派生一个启动器和终止器 (l&k) 进程。
这个 l&k 进程 fork-off 稍后要杀死的实际进程,全局存储它的 pid(这是这个 l&k 进程的本地),然后设置为警报处理程序(使用 alarm()
)以杀死它事先分叉的实际过程。
为了避免竞争,l&k 进程还需要设置一个处理程序来检测子进程是否在收到警报信号之前终止。
我一直在尝试编写一个程序,可以在 child 超过某个 运行ning 时间后准确地终止分叉进程。该进程应该从磁盘上的可执行文件中产生。我在这里使用 linux。让我们甚至不考虑简单地在 Windows 上分叉一个进程的丑陋(我为必须维护 Windows 源代码的人感到遗憾。)
我当前的解决方案是在 child 进程中使用 setrlimit
函数,然后继续调用 execvp
以用可执行文件覆盖 child。 setrlimit
函数可以与参数 RLIMIT_CPU
一起使用,以限制进程的 CPU 运行 宁时间。本质上,分叉的 child 限制了它自己的 运行ning 时间,然后使用 execvp
启动可执行文件。
我写了一个结构 proc_timer,它存储 运行 可执行文件所需的信息:
typedef struct _proc_timer {
double limit;
char** args;
char* name;
pid_t proc;
int argc;
} proc_timer;
此结构已通过名为 make_timer
的函数正确分配和初始化。但这不是问题。
当前代码如下所示:
int status;
const pid_t proc = fork();
if (proc < 0)
return FORK_FAIL;
else if (proc == 0) {
struct rlimit rlim;
rlim.rlim_cur = rlim.rlim_max = (rlim_t)timer->limit + 1; // An extra second, since rlim_t is in seconds.
if (setrlimit(RLIMIT_CPU, &rlim) < 0)
_exit(TIME_FAILURE);
execvp(timer->name, timer->args);
_exit(NOT_FOUND);
}
timer->proc = proc; // save the PID, probably not useful though.
waitpid(proc, &status, 0); // wait for the child.
// If true, then the termination was caused by an exceeded time limit.
if (WTERMSIG(status) == SIGXCPU) {
fprintf(stderr, "We crossed the time limit!");
}
注意在上面的代码中,我必须给child进程超过limit
秒,因为struct rlimit
只能取rlim_t
类型的限制, 这是一个整数。现在,如果用户想要一个浮点数,我仍然必须向 child 进程提供整数秒数。这就是为什么我被迫写:
rlim.rlim_cur = rlim.rlim_max = (rlim_t)timer->limit + 1; // An extra second, since rlim_t is in seconds.
这显然过于近似,但我还没有考虑其他可行的选择。
以下解决方案无论如何都行不通,因为 ualarm 测量的是实际时间,而不是 CPU 时间,我在写这个问题时并不知道这一点。我只对CPU时间感兴趣。
不过,我找到了一个可能的解决方案。它涉及在调用 waitpid
之前使用 ualarm
系统调用。由于 ualarm
以微秒为单位接受参数,因此它比 setrlimit
解决方案准确得多。然后,在 waitpid
之后,我会立即通过调用 ualarm(0, 0)
来禁用警报。这样,如果parent还在等待child,超过了时间限制,parent就会被发送SIGALRM
,我就可以处理了。然而,这个解决方案有很多问题,我永远不会考虑使用它。处理信号的唯一方法是创建一个函数作为处理程序。这可以使用 sigaction
或简单地调用 signal
函数来安装处理程序来完成。但是,处理程序只能是一个接受一个参数的函数,即信号的整数 sent.There 处理程序无法知道要杀死哪个进程!
解决这个问题的唯一方法是设置一个全局变量来存储 pid,但这可能会导致问题,因为此代码将用作 GUI 应用程序的后台代码,用户应该在其中理论上可以一次多次执行此过程。为所有不同进程存储多个全局变量会使这样的解决方案无法工作。
所以,这里的最优方案是使用child中的setrlimit
函数。但是,我不能在这里准确地使用浮点数!还有其他更准确的解决方案吗?
忽略我在评论中说的:我相信你正在寻找setitimer
和ITIMER_VIRTUAL
。您可以在 execve
之前的 child 中调用它。它可以在一定的 CPU 时间过去后触发致命信号,分辨率以微秒为单位,并且(与 timer_create
和 ualarm
不同)被记录为存活 execve
。但是请注意,没有什么可以阻止该过程清除计时器本身。
您可以为每个要启动和终止的进程派生一个启动器和终止器 (l&k) 进程。
这个 l&k 进程 fork-off 稍后要杀死的实际进程,全局存储它的 pid(这是这个 l&k 进程的本地),然后设置为警报处理程序(使用 alarm()
)以杀死它事先分叉的实际过程。
为了避免竞争,l&k 进程还需要设置一个处理程序来检测子进程是否在收到警报信号之前终止。