ghrun 执行引擎

文档发表:2026-09-11 · 阅读:5 · 更新:2026-09-11

ghrun 执行引擎

无人值守的进程治理 —— AI 时代需要一套新的生死判据

1. 要解决什么问题

AI 正在成为命令的主要书写者:它比你敲得快、比你敲得多,而且——没有人在旁边看它敲。

这给执行基础设施提出了一个此前从未真正存在过的问题:一条命令开始运行之后,谁来判断它应该继续活下去,还是已经被判了死刑?

终端时代,判断者坐在键盘前面(Ctrl+C)。CI 时代,判断者是时间表(超时即杀)。而当命令由 AI 即时合成、在用户机器上逐条执行时——执行多久无法预估、出错方式无法穷举、现场没有任何人站岗——两个现成的判断者都不在了。

判断者缺位之后,失败以两种面貌出现:

  • 误杀:一个正在正常工作的长任务被时间保险错杀——全程它一直在说话,从未失去生命;
  • 失控:一条失控的命令在数十秒内膨胀到 40GB 级内存,把整机拖进泥潭;或者父进程退出了,整棵子进程树还在后台无声地跑。

本质问题:无人值守的执行环境里,“这个进程该死还是该活”必须由引擎自己回答——而现存的执行器,都把这个问题外包给了人(终端)或时间表(CI)。


2. 历史方案的失败点

终端 / shell:判断者必须是人

  • 人在场时,这是一个完美的模型——响应即时、判断准确(人知道任务是否正常)。
  • 根本缺陷:人在场本身就是模型的一部分。 无人值守时,它退化为“没有任何判死机制”——挂死的进程可以永远挂着。

CI / 批处理:判据是一张时间表

  • 默认保险是总时长上限:跑了 N 分钟就杀。
  • 根本缺陷:时间与死活被画了等号。 长任务(构建、训练、下载)与死进程(挂死、断连)在时间轴上完全重叠——跑得久的有可能是活的,静默的有可能是死的;一张时间表区分不了这两种。
  • 而且时间表只覆盖一类死法:内存失控、静默假活、孤儿进程,它都不负责。

容器:正确,但太重

  • cgroup 提供内核级内存约束,整体生命周期管理成熟——隔离能力是对的。
  • 根本缺陷:为“服务隔离”而生,不为“逐条命令的即时治理”而生。 守护进程、镜像、编排层——在用户机器上给 AI 逐条合成的命令施加治理,成本高过被治理对象。你不可能为每条 echo 起一个容器。

通用进程接口(child_process / subprocess 类)

  • 提供 spawn 与 kill。
  • 根本缺陷:它们回答“怎么启动”,不回答“何时判死”。 这是工具,不是模型。

共同缺陷

所有方案的判死判据——人、时间表、人工编排——在“无人值守 + 任务时长无界”这个场景里,要么缺位、要么失真。它们并非做得不好——而是没有一个把“进程何时该死”当作第一性问题来回答。


3. 我们如何一步步推演

ghrun 不是一次设计出来的。它是五次追问的收敛——每一步都暴露并解决了上一步的问题。

3.1 第一步:内存怎么管住?——把约束下沉到内核

实测起点:一条失控的命令,在两次巡检之间膨胀到 40GB 级。

用户态看门狗是采样的:每 N 秒查一次,意味着每 N 秒之间存在一段盲区——爆炸总是发生在窗口里。把巡检调快?更快=成本更高,窗口依然在;窗口不可能靠“跑得更快”变成零。

于是换判定方式:约束不应该被“检测”,而应该被“实施”。 内存上限直接写进操作系统内核(Windows 用 Job Object,POSIX 用 setrlimit):越限不是“下一轮巡检会发现的问题”,而是分配那一刻即发生的失败。

  • 用户态轮询:成本恒在(一切正常也要照查),窗口恒在(爆炸总在窗口里);
  • 内核实施:空闲成本为零,检测间隙为零——不存在“没查到的时刻”。

主张:轮询的每一次间隙,都是一次炸内存的机会。

3.2 第二步:怎么判死?——从时间预算到生命信号

早期版本给长任务配了总时长保险,结果是:一个正常输出的大任务在接近完成时被误杀——全程它一直在说话,从未失去生命。反过来,真正死掉的进程(断连、挂死)的特征从来不是“跑了多久”,而是“多久没有动静”。

时间与死活本来就是两个维度,旧模型偷懒地把它们画了等号。于是判据反转:

进程该不该死?
  任意生命信号到达(输出 / 进度)→ 续命,无论已运行多久
  零进展持续超过阈值             → 判死,收回整棵进程树

主张:静默即死,无论跑了多久;开口即活,无论还要跑多久。

直接后果:长任务不再需要“申请更多时间”——它从不需要证明自己配得上更多时间,只需要证明自己还活着。

3.3 第三步:死法有几类?——四把锁对四类死法

前两步暴露了一个结构:每一类“死法”,都有唯一一个正确的对手。于是我们把进程的死亡方式做了一次完整分类:

死法特征唯一对手
无限跑活着,但永远不会自己停下硬超时(2 小时绝对上限)
炸内存快速失控,拖着整机陪葬内核内存硬限(2GB,越限即死)
失速假活未退出、不输出,实质已死失速看门狗(15 分钟零输出即判)
变孤儿父进程已退,子进程还在,无人认领进程组隔离(父退子杀)

测一下这个分类的完整性:硬超时拦不住内存爆炸;内存限制不会去杀一个没超限的挂死进程;两者都不管孤儿。每少一条防线,就有一整类死法完全没有对手。

主张:“四重防线”不是同一个敌人的四层墙(纵深),而是四个敌人各自的唯一对手(宽度)。

3.4 第四步:监管者本身可以被信任吗?——零依赖单文件

一个负责“看住别人”的组件,如果自己带上依赖(运行库、解释器、环境),它就把自己的故障面复制进每一台机器——监管者先坏,被监管者失控。

所以 ghrun 把自己做成零依赖单文件二进制:Rust 编写、静态编译,四个构建目标(Windows / Linux / macOS)全部零安装依赖——没有要装的运行库、没有解释器、没有依赖树。

主张:监管者必须先于被监管者可信。

由此推出一条成本对称原则:监管不是免费的,治理预算只花在值得治理的地方。不含 shell 语法的短命令直接绕过引擎执行、不付引擎启动的固定开销——高频的琐碎命令(查询、列表、读文件)不值得为治理付出这笔成本。

3.5 第五步:执行管线上的两条经验推论

推论一:减少解释层数,优于改善转义。 一条命令字符串每多经过一层解释器(shell 一层、进程创建一层、远程传输再一层),转义规则就多互相纠缠一次——我们实测过三层引号地狱让一条普通查询连续失败 28 次。正确做法不是把转义写得更巧,而是把语义通道与解释通道分离:意图只解析一次,以参数数组直达进程创建的原生通道;只有在命令真的需要 shell 语法(管道、重定向)时,才交给 shell。

推论二:字节流是唯一真理。 中文 Windows 的输出管道里,GBK 与 UTF-8 长期混行;任何“按文本处理”的中间层都可能译错,而截断若切在多字节字符中间,损坏不可逆。引擎在出口做严格解码(UTF-8 优先、GBK 兜底),在截断处尊重字符边界——半个字符不是字符。


4. 核心突破:判断者缺位,判据必须写进引擎

回看五步,可以归纳为一个命题:无人值守的执行环境里,“生死判断”不能外包。

  • 外包给人 → 无人可包(终端模型失效);
  • 外包给时间表 → 判据失真(长任务误杀、静默死漏杀);
  • 外包给编排 → 成本爆炸(容器模型太重)。

唯一的位置是引擎自己。我们把判断写进去,得出一套完整的判死模型。它的第一原理只有一条:先判活,再判死。

主张一句话它防住的失效
生命判据静默即死,开口即活长任务误杀 / 静默死漏杀
约束下沉约束被实施,不被检测内存爆炸 / 巡检窗口
四把锁宽度不是深度,一类死法一个对手任意一类死法的缺防
监管者无重先于被监管者可信治理组件自身腐坏

这四个主张里,没有一个能直接沿用现成执行器的默认值——它们全部是“无人值守 + 任务无界”这个新场景逼出来的答案。四者缺一不可:没有生命判据,长任务永远被误杀;没有内核约束,判断得再准也来不及(爆炸发生在毫秒级);没有完整分类,总有一类死法没有对手;没有零依赖,监管者自己就是下一个故障源。


5. 与现代执行模型的对比

维度终端CI 超时容器ghrun
判死依据人在场时间表(总时长)编排策略(需自配)生命信号(零进展判死)
内存约束视平台而定cgroup(内核级)Job Object / setrlimit(内核级)
死法覆盖靠人仅“无限跑”内存、孤儿好;失速需自配四类全覆盖
长任务人扛着有误杀风险可支持零误杀(越跑越活)
部署形态系统自带服务端编排守护进程 + 镜像零依赖单文件,逐条命令

这不是“我们比它们多几个功能”,这是两种执行哲学的分岔:

  • 有人值守哲学:判断者是人或时间表。默认前提是“任务有界、现场有人”。
  • 无人值守哲学:判断者是写进引擎的生命判据。默认前提是“任务无界、现场无人”——一切判死规则必须自带。

6. 深层含义

历史上,每一个自动化系统最终都要面对同一个问题:把“执行”从人手里拿走之后,“判断”怎么办?

大多数系统的答案是“把判断留给外层的人”——告警发给人、超时设得宽、异常交给人排查。这在有人值守的场景里是合理的。但当 AI 开始批量合成命令、长时间无人值守地工作时,“外层还有人”这个假设就消失了——判断必须下沉进基础设施本身。

这也意味着一种新的设计责任:AI 越自治,基础设施里被写死的判断就越重要。 写成时间表,长任务就会被误杀;写成生命信号,干活的东西永远不会被错杀。写多宽,就覆盖几类死法;写多深,就防得住多大失控。

这条判据后来被证明可以跨尺度成立——不止进程一层需要它。凡无人值守的长任务,“判活”都应该先于“判时”。这是无人值守世界的通约规则。


7. 结论

ghrun 只回答一个问题:这个进程,现在应该活着吗?

它的答案由四条主张构成:

#主张一句话
生命判据静默即死,无论跑了多久;开口即活,无论还要跑多久
约束下沉轮询的每一次间隙,都是一次炸内存的机会——所以约束属于内核
四把锁防线是宽度不是深度:四类死法,各有一把唯一的锁
监管者无重监管者必须先于被监管者可信

无人值守是一个新的执行环境,它需要一套自己的生死判据。这四条主张,就是 ghrun 存在的全部理由。


返回:qd (qqqide) 引擎总览