自定义发送到子进程的终止信号,超出Python中`resource.setrlimit()`设置的限制

问题描述 投票:0回答:1

我正在为一个竞争性编程网站编写在线法官,我想检测程序是否超出了指定的内存限制,从而导致

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
。这意味着,这种方法也无法尝试区分两种终止条件。

python linux unix process signals
1个回答
1
投票

不可能。您无法自定义超出 rlimit 时发送的信号,而且内存 rlimit 很大程度上不会通过信号强制执行。尝试将堆栈增长到超出 rlimit 的程序会收到 SIGSEGV,但引用 POSIX 规范:

RLIMIT_AS
这是进程的总可用内存的最大大小(以字节为单位)。如果超过此限制,malloc() 和 mmap() 函数将失败,并将 errno 设置为 [ENOMEM]。此外,自动堆栈增长会失败并产生上述影响。

尝试超出 rlimit 的程序只会得到一个空指针,并将 errno 设置为 ENOMEM。如果程序尝试使用

malloc
返回值而不检查它,
然后
程序会得到一个 SIGSEGV,只是因为它取消引用了一个无效指针。

© www.soinside.com 2019 - 2024. All rights reserved.