mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-28 21:33:05 +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:
@ -31,10 +31,11 @@ type Host struct {
|
||||
shmSize int
|
||||
|
||||
// 事件环段(独立于 StageContext)
|
||||
evtfd *os.File // eventfd fd(fd 5 的句柄,子进程读取消费)
|
||||
evtRing *EvtRing // 内核侧事件环句柄
|
||||
evtRingFd *os.File // 事件环段 memfd(fd 4,子进程 mmap 读事件)
|
||||
evtData []byte // 事件环段 mmap 数据
|
||||
evtfd *os.File // Unix:eventfd/pipe 读端(fd 5)。Windows 为 nil,用 evtNotifyFd 。
|
||||
evtNotifyFd int // 通知句柄的平台无关标识(Unix 是真 fd,Windows 是伪 fd)
|
||||
evtRing *EvtRing // 内核侧事件环句柄
|
||||
evtRingFd *os.File // Unix:事件环段 memfd(fd 4)。Windows 为 nil(命名段)。
|
||||
evtData []byte // 事件环段 mmap 数据
|
||||
|
||||
// evtSubscriber 由 internal/plugin 注入,coreHandler 用它接子进程的 events.subscribe 请求。
|
||||
// proc 包不依赖 internal/plugin(循环依赖),故用接口类型存储。
|
||||
@ -48,11 +49,13 @@ type Host struct {
|
||||
|
||||
// NewHost 创建共享段(平台层 allocShm + 布局初始化)。
|
||||
//
|
||||
// 段的分配按平台分开(shmalloc_*.go):Linux 用 memfd,macOS 用
|
||||
// 立即 unlink 的临时文件(无 memfd_create),其余平台明确报错。
|
||||
// 两者语义一致:无文件名残留,fd 可经 ExtraFiles 传给子进程,
|
||||
// 子进程 mmap 同一 inode——「全部插件共享一块段」的前提得以成立。
|
||||
// 实验 2 已验证父子 mmap 到不同虚拟地址时相对偏移仍正确解引用。
|
||||
// 段的**传递机制**按平台分开(shmalloc_*.go),但**布局**完全一致:
|
||||
// - Linux:memfd,经 ExtraFiles 传继承 fd
|
||||
// - macOS:立即 unlink 的临时文件(无 memfd_create),同样走 fd 继承
|
||||
// - Windows:命名 FileMapping(无 fd 继承语义),插件按名字打开
|
||||
//
|
||||
// 三者共同点:全部插件看到同一份物理页,段内一律用相对偏移而非指针
|
||||
// (实验 2 已验证各进程 mmap 到不同虚拟地址时偏移解引用仍正确)。
|
||||
func NewHost() (*Host, error) {
|
||||
memfd, data, err := allocShm(shmDefaultSize)
|
||||
if err != nil {
|
||||
@ -78,15 +81,16 @@ func NewHost() (*Host, error) {
|
||||
evtRing.Init()
|
||||
|
||||
return &Host{
|
||||
memfd: memfd,
|
||||
data: data,
|
||||
seg: seg,
|
||||
shmSize: shmDefaultSize,
|
||||
evtfd: evtfdReadFile(efd),
|
||||
evtRing: evtRing,
|
||||
evtRingFd: evtRingFd,
|
||||
evtData: evtData,
|
||||
locks: &lockRegistry{},
|
||||
memfd: memfd,
|
||||
data: data,
|
||||
seg: seg,
|
||||
shmSize: shmDefaultSize,
|
||||
evtfd: evtfdReadFile(efd),
|
||||
evtNotifyFd: efd,
|
||||
evtRing: evtRing,
|
||||
evtRingFd: evtRingFd,
|
||||
evtData: evtData,
|
||||
locks: &lockRegistry{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -117,6 +121,7 @@ func (h *Host) Close() error {
|
||||
h.evtfd.Close()
|
||||
h.evtfd = nil
|
||||
}
|
||||
evtfdClose(h.evtNotifyFd)
|
||||
return firstErr
|
||||
}
|
||||
|
||||
@ -240,9 +245,16 @@ func (h *Host) ShmSize() int { return h.shmSize }
|
||||
// EvtRing 返回内核侧事件环句柄。
|
||||
func (h *Host) EvtRing() *EvtRing { return h.evtRing }
|
||||
|
||||
// Evtfd 返回 eventfd 的 *os.File(供 EventRing 写通知)。
|
||||
// Evtfd 返回通知读端的 *os.File(Unix;eventfd/pipe)。
|
||||
// Windows 返回 nil——命名 Event 不是文件句柄,用 EvtNotifyFd 代替。
|
||||
func (h *Host) Evtfd() *os.File { return h.evtfd }
|
||||
|
||||
// EvtNotifyFd 返回通知句柄的平台无关标识,供 EventRing 写通知。
|
||||
//
|
||||
// Unix 是真 fd;Windows 是映射到命名 Event 句柄的伪 fd。
|
||||
// EvtfdNotify 接受这个值并按平台分派。
|
||||
func (h *Host) EvtNotifyFd() int { return h.evtNotifyFd }
|
||||
|
||||
// SetEvtSubscriber 注入事件环订阅接口(由 Registry 在创建 Host 后设置)。
|
||||
func (h *Host) SetEvtSubscriber(sub EvtRingSubscriber) { h.evtSubscriber = sub }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user