mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
Part 3 收尾。tryLoadProc 从桩位变成真实加载路径,plugin.bin 插件现在 经 registry 完整跑起来:spawn → 握手(共享段 fd 3)→ init/start → 反向注册 → 工具调用 → stage 共享内存读改写。 registry 侧: - Registry 持有 procHost(惰性创建,**全部 .bin 插件共用一块段**)。 每插件一段会让「内核 ctx → 段 → 插件改 → 回读 ctx」在多插件下退化成 副本模型,lost update 原样复现(§8.4 实测 35.8~36.8%)。 - tryDynamic 分派到 Registry.loadProc;tryLoadProc 退为纯静态校验 (构造需要 Host,只有 Registry 有)。 - StopAll 在锁外释放共享段:插件还持有映射时拆段,它们下一次访问就是 SIGBUS;且持锁调用会与 onProcCrash 回调产生锁序风险。 - onProcCrash 把子进程退出转成 EventSystem 事件,不在回调里直接重载 (重载需 registry 锁,而回调可能来自持锁路径的 goroutine)。 proc_core.go —— 权限梯度的类型系统落点(§3.8): - procCore 用**命名字段**持有 *isdk.PluginSDK,不是嵌入。嵌入会提升全部 方法,外部插件就能经类型断言拿到 Supervisor/Tracker/Adapter/Indexer/ Status/Selftest。命名字段下只有显式写出的方法存在——权限梯度从 「C ABI 表达能力的意外产物」变成显式声明并强制的策略。 - 能力访问器把内部超集接口收窄到公开面(isdk.KnowledgeAPI 内嵌 pubsdk.KnowledgeAPI 再加 Stats/Remove,isdk.MemoryAPI 加 GraphData, isdk.LLMAPI 加 Chat/ReloadFromConfig);nil 保护避免类型化 nil 让 corehandler 的判空失效。 - procPluginAdapter 转接 Start(*isdk.PluginSDK) → Start(proc.CoreSDK), Close 对 closeDynamic 可见故重载能真 kill 子进程(对比 dlclose 对 Go c-shared 是 no-op,§1.1)。 共享段分配按平台拆分(原先 host.go 直接调 unix.MemfdCreate,darwin/windows 交叉编译失败):Linux memfd;macOS 立即 unlink 的临时文件(无 memfd_create, 但语义一致:无残留、fd 可经 ExtraFiles 传递、子进程 mmap 同一 inode); 其余平台明确报错而非静默降级成「无共享段」——那会让 stage 静默失去数据面。 测试 +13 项: - e2e_template_test.go 用**真实 plugindev 模板**(而非 testdata 手写假插件) 编译插件跑全链路,验证「模板 ↔ 内核」协议/布局真的对齐,不只是内核自己 跟自己对齐。含 lifecycle.autoRestart 上报、工具调用、stage 读改写、 FinalText 回传(C ABI 下 after_toolcall 看不到此字段,§8.3 10→16)、 只读插件不覆盖改写插件。 - proc_load_test.go 验证 Host 唯一性/惰性、chmod +x 错误提示、 Close 可见性,以及 procCore 不暴露内核内部机制的断言。 验证:go build ./... 通过;go test -race ./internal/plugin/... 全绿; 全仓 go test 无新增失败;git diff third_party/homeagent-sdk/sdk/ 为空。 既有告警 cabi/loader.go:156 unsafe.Pointer 非本次引入。 Ref: docs/zh/架构迁移评估.md §3.3/§3.4/§3.8、docs/zh/plugin-migration-plan.md Part 3
51 lines
1.4 KiB
Go
51 lines
1.4 KiB
Go
//go:build darwin
|
||
|
||
package proc
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
|
||
"golang.org/x/sys/unix"
|
||
)
|
||
|
||
// allocShm 用临时文件 + mmap 创建共享段(macOS)。
|
||
//
|
||
// macOS 没有 memfd_create。改用 os.CreateTemp 后立即 unlink:文件名从目录树消失,
|
||
// 但 fd 与映射继续有效,进程退出即回收——与 memfd 的不残留语义一致。
|
||
// 已 unlink 的 fd 仍可经 ExtraFiles 传给子进程,子进程 mmap 同一 inode,
|
||
// 故「全部插件共享一块段」的前提在 macOS 同样成立。
|
||
func allocShm(size int) (*os.File, []byte, error) {
|
||
f, err := os.CreateTemp("", "hastagectx-*")
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("proc: 创建共享段临时文件: %w", err)
|
||
}
|
||
// 立即摘除目录项:后续无人能按路径打开它,也不会有残留文件
|
||
if err := os.Remove(f.Name()); err != nil {
|
||
f.Close()
|
||
return nil, nil, fmt.Errorf("proc: unlink 共享段临时文件: %w", err)
|
||
}
|
||
if err := f.Truncate(int64(size)); err != nil {
|
||
f.Close()
|
||
return nil, nil, fmt.Errorf("proc: 共享段 ftruncate: %w", err)
|
||
}
|
||
data, err := unix.Mmap(int(f.Fd()), 0, size,
|
||
unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
|
||
if err != nil {
|
||
f.Close()
|
||
return nil, nil, fmt.Errorf("proc: 共享段 mmap: %w", err)
|
||
}
|
||
return f, data, nil
|
||
}
|
||
|
||
// freeShm 解除映射并关闭段。
|
||
func freeShm(f *os.File, data []byte) error {
|
||
if data != nil {
|
||
unix.Munmap(data)
|
||
}
|
||
if f != nil {
|
||
return f.Close()
|
||
}
|
||
return nil
|
||
}
|