mirror of
https://gitcode.com/JianFeeeee/homeagent-sdk.git
synced 2026-09-21 09:28:04 +00:00
plugindev: 支持子进程插件构建(entry=plugin.bin,零 cgo)
Part 3 工具链改造。插件业务代码零改动,只需把 plg.json 的 entry 从 plugin.so 换成 plugin.bin。 新增 templates/proc_main.go.tmpl(1113 行)——子进程运行时: - 51 个 core method 的插件侧 RPC 实现(procIO/procMemory/procSettings/ procSocial/procLLM/procKnowledge/procDocMemory/procTextMemory/procPluginMgr) - 共享段访问(fd 3 = 内核经 ExtraFiles 传入的 memfd)+ 16 字段 StageContext 编解码,布局常量与 internal/plugin/proc/shm.go 逐一对齐 - handleStageInvoke:拿锁 → 读段 → handler → **只写脏字段** → 放锁。 只读插件脏字段集为空 → 零写入 → 不可能覆盖他人改写 (对照 C ABI 副本模型实测 35.8~36.8% lost update) - 主循环每请求独立 goroutine:handler 内会反向调用内核并等应答, 在读循环里同步处理会死锁 - plugin.start 后显式上报 AutoRestart:公开 SDK 的 SetAutoRestart 是纯 setter 无 hook,隔着进程边界内核读不到(内核侧 corehandler.go:145 已就绪) 模板选择真实 .go 源文件 + //go:embed 而非 raw string:900+ 行代码塞在 字符串里写错只能等生成插件时才炸,作为源文件可被 parser/gofmt/vet 检查。 cmd_build.go:resolveBuild(target, proc) 分派;proc 走 go build -trimpath + CGO_ENABLED=0,交叉编译不再需要目标平台 C 工具链。bundle 模式各平台 产物同名故 zip 内加平台后缀(plugin.bin.linux.amd64)。 proc_runtime.go 生成时清理残留 z_bridge_gen.go/z_entry.c——同目录两套 main 会编译冲突,这让 .so → .bin 切换无需人工清理。 proc_runtime_test.go 16 项静态检查,防内核/插件两侧漂移: method 名清单、7 个内核调用、共享段常量与字段枚举顺序、stage 加锁顺序、 快照必须存序列化字符串(切片共享底层数组的坑在 11.3 已踩过)、 arena 不足须报错、日志走 stderr、版本不匹配须拒绝、零 cgo。 验证:真实 plugindev 构建 example/weather,plugin.go 逐字节未改, 产出静态链接 ELF;git diff sdk/ 为空(接口冻结)。 Ref: docs/zh/架构迁移评估.md §3、docs/zh/plugin-migration-plan.md Part 3
This commit is contained in:
54
tools/plugindev/proc_runtime.go
Normal file
54
tools/plugindev/proc_runtime.go
Normal file
@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// 子进程插件运行时(外部插件多进程化,Part 3)。
|
||||
//
|
||||
// 与旧 C ABI bridge 的差异:
|
||||
// - 模板改为**真实 .go 源文件**(templates/proc_main.go.tmpl)而非 raw string:
|
||||
// 900+ 行代码塞在字符串里,写错只能等生成插件时才炸;作为源文件可被
|
||||
// gofmt / go vet / parser 直接检查。
|
||||
// - 构建从 `-buildmode=c-shared` + CGO_ENABLED=1 改为普通 `go build` + CGO_ENABLED=0,
|
||||
// 交叉编译不再需要目标平台的 C 工具链(§3.1 连带消失项)。
|
||||
//
|
||||
// 设计依据:docs/zh/架构迁移评估.md §3、docs/zh/plugin-migration-plan.md Part 3
|
||||
|
||||
//go:embed templates/proc_main.go.tmpl
|
||||
var procTemplates embed.FS
|
||||
|
||||
// procEntryFile 是子进程插件的入口二进制名(与内核 internal/plugin/dynamic.go 的 binEntry 一致)。
|
||||
const procEntryFile = "plugin.bin"
|
||||
|
||||
// procGenFile 是生成的运行时文件名。
|
||||
// 前缀 z_ 使其在目录列表中排在业务代码之后,且与旧 bridge 的 z_bridge_gen.go 风格一致。
|
||||
const procGenFile = "z_proc_gen.go"
|
||||
|
||||
// generateProcRuntime 把子进程运行时写入插件目录,返回清理函数。
|
||||
//
|
||||
// 与 generateBridge 的差异:只写一个 .go 文件,不需要 C 入口(z_entry.c)。
|
||||
func generateProcRuntime() (func(), error) {
|
||||
data, err := procTemplates.ReadFile("templates/proc_main.go.tmpl")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取内嵌模板: %w", err)
|
||||
}
|
||||
|
||||
// 清理可能残留的 C ABI 产物:同目录同时存在两套 main 会编译冲突。
|
||||
// 这也让 .so → .bin 的切换无需人工清理。
|
||||
for _, stale := range []string{"z_bridge_gen.go", "z_entry.c"} {
|
||||
os.Remove(stale)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(procGenFile, data, 0644); err != nil {
|
||||
return nil, fmt.Errorf("写入 %s: %w", procGenFile, err)
|
||||
}
|
||||
return func() { os.Remove(procGenFile) }, nil
|
||||
}
|
||||
|
||||
// isProcEntry 判断 plg.json 的 entry 是否声明了子进程模式。
|
||||
func isProcEntry(entry string) bool {
|
||||
return entry == procEntryFile
|
||||
}
|
||||
Reference in New Issue
Block a user