Files
HomeAgent/internal/plugin/proc/evtfd_linux.go
JianFeeeee d027c964e2 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
2026-09-02 19:07:14 +08:00

39 lines
1.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build linux
package proc
import (
"os"
"syscall"
"golang.org/x/sys/unix"
)
// evtfdCreate 创建 Linux eventfdEFD_NONBLOCK | EFD_CLOEXEC
//
// 语义64 位无符号计数器,多次 Write(8) 只累加Read 一次取出合并值。
// 计数合并满足 §3.6 的设计1000 个 token 事件只唤醒几次。
// 走 Go netpoller实验 1 已验证 200 等待者仅 +1 OS 线程)。
func evtfdCreate() (int, error) {
return unix.Eventfd(0, unix.EFD_CLOEXEC)
}
// evtfdNotify 写 1 到 eventfd 通知子进程有新事件post-and-forget
//
// EFD_NONBLOCK 保证不阻塞§3.6 约束 BBus.Publish 路径上绝不等待)。
// 计数语义使多事件写入合并成一次唤醒。
func EvtfdNotify(efd int) {
var buf [8]byte
buf[0] = 1
// 忽略错误EFD_NONBLOCK 下只有内存不足才会失败,此时进程已在崩溃边缘
syscall.Write(efd, buf[:])
}
// evtfdReadFile 把 eventfd 包装成 *os.File 供 netpoller 消费。
func evtfdReadFile(efd int) *os.File {
return os.NewFile(uintptr(efd), "evtring-notify")
}
// evtfdClose 关闭通知句柄。Unix 侧由 *os.File.Close 负责,此处为跨平台签名占位。
func evtfdClose(efd int) {}