Replies: 5 comments
|
诉求成立:宿主不该因为子进程崩溃而陪葬,这条无论根因在哪儿都站得住。但你这份报告现在有一个归属问题和一条我建议你撤回的修复建议——两条都会影响它被谁接手、怎么修。 先说归属,这对你有利。 你环境里写的是「桌面版社区构建(Electron,基于
顺带一个值得在复现里写清的分支:DSH 的安装树里确实带 再说我建议你撤回的那条:全局 这条看起来最省事,但它是公认的反模式,而且会让这个 bug 变得更难修而不是更好修。Node 自己的文档就写明: 你的建议 1 和 3 才是正解,而且 1 大概率就是真根因:Node 里一个没有挂 另外你那段 Windows 利益相关与边界:我维护一个第三方 DSH 插件(Pi 生态兼容层),subprocess seam 和桌面壳都不是我们的面,我们既不碰也修不了。这条我没有任何第一手证据——上面全是对你报告本身的分析和 Node 的通用行为,不是我在 DSH 上实测出来的结论。stock 复现那一步只有你能做。 |
|
我按当前
因此建议下一步不要先加一个“捕获所有异常后继续运行”的全局 handler,而是把四个进程身份分别记录:第三方 child、官方 DSH Host、
我把 rc.2 源码路径、EPIPE/close 处理、Windows 退出事实、最小安全复现和 19 项 Host-survival 回归门整理在这里:https://sandbaseai.github.io/deepseek-harness-handbook/async-subprocess-tools.html Disclosure: I maintain the SandBase DeepSeek Harness Handbook. |
|
@weijiafu14 @denial123789 感谢两位的分析,按你们的建议完成了全部验证,正文已更新(标题与全文均已重写)。 已完成的事项:
遗留疑点:桌面版(Electron GUI,无控制台)为何也随 stock 一起崩溃——传播路径尚未完全解释,如需补充证据请告知。 再次感谢两位的指导。 |
|
我们在另一个 Windows 环境(dsh web 宿主 + 消费方控制台项目)不知道此帖的情况下独立复现并定位了同一根因,结论与本文完全一致,补充三个数据点:
消费方规避:存活检测改用 tasklist /FO CSV /FI "PID eq N" 精确匹配,不再使用 os.kill;全量测试通过、宿主 0 复发。上游修复建议与本文一致(Windows spawn 独立进程组/console,或宿主 SetConsoleCtrlHandler(NULL, TRUE))。 |
|
Updated the handbook subprocess runbook to reflect the corrected root cause: on Windows, |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
更新:根因已定位(附完整证据链)
在 stock 官方版与纯 seam 层复现后,崩溃机制已修正——之前报告中的归因(
os.kill(pid, 0)走TerminateProcess)不准确,实际是 Ctrl+C 控制台广播。以下是完整证据与修正后的根因分析。复现矩阵(全部实测)
_pid_is_alive(os.getpid())dsh web(0.1.1-rc.2,独立 DSH_HOME,不经桌面壳)执行同命令@deepseek-ai/dsh-subprocess-localspawn 自杀 python(不经 web/agent/桌面壳)_pid_is_alive(17576)/_pid_is_alive(999999999)(PID 不存在)python --version(正常退出的子进程)关键退出码
纯 seam 脚本宿主进程的
$LASTEXITCODE = -1073741510=0xC000013A=STATUS_CONTROL_C_EXIT(被 Ctrl+C 终止)。第二次运行时连 PowerShell 命令行的后续命令都被打断——证明 Ctrl+C 事件广播到了整个共享控制台。根因链
os.kill(pid, 0)的sig=0恰好等于signal.CTRL_C_EVENT(已实测:CTRL_C_EVENT = 0)。Python 对CTRL_C_EVENT/CTRL_BREAK_EVENT走GenerateConsoleCtrlEvent(sig, pid)分支(TerminateProcess只用于其他 sig 值)。因此os.kill(pid, 0)的真实语义是:向与 pid 共享同一控制台的所有进程广播 Ctrl+C,而不是 POSIX 的"存活探测"。@deepseek-ai/dsh-subprocess-local的spawn在 Windows 上使用detached: false(lib/index.js:805:detached: platform !== "win32")→ 子进程与宿主共享同一控制台。子进程执行os.kill(自己的pid, 0)时,Ctrl+C 广播直接命中宿主。结论与修复建议(更新)
uncaughtException/unhandledRejection兜底(那是最后边界,不是修复手段)。spawn在 Windows 上为子进程创建独立进程组/控制台(CREATE_NEW_PROCESS_GROUP/detached: true),使子进程的GenerateConsoleCtrlEvent无法波及宿主;SetConsoleCtrlHandler(NULL, TRUE)忽略CTRL_C_EVENT(适用于可控场景);'error'监听与句柄清理仍建议补(EPIPE 路径),但不是本问题的根因。launcher.py):os.kill(pid, 0)存活探测在 Windows 上不可用(语义=Ctrl+C 广播),应改用OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION)+GetExitCodeProcess,或 psutil。此坑会单独提交到其仓库。对维护者的请求
spawn的控制台共享策略(detached: false)是有意设计还是疏忽;四层进程身份记录(按 denial123789 的建议)
0xC000013A(STATUS_CONTROL_C_EXIT)静默退出All reactions