雨天小六

读懂 Codex(3.19):后台任务、子进程和资源清理

· 更新于 2026-08-02 · 专栏:读懂 Codex

#Codex#Agent Runtime#生命周期#软件架构

Turn 完成、Turn 中断和 Session Shutdown 是三个不同清理等级。普通 Turn 完成不会关闭 MCP 或杀掉所有后台 terminal;Interrupt 收走当前 Task,但 Thread 继续服务;只有 Session teardown 才关闭线程级长期资源。

资源按所有者分层

资源所有者Turn 结束Session Shutdown
模型 stream / tool futureRunningTask token tree结束或取消先 abort active task
Git enrichmentTurnMetadataStatefinish/abort 均取消随 active task 清理
UnifiedExec background processProcessManager可继续存在terminate all
Realtime conversationRealtimeManager独立于普通 Turnshutdown
Code Mode worker/sessionCodeModeServiceworker按 Turn 结束service shutdown
MCP prewarm/runtimeSessionServices跨 Turn 复用stop worker + close runtime
Guardian review sessionGuardian manager按审查需要shutdown
LiveThread writerThread 生命周期flush terminalshutdown writer
Turn 级与 Session 级后台资源所有权
图 3.19-1:后台化不等于无主;资源必须挂在 RunningTask、ProcessManager 或 SessionServices 之一,清理等级由所有者决定。

shutdown_session_runtime 的真实顺序

Session teardown 先 abort startup prewarm 和 realtime,再 Interrupt active Task;随后清空 UnifiedExec 进程,关闭 Code Mode,停止 MCP prewarm。MCP refresh 使用的 gate 要先 acquire,随后 close gate并 shutdown runtime,防止刷新任务与关闭并发重建连接。最后关闭 Guardian 并运行 session-end hooks。

async def shutdown_session_runtime(session):
    if prewarm := await take_startup_prewarm():
        await prewarm.abort()
    await realtime.shutdown()
    await abort_all_tasks(INTERRUPTED)
    await unified_exec.terminate_all_processes()
    await code_mode.shutdown()
    await stop_mcp_prewarm_worker()
    async with mcp_refresh.acquire():
        mcp_refresh.close()
        await mcp_runtime.shutdown()
    await guardian.shutdown()
    await run_session_end_hooks()

ThreadStop lifecycle 在 runtime resources 停止后执行,使 extension contributor 能观察最终 thread/session stores。显式 Shutdown 随后关闭 LiveThread writer、发 ShutdownComplete 并结束 rollout trace。Submission channel 自然关闭也会运行 runtime teardown 和 ThreadStop;其完成信号由 submission loop termination future 解析。

Session Shutdown 按依赖顺序清理后台资源
图 3.19-2:先阻止新工作,再清运行资源,最后关闭持久化和发布完成;顺序反过来会让迟到事件写入已关闭 Rollout。

ProcessManager 为什么先 drain map 再 terminate

terminate_all_processes 在锁内 drain 所有 ProcessEntry并清 reserved ids,然后释放锁,逐个注销网络审批并 terminate。若持有进程表锁等待操作系统终止,exit watcher或审批清理可能也需要这把锁,造成阻塞。

async def terminate_all(manager):
    async with manager.process_store.lock() as store:
        entries = list(store.processes.values())
        store.processes.clear()
        store.reserved_ids.clear()
    for entry in entries:
        await unregister_network_approval(entry)
        entry.process.terminate()

从 map 先移除还防止新的 write_stdin 获得一个正在关闭的 process handle。物理退出由 process 的 cancellation/termination 和 watcher继续收尾。

评论


← 返回文章列表