refactor(agent/core): 中断队列归还调度器,中断管理器只留现场保护

用户裁定:「中断队列就该调度器管理啊,中断管理器只负责进入中断的任务」。

上一版我把 interruptQueues/immediate 也搬进了中断管理器,这是错的:
排队是**调度职责**——判断该不该打断、打断不了先排队、按什么次序再取出来,
全是调度策略,不是"进入中断"本身。

## 本次改动

中断管理器(interrupt.go)现在只有:
- 中断栈 suspendStack(**唯一操作者**:压栈/弹栈都在其内)
- 中断屏蔽 critical(对应 CPU 关中断)
- 让位信号原语 preemptArmed/preemptLevel
- 栈状态钩子 stackNotify

迁回调度器(scheduler.go):
- interruptQueues[5] / immediate 两个字段
- highestInterruptLocked / popInterruptLocked / interruptCountLocked
- enqueueInterruptLocked / installImmediateLocked

## 新增边界判据

TestInvariant_InterruptManagerHoldsOnlyStackState 用**反射白名单**钉住边界:
interruptState 只允许 6 个字段(栈/屏蔽/让位×2/上界/钩子)。
反向确认 interruptQueues 与 immediate 确实在 scheduler 上——避免"两边都没有"的假绿。

变异验证:把 interruptQueues 塞回 interruptState ⇒ 判据立刻变红
("中断管理器不应持有这些字段:[interruptQueues]")。

## 验证

- go build ./... 通过
- go test ./internal/agent/core/ 通过
- go test -race ./internal/agent/core/ 通过
- 12 条不变量判据全绿

## 顺带排查:/tmp 是 tmpfs 且已 91%

`df` 显示 /tmp 是 8.8G tmpfs,Go 链接期临时文件把它填满,
导致并发构建出现 write ... no space left on device(表现为
某包 `FAIL 0.008s`——不是代码问题)。已改用 TMPDIR=/var/tmp/gobuild(根分区 112G)。
This commit is contained in:
JianFeeeee
2026-10-25 13:37:29 +08:00
parent b829968ac6
commit c5f236a569
4 changed files with 157 additions and 92 deletions

View File

@ -355,14 +355,24 @@ internal/agent/core/
| 3 验证 | ✅ 完成 | 全仓 `go test -p 1 ./...` 通过;`-race` 三关键包绿;变异验证通过 |
| 中断向量表 | ⏳ 未做 | §7 已设计,按 §7.6 顺序应在拆分完成后单独做 |
### 实际划定的边界(比原计划更严格)
### 最终边界(用户第四次裁定:队列归调度器)
用户三次裁定后确定的最终边界:
> 「中断队列就该调度器管理啊,中断管理器只负责进入中断的任务」
这条纠正了我第一版的错误:我把中断队列(`interruptQueues` / `immediate`)
也搬进了中断管理器。**排队是调度职责**——判断该不该打断、打断不了先排队、
按什么次序再取出来,全是调度策略。
最终边界:
| 归属 | 内容 |
| --- | --- |
| **中断子系统**(`interrupt.go`) | 中断队列、immediate 槽、**中断栈(唯一操作者)**、中断屏蔽(critical)、让位信号原语、**栈状态钩子** |
| **调度器**(`scheduler.go`) | 任务优先级语义(`Level`/`effectiveLevel`/`canPreempt`/`clampPluginLevel`)、**恢复决策**(`nextInterruptSelection`)、就绪队列、停止配额、背压 |
| **中断管理器**(`interrupt.go`) | **中断栈(唯一操作者)**、中断屏蔽(critical)、让位信号原语、**栈状态钩子** —— 只负责"进入中断"这件事本身 |
| **调度器**(`scheduler.go`) | **中断队列**(`interruptQueues`/`immediate`)、优先级语义(`Level`/`effectiveLevel`/`canPreempt`/`clampPluginLevel`)、**恢复决策**(`nextInterruptSelection`)、就绪队列、停止配额、背压 |
字段级判据:`TestInvariant_InterruptManagerHoldsOnlyStackState` 用反射白名单
钉住这一点——`interruptState` 只允许 6 个字段(栈/屏蔽/让位/上界/钩子),
把队列塞回去立刻变红(已变异验证)。
关键设计:**中断栈只有中断管理器能操作**。调度器通过 `stackTopLocked()`
**只读查询**栈顶做优先级比较,真正的弹栈由 `restoreTopLocked()` 在中断管理器内完成——