在受控环境下运行应用程序的问题(Win32)

时间:2021-12-23 23:05:14

I'm not exactly sure how to tag this question or how to write the title, so if anyone has a better idea, please edit it

我不确定如何标记这个问题或如何写标题,所以如果有人有更好的想法,请编辑它

Here's the deal:

这是交易:

Some time ago I had written a little but cruicial part of a computing olympiad management system. The system's job is to get submissions from participants (code files), compile them, run them against predefined test cases, and return results. Plus all the rest of the stuff you can imagine it should do.

前段时间我曾写过一个计算奥林匹克管理系统的一小部分但非常重要的部分。系统的工作是从参与者(代码文件)获取提交,编译它们,针对预定义的测试用例运行它们,并返回结果。加上你可以想象的其他所有东西。

The part I had written was called Limiter. It was a little program whose job was to take another program and run it in a controlled environment. Controlled in this case means limitations on available memory, computing time and access to system resources. Plus if the program crashes I should be able to determine the type of the exception and report that to the user. Also, when the process terminated, it should be noted how long it executed (with a resolution of at least 0.01 seconds, better more).

我写的部分叫做Limiter。这是一个小程序,其工作是采取另一个程序并在受控环境中运行它。在这种情况下受控制意味着对可用内存,计算时间和对系统资源的访问的限制。此外,如果程序崩溃,我应该能够确定异常的类型并将其报告给用户。此外,当进程终止时,应该注意它执行了多长时间(分辨率至少为0.01秒,更好)。

Of course, the ideal solution to this would be virtualization, but I'm not that experienced to write that.

当然,理想的解决方案是虚拟化,但我没有那么经验。

My solution to this was split into three parts.

我对此的解决方案分为三个部分。

The simplest part was the access to system resources. The program would simply be executed with limited access tokens. I combined some of the basic (Everyone, Anonymous, etc.) access tokens that are available to all processes in order to provide practically a read-only access to the system, with the exception of the folder it was executing in.

最简单的部分是访问系统资源。该程序将仅使用有限的访问令牌执行。我结合了一些可用于所有进程的基本(Everyone,Anonymous等)访问令牌,以便实际上提供对系统的只读访问权限,但正在执行的文件夹除外。

The limitation of memory was done through job objects - they allow to specify maximum memory limit.

内存的限制是通过作业对象完成的 - 它们允许指定最大内存限制。

And lastly, to limit execution time and catch all the exceptions, my Limiter attaches to the process as a debugger. Thus I can monitor the time it has spent and terminate it if it takes too long. Note, that I cannot use Job objects for this, because they only report Kernel Time and User Time for the job. A process might do something like Sleep(99999999) which would count in none of them, but still would disable the testing machine. Thus, although I don't count a processes idle time in its final execution time, it still has to have a limit.

最后,为了限制执行时间并捕获所有异常,我的Limiter作为调试器附加到进程。因此,如果花费的时间太长,我可以监控它花费的时间并终止它。请注意,我不能使用Job对象,因为它们只报告作业的内核时间和用户时间。一个进程可能会执行类似Sleep(99999999)的操作,这些操作不会计入其中,但仍会禁用测试计算机。因此,虽然我没有在最后执行时间内计算进程空闲时间,但它仍然必须有一个限制。

Now, I'm no expert in low-level stuff like this. I spent a few days reading MSDN and playing around, and came up with a solution as best I could. Unfortunately it seems it's not running as well as it could be expected. For most part it seems to work fine, but weird cases keep creeping up. Just now I have a little C++ program which runs in a split second on its own, but my Limiter reports 8 seconds of User mode time (taken from job counters). Here's the code. It prints the output in about half a second and then spends more than 7 seconds just waiting:

现在,我不是这种低级别的专家。我花了几天时间阅读MSDN并玩游戏,并尽可能地提出了解决方案。不幸的是,它似乎没有像预期的那样运行。在大多数情况下,它似乎工作正常,但奇怪的情况继续爬升。刚才我有一个小C ++程序,它自己运行一瞬间,但我的Limiter报告8秒的用户模式时间(取自作业计数器)。这是代码。它打印输出大约半秒钟,然后花费超过7秒等待:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector< vector<int> > dp(50000, vector<int>(4, -1));
    cout << dp.size();
}

The code of the limiter is pretty lengthy, so I'm not including it here. I also feel that there might be something wrong with my approach - perhaps I shouldn't do the debugger stuff. Perhaps there are some common pitfalls that I don't know of.

限制器的代码非常冗长,所以我不在此处。我也觉得我的方法可能有问题 - 也许我不应该做调试器的东西。也许有一些我不知道的常见陷阱。

I would like some advice on how other people would tackle this problem. Perhaps there is already something that does this, and my Limiter is obsolete?

我想就其他人如何解决这个问题提出一些建议。也许已经有了这样做,我的Limiter已经过时了?


Added: The problem seems to be in the little program that I posted above. I've opened a new question for it, since it is somewhat unrelated. I'd still like comments on this approach for limiting a program.

1 个解决方案

#1


Running with a debugger attached can change the characteristics of the application. Performance can be impacted, and code paths can even change (if the target process does things based on the presence of a debugger, i.e. IsDebuggerPresent).

使用附加的调试器运行可以更改应用程序的特征。性能可能会受到影响,代码路径甚至可能会发生变化(如果目标进程基于调试器的存在而执行操作,即IsDebuggerPresent)。

A different approach that we've used is to configure our own application to run as the JIT debugger. By setting the AeDebug registry key, you can control what debugger is invoked when an application crashes. This way you only jump in when the target process crashes, and it doesn't impact the process during normal run-time.

我们使用的另一种方法是将我们自己的应用程序配置为作为JIT调试器运行。通过设置AeDebug注册表项,您可以控制应用程序崩溃时调用的调试器。这样,您只能在目标进程崩溃时跳转,并且在正常运行时不会影响进程。

This site has some details about setting the postmortem debugger: Configuring Automatic Debugging.

此站点提供了有关设置事后调试程序的一些详细信息:配置自动调试。

Your approaches for limiting the memory, getting timing etc. all sound perfectly fine.

你限制记忆,获得计时等的方法听起来都很好。

#1


Running with a debugger attached can change the characteristics of the application. Performance can be impacted, and code paths can even change (if the target process does things based on the presence of a debugger, i.e. IsDebuggerPresent).

使用附加的调试器运行可以更改应用程序的特征。性能可能会受到影响,代码路径甚至可能会发生变化(如果目标进程基于调试器的存在而执行操作,即IsDebuggerPresent)。

A different approach that we've used is to configure our own application to run as the JIT debugger. By setting the AeDebug registry key, you can control what debugger is invoked when an application crashes. This way you only jump in when the target process crashes, and it doesn't impact the process during normal run-time.

我们使用的另一种方法是将我们自己的应用程序配置为作为JIT调试器运行。通过设置AeDebug注册表项,您可以控制应用程序崩溃时调用的调试器。这样,您只能在目标进程崩溃时跳转,并且在正常运行时不会影响进程。

This site has some details about setting the postmortem debugger: Configuring Automatic Debugging.

此站点提供了有关设置事后调试程序的一些详细信息:配置自动调试。

Your approaches for limiting the memory, getting timing etc. all sound perfectly fine.

你限制记忆,获得计时等的方法听起来都很好。