slotd

slotd 是一个使用 Rust 实现的单节点、单用户、具备 Slurm 风格命令界面的调度器。
其他语言文档:
它面向的是一台工作站,而不是集群。目标是在保留常见 Slurm 命令名和主要选项的同时,显著简化运行模型。
- 一个本地 daemon
- 一个 SQLite 数据库
- 一个执行主机
- 一个本地用户工作流
你通过下面这些熟悉的命令来使用 slotd。
sbatchsrunsallocsqueuesacctscontrolscancelsinfo
适合的场景
slotd 适合以下用途。
- 本地实验队列
- 长时间运行的 CPU / GPU 作业
- 单机批处理流水线
- 带资源预留的交互式工作
- 工作站上的轻量级 Slurm 风格接口
它不打算提供以下能力。
- 多节点调度
- 集群管理
- account、QoS 或 fairshare
- 跨主机 federation 或 reservation
主要特性
- 作为单一 Rust 二进制程序构建
- 使用 daemon 与 Unix domain socket
- 使用 SQLite 持久化状态
- 调度 CPU、内存和 GPU 预留
- 支持批处理作业、数组作业、交互执行、allocation 与 step
- 支持延迟启动、单次 requeue、依赖关系以及本地 feature constraint
文档目录
安装
运行要求
- Linux 或 WSL
- 带
cargo的 Rust toolchain - 如果希望自动管理 daemon,则需要
systemd --user - 如果希望自动检测 GPU,则需要
nvidia-smi
克隆仓库
git clone https://github.com/ymgaq/slotd.git
cd slotd
使用附带脚本安装
在仓库根目录运行:
./scripts/install.sh
默认会执行以下操作:
- 以 release 模式构建
slotd - 将
slotd安装到~/.local/bin - 创建
sbatch、squeue等命令别名 - 在
~/.local/share/slotd下创建 runtime root - 写入
~/.config/slotd/slotd.env - 安装并启动
systemd --user服务
安装脚本选项
| 选项 | 说明 | 默认值 |
|---|---|---|
--repo-root PATH | 从其他仓库根目录构建 | 当前仓库 |
--profile NAME | 使用的 Cargo profile | release |
--install-bin-dir PATH | 二进制和别名安装目录 | ~/.local/bin |
--runtime-root PATH | 作为 SLOTD_ROOT 的 runtime root | ~/.local/share/slotd |
--config-dir PATH | 配置目录 | ~/.config/slotd |
--systemd-user-dir PATH | user unit 目录 | ~/.config/systemd/user |
--cpu-partitions VALUE | 写入 SLOTD_CPU_PARTITIONS 的值 | cpu |
--gpu-partitions VALUE | 写入 SLOTD_GPU_PARTITIONS 的值 | gpu |
--features VALUE | 写入 SLOTD_FEATURES 的值 | 未设置 |
--notify-cmd VALUE | 写入 SLOTD_NOTIFY_CMD 的值 | 未设置 |
--cgroup-base PATH | 写入 SLOTD_CGROUP_BASE 的值 | 未设置 |
--skip-build | 复用已有构建产物 | off |
--skip-systemd | 不安装或启动 user service | off |
--uninstall | 删除已安装内容 | off |
--purge-runtime | 卸载时删除持久化状态 | off |
示例:
./scripts/install.sh \
--features cpu,gpu \
--notify-cmd 'notify-send "slotd" "$SLOTD_JOB_ID $SLOTD_JOB_STATE"'
如果使用 --cgroup-base,请传入一个可写的 cgroup v2 subtree。若保持未设置,
CPU 与内存仍然只按预留值参与调度。
卸载
删除安装内容:
./scripts/install.sh --uninstall
删除安装内容和 runtime 状态:
./scripts/install.sh --uninstall --purge-runtime
手动安装
如果不想使用安装脚本,也可以直接构建并运行 slotd:
cargo build --release
SLOTD_ROOT="$HOME/.local/share/slotd" ./target/release/slotd daemon
然后在另一个 shell 中使用相同的 SLOTD_ROOT:
SLOTD_ROOT="$HOME/.local/share/slotd" ./target/release/slotd sbatch --wrap 'echo hello'
Runtime 文件
默认的 runtime root 为:
~/.local/share/slotd
重要文件和目录:
run/slotd.socklib/state.dblib/jobs/<job_id>/
client 与 daemon 必须使用相同的 SLOTD_ROOT。
快速开始
1. 验证 daemon
如果你通过脚本安装,并且没有使用 --skip-systemd,daemon 应该已经启动。
先检查基础命令:
sinfo
squeue
sacct
首次运行时的典型结果:
sinfo会为每个已配置分区显示一行squeue为空sacct为空
2. 提交一个简单的批处理作业
sbatch --wrap 'echo hello from slotd'
典型输出:
Submitted batch job 1
3. 查看队列
squeue
作业运行时的典型输出:
JOBID | PARTITION | NAME | USER | ST | TIME | NODELIST(REASON)
1 | cpu | wrap | ... | R | 0:00 | localhost
4. 查看已完成作业
sacct
作业完成后的典型输出:
JobID | Partition | JobName | User | State | ExitCode
1 | cpu | wrap | ... | COMPLETED | 0:0
5. 查看详细作业信息
scontrol show job 1
这里会显示:
- 作业标识与所有者
- 作业状态与 reason
- 请求的资源
- 输出路径
- 工作目录
- 各类时间戳
6. 试一下交互执行
srun --label --unbuffered -- echo hello
典型输出:
0: hello
运行模型
高层模型
slotd 是一个单主机调度器。它的运行模型刻意保持简单:
- 一个本地 daemon
- 一个本地 SQLite 数据库
- 一个本地执行主机
- 一个本地用户工作流
没有 controller/worker 拆分,也没有远程节点启动协议。
核心资源
slotd 调度三类资源:
- CPU
- 内存
- GPU
当前行为:
- CPU 预留量等于
ntasks * cpus-per-task ntasks会在 batch 和前台执行中按 task rank 启动一个本地进程- 总内存默认从
/proc/meminfo的MemTotal检测,失败时回退到16384 MB - 内存以 MB 保存
- GPU 以整数 slot 表示
- 准入基于预留量,而不是实际使用量
- 如果
SLOTD_CGROUP_BASE未设置,CPU 和内存仍然只是预留值 - 如果
SLOTD_CGROUP_BASE指向可写的 cgroup v2 subtree,slotd会写入memory.max与cpu.max - 显式启用后如果 cgroup 设置失败,作业启动会失败,而不是静默跳过 enforcement
分区
通过环境变量配置:
SLOTD_CPU_PARTITIONSSLOTD_GPU_PARTITIONS
规则:
- 只接受已配置的分区名称
- 如果没有 GPU,则不会暴露 GPU 分区
- 如果选择了 GPU 分区且省略
--gpus,默认 GPU 请求为1 - 否则默认 GPU 请求为
0 - CPU/GPU 分区是为了使用方便而划分的同一台本地主机的虚拟视图
- CPU 和内存容量在分区之间共享,分区差异主要体现在 GPU 可见性和默认值
GPU 检测
如果没有设置 SLOTD_GPU_COUNT,slotd 会尝试通过 nvidia-smi 检测 GPU。
当前实现会检查:
nvidia-smi/usr/bin/nvidia-smi/usr/lib/wsl/lib/nvidia-smi/bin/nvidia-smi
作业类型
持久化记录分为以下几类:
- 顶层 batch job
- 仅 allocation 的 job
- array task
- allocation 下的 step
作业状态
已实现的状态:
PENDINGRUNNINGCOMPLETINGCOMPLETEDFAILEDCANCELLEDTIMEOUTOUT_OF_MEMORY
终态:
COMPLETEDFAILEDCANCELLEDTIMEOUTOUT_OF_MEMORY
调度规则
daemon 循环每 300ms 运行一次。
pending job 可能被以下条件阻塞:
- dependency
- array concurrency limit
- delayed start time
- exclusive host use
- 预留资源不足
- user hold state
排序规则:
- 基础规则是提交顺序
- 显式 job priority 可以覆盖纯提交顺序
- array task 会按 array group 交错执行
Runtime 文件
在 SLOTD_ROOT 下:
run/slotd.sock: daemon socketlib/state.db: SQLite 状态库lib/jobs/<job_id>/script.sh: batch 脚本lib/jobs/<job_id>/runner.sh: daemon wrapperlib/jobs/<job_id>/exit_status: wrapper 退出状态
通知
如果设置了 SLOTD_NOTIFY_CMD,slotd 会在顶层 job 进入终态时执行它。
导出的变量:
SLOTD_JOB_IDSLOTD_JOB_NAMESLOTD_JOB_STATESLOTD_JOB_PARTITIONSLOTD_JOB_REASON
sbatch 批处理作业
形式
sbatch [options] <script>
sbatch [options] --wrap '<command>'
sbatch 的作用
sbatch 会创建一个持久化的 batch job 记录,并把它提交给本地 daemon。
在 script 模式下:
- 从磁盘读取脚本
- 将脚本内容保存到 job 目录
- 解析前导
#SBATCH指令
在 --wrap 模式下:
- 为命令生成一个内部 shell 脚本
- 当
--ntasks大于1时,会按 task rank 启动一个本地进程
典型输出:
Submitted batch job 1
使用 --parsable 时:
1
主要选项
| 选项 | 含义 |
|---|---|
--wrap <command> | 提交内联 shell 命令 |
-J, --job-name <name> | 设置作业名 |
-p, --partition <partition> | 选择分区 |
-c, --cpus-per-task <n> | 每个 task 的 CPU 数 |
-n, --ntasks <n> | 并发启动的本地 task 数量 |
--mem <size> | 请求内存,例如 512M 或 8G |
-t, --time <time> | 时间限制 |
-G, --gpus <n> | 请求 GPU slot 数 |
-o, --output <path> | stdout 路径模式 |
-e, --error <path> | stderr 路径模式 |
-D, --chdir <path> | 工作目录 |
--constraint <feature> | 要求匹配的本地 feature |
-d, --dependency <spec> | dependency 表达式 |
-a, --array <spec> | array 规格 |
--export <spec> | 向作业环境导出变量 |
--export-file <path> | 从文件加载环境变量 |
--open-mode append|truncate | 选择追加或截断输出文件 |
--signal <spec> | 在超时前发送 warning signal |
--begin <time> | 延迟作业进入可运行状态 |
--exclusive | 不与其他顶层作业共享主机 |
--requeue | 某些失败状态下只重排一次 |
--parsable | 仅输出 job ID |
-W, --wait | 等待作业完成 |
默认值
未指定时:
cpus-per-task = 1ntasks = 1mem = 512M- partition = 已配置的默认分区
- GPU 默认值在 GPU 分区为
1,否则为0
#SBATCH 支持
支持的指令:
-J,--job-name-p,--partition-c,--cpus-per-task-n,--ntasks--mem-t,--time-G,--gpus-o,--output-e,--error-D,--chdir--constraint--begin--exclusive--requeue-d,--dependency-a,--array
优先级:
- 命令行选项
SBATCH_*环境变量#SBATCH指令- 内建默认值
示例 batch script:
#!/usr/bin/env bash
#SBATCH -J script-demo
#SBATCH -p cpu
#SBATCH -c 2
#SBATCH --mem 1G
#SBATCH -t 00:05:00
#SBATCH -o logs/%j.out
echo "hello from script mode"
echo "job=$SLURM_JOB_ID cpus=$SLURM_CPUS_PER_TASK"
提交方式:
sbatch ./script-demo.sh
预期结果:
sbatch会读取脚本并应用开头的#SBATCH指令- 作业会按指定的作业名、分区、CPU 数、内存和输出路径运行
logs/<jobid>.out会包含脚本正文输出的内容
Dependencies
支持的 dependency 表达式:
after:<jobid>[,<jobid>...]afterany:<jobid>[,<jobid>...]afterok:<jobid>[,<jobid>...]afternotok:<jobid>[,<jobid>...]singleton
Arrays
支持的 array 形式:
- 单个 ID
- 范围,例如
0-7 - 带步长的范围,例如
0-15:2 - 并发限制,例如
0-31%4
示例:
sbatch -a 0-9%2 --wrap 'echo task=$SLURM_ARRAY_TASK_ID'
预期结果:
- 会持久化多个 task 记录
- 同一 array 同时最多运行两个 task
Delayed Start
--begin 支持:
- epoch 秒
YYYY-MM-DDYYYY-MM-DDTHH:MM:SSnow+<duration>
示例:
sbatch --begin now+00:10:00 --wrap 'echo delayed'
Requeue Once
--requeue 改变失败后的处理方式:
FAILED只重排一次TIMEOUT只重排一次OUT_OF_MEMORY只重排一次COMPLETED不重排CANCELLED不重排
示例:
sbatch --requeue --wrap 'exit 1'
Output Paths
路径模式中的 token:
%j: job ID%A: array job ID%a: array task ID%x: job name%u: user name%N: hostname%%: 字面%
默认值:
- 非 array 的 stdout:
slurm-%j.out - array 的 stdout:
slurm-%A_%a.out - 如果未设置
--error,stderr 默认与 stdout 相同
Environment Export
--export 支持:
ALLNONEKEY=VALUE,...
示例:
sbatch --export FOO=bar,HELLO=world --wrap 'echo "$FOO $HELLO"'
预期结果:
- 输出中包含
bar world
srun 交互执行
形式
srun [options] -- <command...>
srun 的行为
srun 默认以前台方式运行命令。
行为取决于你是否已经位于某个 allocation 内部:
- 在 allocation 内部:
- 创建一个 step 记录
- 直接前台运行命令
- 在 allocation 外部:
- 创建一个类似 allocation 的顶层记录
- 等待其可以运行
- 创建一个 step 记录
- 前台运行命令
只有 --no-wait 会提交一个由 daemon 管理的 run job。
当 --ntasks 大于 1 时,前台 srun 会在同一台主机上按 task rank 启动一个
本地进程,并导出 SLURM_PROCID 与 SLURM_LOCALID。
主要选项
| 选项 | 含义 |
|---|---|
-J, --job-name <name> | 设置作业名 |
-p, --partition <partition> | 选择分区 |
-c, --cpus-per-task <n> | 每个 task 的 CPU 数 |
-n, --ntasks <n> | 并发启动的本地 task 数量 |
--mem <size> | 请求内存 |
-t, --time <time> | 时间限制 |
-G, --gpus <n> | 请求 GPU slot 数 |
-o, --output <path> | 前台 stdout 输出路径 |
-e, --error <path> | 前台 stderr 输出路径 |
-D, --chdir <path> | 工作目录 |
--immediate | 如果资源不能立即可用则直接失败 |
--pty | 为 PTY 支持保留;当前会明确报错拒绝 |
--constraint <feature> | 要求匹配的本地 feature |
--cpu-bind <mode> | 设置 CPU affinity |
--label | 在输出前加上 <task_id>: 前缀 |
--unbuffered | 积极 flush 转发输出 |
--no-wait | 提交 daemon 管理的 run job |
输出行为
示例:
srun --label --unbuffered -- echo hello
典型输出:
0: hello
CPU Binding
支持的值:
nonecoresmap_cpu:<id,id,...>
示例:
srun --cpu-bind map_cpu:0,2 -- python train.py
Immediate Mode
--immediate 会在资源无法立即获得时直接失败,而不是等待。
示例:
srun --immediate -p gpu -G 1 -- nvidia-smi
--no-wait
--no-wait 会把 run job 提交给 daemon,而不是在前台等待。
典型输出:
Submitted run job 12
限制:
--label和--unbuffered不能与--no-wait一起使用--pty为兼容性保留并会被解析,但在真正的 PTY 路径实现之前,当前会以明确的 “not implemented yet” 错误退出
salloc 资源分配
形式
salloc [options] [command...]
salloc 的作用
salloc 会创建一个仅 allocation 的顶层 job,等待其变为可运行,然后在该 allocation 内启动一个前台命令。
如果没有提供命令,则启动你的 shell。
当 --ntasks 大于 1 时,前台命令会按 task rank 启动一个本地进程。
典型输出:
Granted job allocation 4
主要选项
| 选项 | 含义 |
|---|---|
-J, --job-name <name> | 设置 allocation 名称 |
-p, --partition <partition> | 选择分区 |
-c, --cpus-per-task <n> | 每个 task 的 CPU 数 |
-n, --ntasks <n> | 并发启动的本地 task 数量 |
--mem <size> | 请求内存 |
-t, --time <time> | 时间限制 |
-G, --gpus <n> | 请求 GPU slot 数 |
-D, --chdir <path> | 工作目录 |
--constraint <feature> | 要求匹配的本地 feature |
--immediate | allocation 无法立即启动时直接失败 |
示例
salloc -p gpu -c 4 --mem 8G -G 1 -t 00:30:00
预期结果:
- 创建一个 allocation 记录
- 命令会等待 allocation 进入运行状态
- 在 allocation 内启动你的 shell
- 之后的
srun会成为该 allocation 下的 step - allocation 命令会按 allocation 的 task 数执行本地 multi-task 启动
队列与记账信息
squeue
squeue 显示顶层排队作业和运行中的作业。
常用选项
| 选项 | 含义 |
|---|---|
--all | 显示所有状态 |
-t, --states | 按状态过滤 |
-j, --jobs | 按 job ID 过滤 |
-u, --user | 按 user 过滤 |
-p, --partition | 按 partition 过滤 |
-o, --format | 选择输出字段 |
-S, --sort | 指定排序方式 |
-l, --long | 使用长格式默认视图 |
--start | 显示预计开始时间 |
--array | 显示数组风格的 job ID |
--noheader | 省略表头 |
默认视图
JOBID | PARTITION | NAME | USER | ST | TIME | NODELIST(REASON)
长视图
JOBID | PARTITION | NAME | USER | ST | TIME | TIME_LIMIT | NTASKS | CPUS | REQ_MEM | REQ_GPU | NODELIST(REASON)
开始时间视图
当使用 --start 且未显式指定 format 时:
JOBID | PARTITION | NAME | USER | ST | START_TIME | NODELIST(REASON)
Format Fields
支持的字段名:
JobIDPartitionName,JobNameUserST,StateTime,ElapsedTimeLimit,Time_LimitNTasksCPUS,ReqCPUSReqMemReqGPU,ReqGPUSStart,StartTimeNodeList(Reason),NodeListReason,Reason,NodeList
支持的 % 代码:
%i%P%j%u%t,%T%M%S%R,%N
sacct
sacct 显示持久化的记账数据,包括已完成的作业和 step。
常用选项
| 选项 | 含义 |
|---|---|
-j, --jobs | 按 job ID 过滤 |
-s, --state | 按状态过滤 |
-S, --starttime | 按开始时间过滤 |
-E, --endtime | 按结束时间过滤 |
-u, --user | 按 user 过滤 |
-p, --partition | 按 partition 过滤 |
-o, --format | 选择输出字段 |
-P, --parsable2 | 使用 ` |
-n, --noheader | 省略表头 |
默认视图
JobID | Partition | JobName | User | State | ExitCode
记录类型
sacct 包含:
- 顶层 job
- allocation 记录
- step 记录
- 已完成记录
ID 展示规则:
- step ID 显示为
<job_id>.<step_id> - array task 显示为
<array_job_id>_<task_id>
Format Fields
支持的字段名:
JobIDArrayJobIDArrayTaskIDJobNamePartitionUserStateReasonExitCodeElapsedAllocCPUSReqMemReqTRESAllocTRESNodeListSubmitStartEndWorkDirBatchFlagMaxRSS
支持的 % 代码:
%i%F%K%j%P%u%t,%T%R%X%M%C%m%b%B%N%V%S%E%Z
作业控制
scontrol
支持的形式:
scontrol show job <job_id>
scontrol hold job <job_id>
scontrol release job <job_id>
scontrol update job <job_id> KEY=VALUE...
show job
显示详细作业信息,包括:
- 作业标识与所有者
- 状态与 reason
- 请求的资源
- 时间限制
- dependency 字符串
- submit、start、end 时间戳
- command 与 working directory
- stdout 与 stderr 路径
- array 元数据
ReqTRESAllocTRESMaxRSS- step 摘要
hold job
把 pending job 移动到 held 状态。
结果:
- job 仍保持
PENDING - reason 变为
JobHeldUser
release job
将 held 的 pending job 释放回正常调度。
update job
支持的更新键:
| 键 | 规则 |
|---|---|
JobName / Name | 仅能在 job 为 PENDING 时修改 |
Partition | 仅能在 job 为 PENDING 时修改 |
TimeLimit / Time | 只要 job 尚未进入终态就可以修改 |
Priority | 仅能在 job 为 PENDING 时修改 |
示例:
scontrol update job 10 TimeLimit=02:00:00
scancel
支持的形式:
scancel <job_id>
scancel <job_id.step_id>
scancel --signal <sig> <job_id>
scancel --signal <sig> <job_id.step_id>
默认取消行为
- pending job 会立即变为
CANCELLED - running job 会先进入
COMPLETING - runner 会发送
SIGTERM - 如果需要,宽限期后再发送
SIGKILL
记录的取消原因是:
CancelledByUser
Signal 模式
--signal 只发送指定信号,而不是执行默认取消流程。
示例:
scancel --signal TERM 12
节点与分区视图
sinfo
sinfo 显示本地节点的分区与主机状态。
常用选项
| 选项 | 含义 |
|---|---|
-p, --partition | 过滤分区 |
-N, --Node | 切换到单节点汇总视图 |
-l, --long | 使用长格式默认视图 |
-o, --format | 选择输出字段 |
--noheader | 省略表头 |
默认视图
典型输出:
PARTITION | HOSTNAMES | STATE | FEATURES | GRES_USED
cpu* | localhost | idle | cpu | N/A
gpu | localhost | idle | cpu,generic_gpu | gpu:0
说明:
- 每个已配置分区显示一行
- 默认分区会标记为
* - CPU partition 的
FEATURES只显示cpu - GPU partition 的
FEATURES显示cpu和检测到的 GPU 型号 feature,不显示通用gpu - CPU/GPU 分区只是同一台本地主机上的便捷虚拟视图
- CPU 和内存容量在这些分区之间共享,并不是彼此独立的资源池
节点视图
sinfo -N 会把分区行折叠为单个本地节点汇总行。
典型输出:
PARTITION | HOSTNAMES | STATE
cpu,gpu* | localhost | idle
说明:
PARTITION列会变成用逗号连接的分区列表- 其中默认分区仍然保留
*标记 - 长视图或自定义格式中的容量和分配字段会按可见分区聚合
长视图
sinfo -l 会增加容量与分配情况的细节:
PARTITION | HOSTNAMES | STATE | FEATURES | CPUS | CPU_ALLOC | MEMORY | MEM_ALLOC | GPUS | GPU_ALLOC | RUNNING | PENDING | GRES_USED
支持的格式字段
字段名:
PartitionHostnames,Hostname,NodeListStateFeaturesCPUSCPU_ALLOC,CPUSLOAD,CPUALLOCMemory,MemMEM_ALLOC,MemoryAllocated,MemAllocGPUSGPU_ALLOC,GpusAllocated,GpuAllocRunning,RunningJobsPending,PendingJobsGRES_USED,GresUsed
支持的 % 代码:
%P%N%t,%T%f%G
测试
slotd 主要通过 tests/ 下的 Rust 集成测试进行验证。
每个测试都会创建一个独立的临时运行环境,启动专用 daemon,然后通过编译后的 slotd 二进制去驱动公开的 Slurm 风格命令。
因此测试不会污染你平时使用的 SLOTD_ROOT。
如何运行
运行完整测试套件:
cargo test
只运行某一个集成测试文件:
cargo test --test scheduling
只运行某一个具名测试用例:
cargo test dependency_job_waits_for_prerequisite_before_running --test scheduling
当前测试覆盖内容
当前测试套件重点覆盖对使用者有影响的行为:
sbatch、srun、salloc、sinfo、squeue、sacct、scontrol的核心命令流程- 依赖、数组作业、延迟启动、constraint、resource flag、requeue 等调度规则
srun --pty、--label、--unbuffered以及 allocation / step 等交互式与前台执行路径- cancellation、warning signal、update 处理、输出文件落盘等生命周期与恢复行为
SLOTD_NOTIFY_CMD与 parsable 查询输出等通知和报表相关路径
有代表性的测试文件包括:
cli_basic.rs,sbatch_options.rs,srun.rs,srun_options.rs,srun_modes.rs,salloc.rs,sinfo.rs,control.rs,query_squeue.rs,query_sacct.rsscheduling.rs,compound_scheduling.rs,dependency_variants.rs,array.rs,begin.rs,constraint.rs,resource_flags.rs,requeue.rs,timeout.rssrun_interactive.rs,srun_allocation.rs,cpu_bind.rs,output_files.rs,cancellation.rs,recovery.rs,update.rs,warning_signal.rs,notify.rs
手动冒烟测试
如果只想做一次快速手动验证,可以先启动 daemon:
cargo run -- daemon
然后在另一个使用同一 SLOTD_ROOT 的 shell 中提交一个小作业:
cargo run -- sbatch --wrap 'echo hello'
示例
CPU 批处理作业
sbatch \
-J hello \
-p cpu \
-c 1 \
--mem 512M \
-t 00:05:00 \
-o logs/%j.out \
--wrap 'echo hello'
预期结果:
Submitted batch job <id>logs/<id>.out中包含hello
GPU 批处理作业
sbatch \
-J gpu-demo \
-p gpu \
-c 4 \
--mem 8G \
-G 1 \
-t 01:00:00 \
-o logs/%j.out \
--wrap 'nvidia-smi'
预期结果:
- 作业会在 GPU 分区运行
- 输出中包含
nvidia-smi信息
交互式前台运行
srun --label --unbuffered -- echo hello
预期结果:
0: hello
交互式 allocation
salloc -p gpu -c 4 --mem 8G -G 1 -t 00:30:00
预期结果:
Granted job allocation <id>- 在 allocation 内启动一个 shell
Array Job
sbatch \
-J array-demo \
-a 0-9%2 \
-o logs/%A_%a.out \
--wrap 'echo task=$SLURM_ARRAY_TASK_ID'
预期结果:
- 会创建多个 task 记录
- 生成类似
logs/<array_id>_0.out的日志文件
单次 Requeue
sbatch --requeue --wrap 'exit 1'
预期结果:
- 第一次失败后作业回到
PENDING - 第二次失败后最终状态为
FAILED
延迟启动
sbatch --begin now+00:10:00 --wrap 'echo delayed'
预期结果:
- 作业会在 begin 时间之前保持 pending
squeue --start会显示未来的开始时间
显式导出环境变量
sbatch \
--export FOO=bar,HELLO=world \
--wrap 'echo "$FOO $HELLO"'
预期结果:
- 输出中包含
bar world
手动运行 daemon
SLOTD_ROOT="$HOME/.local/share/slotd" ./target/release/slotd daemon
然后在另一个 shell 中执行:
SLOTD_ROOT="$HOME/.local/share/slotd" ./target/release/slotd sbatch --wrap 'echo hello'
故障排查
Connection refused
症状:
error: io error: Connection refused (os error 111)
含义:
- client 找到了 socket 路径
- 但该路径上当前没有 daemon 在接受连接
检查项:
- daemon 是否真的在运行
- client 与 daemon 是否使用相同的
SLOTD_ROOT - 是否残留了旧运行产生的过期 socket 文件
有用的检查命令:
ls -l "$SLOTD_ROOT/run/slotd.sock"
sinfo
Runtime Root 不一致
如果 daemon 使用一个 runtime root,而 client 使用另一个,命令看起来会随机失败。
常见原因:
- daemon 由
systemd --user启动 - client 从一个没有相同
SLOTD_ROOT的 shell 中启动
最稳妥的修复方式:
- 使用
scripts/install.sh安装 - 使用
~/.local/bin下的 wrapper 命令
看不到 GPU 分区
如果 sinfo 只显示 cpu,请检查:
nvidia-smi是否在 daemon 环境中可用SLOTD_GPU_PARTITIONS是否按预期配置- 修改 GPU 配置后是否已经重启 daemon
手动检查:
nvidia-smi
sinfo
重装时出现 Text file busy
安装脚本现在会以原子方式替换二进制。如果仍然遇到问题:
- 停止或重启 daemon
- 再次运行安装脚本
命令:
systemctl --user restart slotd.service
./scripts/install.sh
作业一直处于 PENDING
可能原因:
- 资源不足
- dependency 未满足
- array 并发限制
- 尚未到达 delayed start time
- 作业处于 held 状态
- 已有 exclusive job 正在运行
排查方式:
squeue
scontrol show job <job_id>
cgroup 设置失败
如果设置了 SLOTD_CGROUP_BASE,但它并不是可写的 cgroup v2 subtree,作业启动会
直接以明确的 cgroup error 失败。
检查:
- 该路径在 daemon 环境中确实存在
- 它是 cgroup v2 subtree,而不是普通目录或文件
- daemon 用户可以在其中创建每个作业的子目录并写入 control file
作业被取消但最终状态是 OUT_OF_MEMORY
如果在终止过程中 cgroup 内存事件表明发生了 OOM,最终状态可能优先记录为 OUT_OF_MEMORY。
找不到输出文件
请检查:
- 作业的 working directory
-o/--output与-e/--error路径%j、%A_%a等模式展开
排查命令:
scontrol show job <job_id>