我正在为一个竞争性编程网站编写在线法官,我想检测程序是否超出了指定的内存限制,从而导致
MLE
(超出内存限制)判决。
我正在使用 python 脚本来运行程序并使用像这样的
resource.setrlimit()
应用限制
def apply_constraints(time_limit, mem_limit, stack_size):
resource.setrlimit(resource.RLIMIT_CPU, (time_limit, time_limit))
resource.setrlimit(resource.RLIMIT_AS, (mem_limit, mem_limit))
resource.setrlimit(resource.RLIMIT_STACK, (stack_size, stack_size))
这是执行命令的函数
def execute_submission(
command, time_limit, mem_limit, stack_size, input_file, output_file
):
start_time = time.time()
with open(input_file, "r") as infile, open(output_file, "w") as outfile:
process = subprocess.Popen(
command,
stdin=infile,
stdout=outfile,
stderr=subprocess.DEVNULL,
preexec_fn=apply_constraints(time_limit, mem_limit, stack_size),
)
process.communicate()
mem_used = resource.getrusage(resource.RUSAGE_CHILDREN).ru_maxrss
end_time = time.time()
return process.returncode, end_time - start_time, mem_used
现在的问题是,如果一个程序尝试分配比允许的更多的内存,它会发送一个
SIGSEGV
信号并以 -11
返回代码退出,但问题是,如果被判断的程序尝试像这样取消引用 nullptr
:
int* p = nullptr;
*p = 3;
它也以
SIGSEGV
信号终止,但对于在线判断,这应该是 运行时错误,而不是 MLE
,所以我正在寻找一种方法来区分两者。
您可能会注意到,我也在测量程序使用的内存,所以我首先考虑使用它作为它是否真的是一个指标
MLE
但如果您尝试分配一大块内存,而该内存超出了限制,由于设置的限制,内存不会分配给您的程序,因此它不会计入使用的内存中,因此,内存使用情况寄存器不会接近内存限制,即使尝试分配更多内存也是如此终止的原因,即 MLE
。这意味着,这种方法也无法尝试区分两种终止条件。
不可能。您无法自定义超出 rlimit 时发送的信号,而且内存 rlimit 很大程度上不会通过信号强制执行。尝试将堆栈增长到超出 rlimit 的程序会收到 SIGSEGV,但引用 POSIX 规范:
RLIMIT_AS
这是进程的总可用内存的最大大小(以字节为单位)。如果超过此限制,malloc() 和 mmap() 函数将失败,并将 errno 设置为 [ENOMEM]。此外,自动堆栈增长会失败并产生上述影响。
尝试超出 rlimit 的程序只会得到一个空指针,并将 errno 设置为 ENOMEM。如果程序尝试使用
malloc
返回值而不检查它,然后程序会得到一个 SIGSEGV,只是因为它取消引用了一个无效指针。