You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python子进程中运行Rust编译器并设置内存限制时的链接错误排查

Python子进程中运行Rust编译器并设置内存限制时的链接错误排查

看起来你踩了一个很容易混淆的坑——把内存限制错加到了Rust编译器(rustc)本身,而不是你最终要运行的Rust程序上。咱们一步步拆解问题和解决方法:

问题根源分析

你当前的代码是在调用rustc编译Rust代码的子进程里,通过preexec_fn=limit_virtual_memory设置了100MB的内存限制。但你可能忽略了两个关键事实:

  • Rust编译器(尤其是链接阶段,比如你用的lld链接器)本身需要大量内存来处理符号、依赖库、代码优化等操作,100MB的限制远远不够支撑链接过程,所以直接触发了内存不足的崩溃,也就是你看到的terminate called after throwing an instance of 'std::system_error' what(): Resource temporarily unavailable错误。
  • 你的真实需求应该是限制最终生成的Rust可执行程序的运行内存,而不是限制编译它的rustc进程的内存。

修正方案:拆分编译与运行阶段的内存控制

我们需要把流程拆成完全独立的两步:

  1. 无内存限制编译Rust代码:先正常调用rustc生成可执行文件,这时候不给rustc加任何内存限制,让它有足够资源完成编译链接。
  2. 带内存限制运行生成的程序:运行编译好的可执行文件时,再通过preexec_fn设置内存限制,这才是你真正要限制的目标。

代码修改示例

调整你的test_simple_stdin_program方法,拆分编译和运行步骤:

def test_simple_stdin_program(self):
    """Test successful execution with correct memory limiting"""
    program = """
use std::io;
fn read_and_display_variables() {
    let mut input1 = String::new();
    io::stdin()
        .read_line(&mut input1)
        .expect("Error reading input 1");
    let first_variable = input1.trim();
    let mut input2 = String::new();
    io::stdin()
        .read_line(&mut input2)
        .expect("Error reading input 2");
    let second_variable = input2.trim();
    println!("{}", first_variable);
    println!("{}", second_variable);
}
fn main() {
    read_and_display_variables();
}
"""
    cwd = "/home/jovyan/ipetrov/bench/LiveCodeBench-mult/tests/assets"
    code_path = os.path.join(cwd, "main.rs")
    exec_name = os.path.join(cwd, "main")

    # 先把代码写入文件
    with open(code_path, "w") as f:
        f.write(program)

    # --------------------------
    # 第一步:编译Rust代码(无内存限制)
    # --------------------------
    args_compile = ["rustc", str(code_path), "-o", exec_name]
    new_env = {
        'PATH': os.environ['PATH'],
        'GCC': os.environ['GCC'],
        'CXX': os.environ['CXX'],
        'CXXFLAGS': os.environ['CXXFLAGS'],
        'LD_LIBRARY_PATH': os.environ['LD_LIBRARY_PATH'],
        'LD': os.environ['LD'],
        'RUST_BACKTRACE': "1",
        'RUST_LIB_BACKTRACE': "1"
    }

    print(f"Compiling Rust code on {platform.uname().system}...")
    p_compile = subprocess.Popen(
        args_compile,
        env=new_env,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        cwd=cwd
    )
    compile_stdout, compile_stderr = p_compile.communicate(timeout=TIMEOUT)
    try:
        compile_stdout = compile_stdout.decode("utf-8")
        compile_stderr = compile_stderr.decode("utf-8")
    except UnicodeDecodeError:
        compile_stderr = compile_stderr.decode("latin-1")

    print("Compile stdout:\n", compile_stdout)
    print("Compile stderr:\n", compile_stderr)
    # 确保编译阶段无错误
    assert p_compile.returncode == 0, f"Compilation failed with code {p_compile.returncode}: {compile_stderr}"

    # --------------------------
    # 第二步:运行生成的可执行文件(设置内存限制)
    # --------------------------
    input_data = b"test_input_1\ntest_input_2\n"  # 对应Rust程序的stdin输入(需为bytes类型)
    p_run = subprocess.Popen(
        [exec_name],
        env=new_env,
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        preexec_fn=limit_virtual_memory  # 仅在运行阶段应用内存限制
    )
    set_nonblocking(p_run.stdout)
    set_nonblocking(p_run.stderr)
    run_stdout, run_stderr = p_run.communicate(input=input_data, timeout=TIMEOUT)

    try:
        run_stdout = run_stdout.decode("utf-8").strip()
        run_stderr = run_stderr.decode("utf-8")
    except UnicodeDecodeError:
        run_stderr = run_stderr.decode("latin-1")

    print("Run stdout:\n", run_stdout)
    print("Run stderr:\n", run_stderr)
    # 验证运行结果符合预期
    assert run_stdout == "test_input_1\ntest_input_2", f"Unexpected output: {run_stdout}"
    assert not run_stderr, f"Runtime error: {run_stderr}"

    # 清理临时文件
    os.remove(code_path)
    os.remove(exec_name)

额外优化建议

  1. 内存限制参数的合理选择
    • 如果你想限制程序的总虚拟内存(包括堆、栈、映射文件等),用RLIMIT_ASRLIMIT_DATA更合适。修改limit_virtual_memory函数:
      def limit_virtual_memory():
          # 限制总虚拟内存为100MB
          resource.setrlimit(resource.RLIMIT_AS, (MAX_PRC_VIRT_MEM, MAX_PRC_VIRT_MEM))
          if not platform.uname().system == "Darwin":
              resource.setrlimit(
                  resource.RLIMIT_STACK, (MAX_PRC_STACK_MEM, MAX_PRC_STACK_MEM)
              )
      
  2. 输入数据的正确性:Rust程序的read_line会等待换行符,所以你需要传入包含换行的bytes数据,否则程序会卡在stdin读取阶段超时。
  3. 编译阶段的错误处理:编译失败时要保留错误信息,方便排查问题,比如添加断言检查返回码和错误输出。

这样修改后,就能同时实现你的两个目标:正常编译Rust代码,并且限制最终Rust程序的运行内存啦!

火山引擎 最新活动