【问题标题】:Graceful signal handling in slurmslurm 中的优雅信号处理
【发布时间】:2021-07-01 17:18:03
【问题描述】:

我在优雅地退出我的 slurm 作业以保存数据等方面遇到问题。

我的程序中有一个信号处理程序,它设置一个标志,然后在主循环中查询该标志,然后优雅退出并保存数据。一般的方案是这样的:

#include <utility>
#include <atomic>
#include <fstream>
#include <unistd.h>

namespace {
    std::atomic<bool> sigint_received = false;
}

void sigint_handler(int) {
    sigint_received = true;
}

int main() {
    std::signal(SIGTERM, sigint_handler);

    while(true) {
        usleep(10);  // There are around 100 iterations per second
        if (sigint_received)
            break;
    }

    std::ofstream out("result.dat");
    if (!out)
        return 1;
    out << "Here I save the data";

    return 0;
}

不幸的是,批处理脚本很复杂,因为:

  • 我想要数百个并行的、低线程数的独立任务,但我的集群每个用户只允许 16 个作业
  • srun 在我的集群中总是声称一个完整的节点,即使我不想要所有的核心,所以为了在单个节点上运行多个进程,我必须使用 bash

正因为如此,批处理脚本才会如此混乱(4 个进程的 2 个节点):

#!/bin/bash -l
#SBATCH -N 2
#SBATCH more slurm stuff, such as --time, etc.

srun -N 1 -n 1 bash -c '
    ./my_program input1 &
    ./my_program input2 &
    wait
' &

srun -N 1 -n 1 bash -c '
    ./my_program input3 &
    ./my_program input4 &
    wait
' &

wait

现在,为了传播 slurm 发送的信号,我遇到了更大的混乱(按照this 的回答,特别是双重等待):

#!/bin/bash -l
#SBATCH -N 2
#SBATCH more slurm stuff, such as --time, etc.

trap 'kill $(jobs -p) && wait' TERM

srun -N 1 -n 1 bash -c '
    trap '"'"'kill $(jobs -p) && wait'"'"' TERM
    ./my_program input1 &
    ./my_program input2 &
    wait
' &

srun -N 1 -n 1 bash -c '
    trap '"'"'kill $(jobs -p) && wait'"'"' TERM
    ./my_program input3 &
    ./my_program input4 &
    wait
' &

wait

大部分情况下它都在工作。但是,首先,我在输出结束时收到错误消息:

run: error: nid00682: task 0: Exited with exit code 143
srun: Terminating job step 732774.7
srun: error: nid00541: task 0: Exited with exit code 143
srun: Terminating job step 732774.4
...

而且,更糟糕的是,在if (!out) - errno 上实际上有 300 多个进程中有 4-6 个失败了“系统调用中断”。同样,在this 的指导下,我猜我的信号处理程序被调用了两次——第二次是在std::ofstream 构造函数下的某个系统调用期间。

现在,

  1. 如何摆脱 slurm 错误并真正优雅地退出?
  2. 我是否正确发送了两次信号?如果是,为什么,我该如何解决?

【问题讨论】:

  • srun 是否保留环境变量?
  • @KamilCul 我相信是的,它与 OMP_NUM_THREADS 一起使用

标签: c++ linux bash slurm signal-handling


【解决方案1】:

建议:

  • 陷阱退出,不是信号。 EXIT 发生一次,TERM 可以多次交付。
  • 使用declare -f 传输代码并使用declare -p 将变量传输到不相关的子shell
  • kill 可以失败,我认为你不应该 &amp;&amp; 就可以了
  • 使用xargs(或parallel)而不是用kill $(jobs -p)重新发明轮子
  • 从“代码”(待完成的工作)中提取“数据”(input1 input2 ...

一些东西:

# The input.
input="$(cat <<'EOF'
input1
input2
input3
input4
EOF
)"

work() {
   # Normally write work to be done.
   # For each argument, run `my_program` in parallel.
   printf "%s\n" "$@" | xargs -d'\n' -P0 ./my_program
}

# For each two arguments run `srun....` with a shell that runs `work` in parallel.
# Note - declare -f outputs source-able definition of the function.
# "No more hand escaping!"
# Then the work function is called with arguments passed by xargs inside the spawned shell.
xargs -P0 -n2 -d'\n' <<<"$input" \
      srun -N 1 -n 1 \
      bash -c "$(declare -f work)"'; work "$@"' --

-P0 特定于 GNU xargs。 GNU xargs 专门处理退出状态 255,如果您希望 xargs 在任何程序失败时终止,您可以编写像 xargs ... bash -c './my_program "$@" || exit 255' -- || exit 255 这样的包装器。

如果srun 保留环境变量,则导出工作函数export -f work 并在子shell 中调用它,如xargs ... srun ... bash -c 'work "$@"' --

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-02
    • 2011-02-02
    • 1970-01-01
    • 2015-03-16
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多