mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-03 15:53:56 +00:00
proc: Windows 共享内存 + 事件通知适配(Part 6.2 内核侧)
补齐内核侧的 Windows 创建端,与 6.1 的插件侧打开端配对。三平台 (linux/darwin/windows)现在都能构建 internal/plugin/proc。 ## Windows 走命名内核对象(无 fd 继承语义) os/exec 的 ExtraFiles 在 Windows 实现里不被支持,故: - shmalloc_windows.go:CreateFileMappingW(INVALID_HANDLE_VALUE + 命名 → 系统页文件支撑的匿名段,不落盘)+ MapViewOfFile - evtfd_windows.go:CreateEventW 命名 Event 对象 + SetEvent 通知 - shmpass_windows.go:把段名/对象名经环境变量注入子进程 (HOMEAGENT_SHM_STAGE / HOMEAGENT_SHM_EVTRING / HOMEAGENT_EVT_EVENT) 名字带 PID + 递增序号:多个 homed 实例并存时不能撞名。 Event 与 eventfd 的语义差异:Event 是二元信号,多次 SetEvent 只对应一次 唤醒,不累积。不影响正确性——消费者被唤醒后按 readSeq 追 writeSeq 批量 drain,丢的是"唤醒次数"不是"事件";事件环本身就允许溢出丢弃并让消费者 知道丢了(dropped 计数),通知面从来不是可靠投递语义。 ## 传递机制抽象为 shmpass_*.go Plugin.Start 不再直接构造 ExtraFiles 列表,改为问 Host 要: Env: p.host.procEnvForShm() // Windows 返回段名,Unix 返回 nil ExtraFiles: p.host.procExtraFilesForShm() // Unix 返回 fd 列表,Windows 返回 nil 平台差异被收敛到这一对函数,Plugin/coreHandler/stage 全部平台无关。 ## macOS pipe 生命周期修正 原实现只返回读端 fd,写端 *os.File 无人持有 → 可能被 GC 回收 → 读端收到 EOF 而非阻塞 → 消费循环变忙转。改为 pipePair 表同时持有两端, evtfdClose 一并关闭。 ## E2E 测试跟进模板拆分 模板从单文件拆成三个(主体 + unix/windows 挂载),测试需要一并落盘, 否则编译报 attachStageShm undefined。procRuntimeTemplates 表必须与 SDK 仓 proc_runtime.go 的 procRuntimeFiles 一致。 验证:三平台 go build ./internal/plugin/... 通过(gojieba 的 cgo 依赖 导致 internal/memory 在非 linux 失败,与本次无关); go test -race ./internal/plugin/... 全绿,含 2 项真实模板 E2E。 Ref: docs/zh/架构迁移评估.md §9.2、docs/zh/plugin-migration-plan.md Part 6
This commit is contained in:
@ -4,30 +4,78 @@ package proc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// evtfdCreate 用 pipe 模拟 Linux eventfd 的通知语义(macOS 无 eventfd)。
|
||||
// macOS 侧事件通知:用 pipe 模拟 eventfd(macOS 无 eventfd_create)。
|
||||
//
|
||||
// 限制:不具 eventfd 的计数合并(多次写会触发多次读),
|
||||
// 但事件环本身允许溢出丢弃,consumer 在 drainEvents 里按 readSeq 批量读取,
|
||||
// 故多次唤醒只多几次无效循环(readSeq == writeSeq 时立即返回),不造成正确性问题。
|
||||
// 与 eventfd 的语义差异:pipe 不具计数合并,多次写会触发多次读。
|
||||
// 这不影响正确性——消费者在 drainEvents 里按 readSeq 追 writeSeq 批量读,
|
||||
// 多次唤醒只多几次空循环(readSeq == writeSeq 时立即返回)。
|
||||
//
|
||||
// 走 Go netpoller(os.File.Read 阻塞时只 park goroutine,实验 1 已验证)。
|
||||
// 走 Go netpoller:os.File.Read 阻塞时只 park goroutine,不占 OS 线程
|
||||
// (实验 1 已验证 200 个等待者仅增 1 个 OS 线程)。
|
||||
//
|
||||
// ❗ 必须同时持有读端与写端:写端若被 GC 回收,读端会收到 EOF 而非阻塞,
|
||||
// 消费循环变成忙转。故用 pipePair 表存住两端。
|
||||
type pipePair struct {
|
||||
r *os.File
|
||||
w *os.File
|
||||
}
|
||||
|
||||
var (
|
||||
evtPipes = map[int]*pipePair{}
|
||||
evtPipesMu sync.Mutex
|
||||
)
|
||||
|
||||
// evtfdCreate 建 pipe,返回读端 fd。
|
||||
func evtfdCreate() (int, error) {
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return int(r.Fd()), nil
|
||||
fd := int(r.Fd())
|
||||
evtPipesMu.Lock()
|
||||
evtPipes[fd] = &pipePair{r: r, w: w}
|
||||
evtPipesMu.Unlock()
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
// evtfdNotify 写 1 字节通知子进程有新事件(post-and-forget)。
|
||||
// EvtfdNotify 写 1 字节通知子进程有新事件(post-and-forget)。
|
||||
//
|
||||
// 直接写裸 fd 而非 pipePair.w:本函数在 Bus.Publish 路径上被高频调用,
|
||||
// 查表加锁不值得。写端 fd 由 pipePair 持有引用故不会被 GC 回收。
|
||||
func EvtfdNotify(efd int) {
|
||||
evtPipesMu.Lock()
|
||||
p, ok := evtPipes[efd]
|
||||
evtPipesMu.Unlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var buf [1]byte
|
||||
syscall.Write(efd, buf[:])
|
||||
// 忽略错误:管道满说明消费者落后,事件环本身允许溢出丢弃
|
||||
syscall.Write(int(p.w.Fd()), buf[:])
|
||||
}
|
||||
|
||||
// evtfdReadFile 把事件通知读端包装成 *os.File 供 netpoller 消费。
|
||||
// evtfdReadFile 返回通知读端(供 netpoller 消费)。
|
||||
func evtfdReadFile(efd int) *os.File {
|
||||
return os.NewFile(uintptr(efd), "evtring-notify")
|
||||
evtPipesMu.Lock()
|
||||
defer evtPipesMu.Unlock()
|
||||
if p, ok := evtPipes[efd]; ok {
|
||||
return p.r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// evtfdClose 关闭 pipe 两端。
|
||||
func evtfdClose(efd int) {
|
||||
evtPipesMu.Lock()
|
||||
p, ok := evtPipes[efd]
|
||||
delete(evtPipes, efd)
|
||||
evtPipesMu.Unlock()
|
||||
if ok {
|
||||
p.r.Close()
|
||||
p.w.Close()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user