引导器 UEFI.boot.py (纯 stdlib, 内核的管家) 体检 6 项 (解释器/venv/包/驱动目录/PG) / venv 重建 / 包台账 / 内核进程启停记账 / 参数原样透传 内核日志 / 引导器日志 / 内核 stdio 三条道的查看入口都归它 内核 内核/ (常驻调度 = 甲) 命令消费 (PG commands + LISTEN/NOTIFY) / 驱动调用仲裁六条 (越权·成环·无人提供·按需拉起·同锁排队·超时收权) 依赖链巡检 10s / 心跳 300s / 独一份调度锁 (pg_try_advisory_lock 0x65666901) 扫描: 9 条校验 + 契约匹配 + 拓扑排序; 状态: 快照原子写 + 断电收尸判定 db.py 是唯一碰 SQL 的文件 (drivers/driver_state/events/scans/commands/calls/kernel_env/kernel_runs) 日志系统 (2026-09-16 完整化, 设计/04-日志系统.md) 三条道一个文件一种内容: 内核.log (结构化行) / 内核.out.log (命令输出 + 崩溃原文) / 引导器.log (引导器动作) 驱动日志每轮启动前插分隔头; 门槛 log_level / 轮转 log_max_mb + log_keep / -n · -f · --级别 · -g · --json · --全部 实现 内核/日志.py (内核与引导器共用一份); 修掉"命令输出混进日志文件" (实证存档 归档/20260916-日志系统重做前/) 驱动样板 (也是写驱动的示范): Json解码 (oneshot) / 样板常驻 (provides 样板:心跳) / 样例消费器 (needs 只发契约名) 代码标点统一为 ASCII (保留界面用的框线 ─│◄▶ 与表格占位符 —); 已用 AST 等价对比证明逻辑零改动 自测 (真机, 不 mock): 进程 (真起进程真收子树) / 内核 58 项 / 配置 / db 182 项 / 日志 86 项 验收: 试跑引导器.py PASS 11 / FAIL 0 / 残留无; uvx pyright 与 uvx basedpyright 均 0 errors 0 warnings
11 KiB
03 引导器 UEFI.boot.py
引导器 = 内核的管家,三件事:管内核环境、管包状态、管内核进程。 它不碰驱动 —— 驱动是内核的活。分层是一层管一层:引导器管内核,内核管驱动。
1. 职责边界
| 引导器做 | 引导器不做 |
|---|---|
| 内核环境体检(解释器 / venv / 路径 / PG) | 不扫驱动、不管驱动进程(内核的活) |
| venv 重建、包安装(显式敲命令才动手) | 不自动修复、不自动装包、不自动升级 |
| 包状态台账(已装 / 缺失 / 版本不符) | 不管驱动的 venv 和依赖(驱动自己的事) |
| 管内核进程(启停 / 判活 / 日志 / 记账) | 不改任何 配置.efi.json |
| 其余参数原样透传给内核 | 不重复实现内核子命令 |
2. 铁律:引导器零第三方依赖
鸡生蛋问题:引导器要检查"内核环境里的包状态",可引导器自己要是依赖某个包,那包坏了引导器也起不来 —— 就没人来报错了。所以:
| 引导器自己的命令(自检 / 环境 / 包) | 纯 stdlib:subprocess 调 uv/pip、importlib.metadata 读版本、pathlib 查路径 |
| 写 PG 台账 | try: import psycopg2;没有就降级:只写 环境状态.efi.json 快照 + 一行 WARN,不算失败 |
| 透传给内核的命令 | PG 连不上由内核自己报错退出(内核的内存就是 PG,它不降级) |
分界线一句话:引导器自己的命令 PG 降级,内核的命令不降级。 引导器永远要能跑起来、能报错、能给出修复命令。
3. 三层文件(对称设计)
每层都是"一份声明 + 一份状态快照":
| 层 | 声明(人/引导器写) | 状态快照(运行时写) |
|---|---|---|
| 驱动 | 驱动/<名>/配置.efi.json |
驱动/<名>/运行.efi.json |
| 内核 / 引导器 | 环境.efi.json |
环境状态.efi.json |
4. 环境.efi.json(项目根,外置声明)
引导器是它唯一的写者,内核只读。 一份配置,不许有第二份(两份真相 = 迟早打架)。
内核原先设计的"内核自己的 配置.efi.json"取消,合并到这一份。
| 键 | 类型 | 说明 |
|---|---|---|
efi |
int | 版本 |
python |
str | 解释器要求,如 ">=3.11" |
venv |
str | venv 相对路径,默认 .venv |
index |
str | 包索引源。校网必须走国内:https://pypi.tuna.tsinghua.edu.cn/simple |
packages |
list | 内核依赖清单,见下 |
db |
dict | {name, host, port, user},host 填 unix socket 目录 |
driver_root |
str | 驱动目录,默认 "驱动" |
stop_timeout |
int | 停进程超时秒,默认 10 |
log_lines |
int | 日志命令默认行数,默认 200 |
log_level |
str | 日志门槛 DEBUG/INFO/WARN/ERROR,默认 INFO(低于它的不写) |
log_max_mb |
int | 单份日志轮转上限(MB),默认 5;0 = 不轮转 |
log_keep |
int | 轮转后保留份数,默认 3;0 = 不轮转 |
packages 每项:
| 键 | 说明 |
|---|---|
name |
安装名(pip 名),如 psycopg2-binary |
version |
版本要求,如 ">=2.9.9",空 = 不校验 |
import |
导入名(校验用),如 psycopg2 |
required |
true 缺失即环境不通过;false 缺失只 WARN |
{
"efi": 1,
"python": ">=3.11",
"venv": ".venv",
"index": "https://pypi.tuna.tsinghua.edu.cn/simple",
"packages": [
{ "name": "psycopg2-binary", "version": ">=2.9.9", "import": "psycopg2", "required": true }
],
"db": { "name": "efi_kernel", "host": "/home/lou/pgdata/socket", "port": 5432, "user": "lou" },
"driver_root": "驱动",
"stop_timeout": 10,
"log_lines": 200
}
5. 环境体检(自检 / 环境)
6 项,逐项出结论 + 修复命令,从不含糊:
| # | 检查 | 方法 | 不过时给什么 |
|---|---|---|---|
| 1 | 解释器版本符合 python |
跑解释器报版本,比 >= / <= / == |
报"需要 ≥x,当前 y" |
| 2 | venv 存在 | .venv/bin/python 在 |
环境 重建 |
| 3 | venv 健康 | 跑 .venv/bin/python -c "import sys;print(sys.prefix)",结果必须等于 .venv 自身绝对路径;pyvenv.cfg 的 home 指向存在的解释器 |
环境 重建(★治"目录搬走后 shebang 还指旧路径"这个病,本项目 .venv 现在正是坏的:shebang 指着 /home/lou/桌面/内核/.venv/...) |
| 4 | 包状态 | uv pip list --format json 读实装版本(没 uv 就 importlib.metadata),逐条核 packages |
列出缺的 / 版本不符的 + 包 安装 |
| 5 | driver_root 可读写 |
存在 + 能建临时文件 | 报错(不自动建空目录) |
| 6 | PG 能连 | 实例在跑 + 库 db.name 存在 + 真连一次 |
给建库/起库命令 |
判定 → 汇总 ok: true/false,落 环境状态.efi.json(快照)+ PG kernel_env(台账)。
6. 包状态(包)
包→ 台账表:包名 / 要求 / 实装 / 状态(一致·缺失·版本不符)/ 必需性包 安装 [名...]→uv pip install(走index里的国内源,校网 GFW 下这是硬要求);不带名字 = 按清单全装- 不自动装、不自动升(手动控制口径,"一搞就炸")
- uv 不在时回落
python3 -m venv+pip -i <index>
7. 内核进程管理(内核 ...)
内核通常是一次性短命进程(跑完就退,状态在 PG),引导器把它当受管进程记账:
| 命令 | 作用 |
|---|---|
内核 状态 |
有没有内核在跑(pid + /proc/<pid>/cmdline 校验);上次运行(起止 / 耗时 / 退出码);今天跑了几次 |
内核 启动 [--守护] |
拉起内核进程;--守护 = 常驻模式(预留,v0.1 不实现) |
内核 停止 / 内核 重启 |
停 / 重启内核进程 |
内核 日志 [选项] |
内核日志(默认看结构化日志 内核.log;--输出 看命令输出/崩溃原文 内核.out.log) |
日志 [选项] |
引导器自己的动作日志(内核/logs/引导器.log;--内核/--输出 转看内核那两份) |
复用 内核/进程.py —— 启停 / 判活 / cmdline 校验 / 收子树全用同一份实现。
绝不为引导器再写一套(两套实现 = 各有一套坑,还各自以为对方对)。
边界:内核是总调度(见 02 第 2 节末),引导器只保证"它活着、它跑得起来"—— 拉起、判活、重启、收日志,不干预它怎么调度驱动。
(内核自身常驻已定稿 = 甲:常驻调度器。所以引导器管的这个内核进程是长期存在的,
内核 启动/停止/状态/日志 就是它的生命周期;引导器只保证它活着,不干预它怎么调度。)
记账两张表(同库 efi_kernel):
-- 环境/包体检台账
CREATE TABLE kernel_env (
id bigserial PRIMARY KEY,
ts timestamptz DEFAULT now(),
python_version text,
venv_path text,
venv_healthy boolean,
packages jsonb, -- [{name, want, got, ok}]
pg_ok boolean,
driver_root_ok boolean,
ok boolean,
detail text
);
-- 内核运行台账(每次运行一行)
CREATE TABLE kernel_runs (
id bigserial PRIMARY KEY,
started_at timestamptz DEFAULT now(),
finished_at timestamptz,
argv text,
mode text, -- oneshot | daemon
pid integer,
exit_code integer,
seconds real,
ok boolean,
detail text
);
8. CLI 全表
顶层固定 4 个词(自检 / 环境 / 包 / 内核)归引导器,其余原样透传内核 ——
重名因此不冲突:启动 抓取器 是驱动,内核 启动 是内核。
| 命令 | 谁干 |
|---|---|
UEFI.boot.py |
自检 → 内核默认动作(列表) |
UEFI.boot.py --check | 自检 |
只体检,不启内核 |
UEFI.boot.py 环境 [--json] |
体检(同上) |
UEFI.boot.py 环境 重建 |
按 python 要求重建 venv(显式) |
UEFI.boot.py 包 [--json] |
包状态台账 |
UEFI.boot.py 包 安装 [名...] |
装包(显式) |
UEFI.boot.py 内核 状态|启动|停止|重启|日志 |
内核进程管理 |
UEFI.boot.py 日志 [选项] |
引导器自己的动作日志(体检 / 包 / 移交 / 内核启停 / WARN) |
UEFI.boot.py 列表|扫描|启动 <驱动>|停止 <驱动>|重启 <驱动>|状态|日志 <驱动>|事件|清单 |
透传给内核 |
日志选项(日志 / 内核 日志 / 透传的 日志 <驱动> 认同一套,解析只有一份 日志.解析选项):
| 开关 | 含义 |
|---|---|
-n 200 |
尾部行数(含轮转的历史份一起数) |
-f |
实时跟(先吐 10 行,Ctrl+C 停;一次只能跟一个来源) |
--级别 WARN |
只要这个级别及以上(驱动原始日志按字样猜级别) |
-g 关键词 |
只要含它的行(不区分大小写) |
--json |
JSON Lines(结构化日志拆字段;裸行给空来源 + 猜的级别) |
--全部 / --内核 / --引导器 / --输出 |
换来源 |
参数写错 → 打用法 + 退出码 1(不猜、不静默用默认值)。引导器每次跑都轮转一次自己的日志 (短命进程,天然没有"运行中改名"的风险)。
9. 启动时序
$ python3 UEFI.boot.py 列表
│
├─ ① 体检(纯 stdlib,任何情况都能报)
│ 解释器版本 → venv 存在/健康 → 包状态 → 驱动目录 → PG
│ 不通过 → 打印原因 + 修复命令,退出 1(不往下走,不自动改)
│
├─ ② 记账
│ 写 环境状态.efi.json(必做,原子替换)
│ 写 PG kernel_env(尽力而为,连不上就 WARN)
│
├─ ③ 移交
│ fork 内核进程(argv 透传) → 等 → 记 kernel_runs → 返回它的退出码
│ 内核自己写 内核/logs/内核.log(结构化行)
│ --守护 时内核 stdio 重定向到 内核/logs/内核.out.log(命令输出 + 崩溃原文)
│
└─ ④ 退出不管子进程(驱动是独立进程组,不随引导器死)
10. 边界
| 场景 | 行为 |
|---|---|
| 引导器自己的命令 + PG 不通 | WARN + 只写 json 快照,不失败 |
| 透传内核的命令 + PG 不通 | 由内核报错退出,引导器原样返回非 0 |
| venv 不健康 | 明确报"不健康 + 为什么" + 环境 重建,不自动重建 |
| 包缺失 | 列出缺哪个 + 包 安装,不自动装 |
| uv 不在 | 回落 python3 -m venv + pip -i <index>,并 WARN |
driver_root 不存在 |
报错退出 1(不建空目录) |
| 内核文件缺失 | 报错退出 1,指路径 |
| 内核抛异常 | 原样打印堆栈,退出码非 0 |
| 内核秒退非 0 | kernel_runs.ok=false + detail 存日志末尾若干行 |
11. v0.1 范围
做:环境体检 / venv 重建 / 包台账 / 装包 / 内核进程记账与启停 / 参数透传。
不做:自动修复、自动装包、开机自启、常驻守护(--守护 只留接口)、驱动 env 代管。