mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-03 15:53:56 +00:00
feat(shm): 统一共享内存区域(§13.1) — 单 memfd 容纳 SuperBlock+StageContext+EvtRing
- unified.go: SuperBlock 布局 + SharedRef 类型 - NewHost(): 两段 memfd 合并为单一 memfd,fd 3 = 统一区域,fd 4 = eventfd - 子进程侧: testdata 插件从 SuperBlock 解析 ctxOff/evtOff - streaming 测试: EvtConsumer 协程在 host.Close 前 Stop+Wait 防 SIGSEGV
This commit is contained in:
@ -9,48 +9,37 @@ import (
|
||||
pubsdk "gitcode.com/JianFeeeee/homeagent-sdk/sdk"
|
||||
)
|
||||
|
||||
// Host 持有**被全部子进程插件共享的一块 StageContext 段**,是共享内存数据面的
|
||||
// Host 持有**被全部子进程插件共享的统一内存区域**,是共享数据面的
|
||||
// 所有权中心(§3.3/§3.4)。
|
||||
//
|
||||
// ❗ 为什么必须共享一块段(这是一个容易走错的关键点):
|
||||
// 若每个插件各持一块段,则「内核 ctx → 段 → 插件改 → 回读 ctx」在多插件下退化成
|
||||
// ❗ 为什么必须共享一块区域:
|
||||
// 若每个插件各持一块段,则「内核 ctx → 段 → 插件改 → 回读 ctx」退化成
|
||||
// 副本模型——两个插件各写各的段、各自回读,最后回读者覆盖前者,
|
||||
// lost update 原样复现(§8.4 实测 35.8~36.8%)。
|
||||
// 实验 8 的做法是 5 个 worker 进程 mmap **同一个 memfd**,本实现与之一致。
|
||||
//
|
||||
// 生命周期:Host 由 registry 创建一次,随内核存活;每个插件 spawn 时经
|
||||
// ExtraFiles 拿到同一 memfd(fd 3),mmap 后即看到同一份物理页。
|
||||
// 统一区域(§13.1):单 memfd 包含 SuperBlock + StageContext + EvtRing。
|
||||
// 子进程经 fd 3 mmap 同一 memfd → 同一份物理页,区域头 SuperBlock
|
||||
// 告知各 segment 的偏移与大小。
|
||||
//
|
||||
// 另外持有事件环段(§3.6):独立于 StageContext 的事件通知通道,
|
||||
// 子进程从 eventfd 感知新事件并从 mmap 读 slot。
|
||||
// fd 分配:fd 3 = StageContext,fd 4 = 事件环,fd 5 = eventfd。
|
||||
// fd 分配:fd 3 = 统一区域,fd 4 = eventfd。
|
||||
type Host struct {
|
||||
memfd *os.File
|
||||
data []byte
|
||||
seg *Segment
|
||||
shmSize int
|
||||
data []byte // 统一区域完整 mmap
|
||||
unified *unifiedRegion // SuperBlock 解析结果
|
||||
seg *Segment // StageContext segment(位于 unified ctxData)
|
||||
shmSize int // 统一区域总大小
|
||||
|
||||
// 事件环段(独立于 StageContext)
|
||||
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 数据
|
||||
// 事件通知(独立于共享段)
|
||||
evtfd *os.File // Unix:eventfd/pipe 读端(fd 4)。Windows 为 nil。
|
||||
evtNotifyFd int // 通知句柄的平台无关标识
|
||||
evtRing *EvtRing // 内核侧事件环句柄(位于 unified evtData)
|
||||
|
||||
// evtSubscriber 由 internal/plugin 注入,coreHandler 用它接子进程的 events.subscribe 请求。
|
||||
// proc 包不依赖 internal/plugin(循环依赖),故用接口类型存储。
|
||||
evtSubscriber EvtRingSubscriber
|
||||
|
||||
locks *lockRegistry
|
||||
stageMu sync.Mutex
|
||||
coordMu sync.Mutex
|
||||
coord *stageCoordinator
|
||||
|
||||
// sup 是内核侧唯一的子进程台账,与共享段同生命周期。
|
||||
//
|
||||
// 放在 Host 而不是 registry 的理由:能拿到 Host 的地方就能拿到台账,
|
||||
// 而 Host 本就是「全部子进程插件共享的那一份内核侧状态」。
|
||||
sup *Supervisor
|
||||
locks *lockRegistry
|
||||
stageMu sync.Mutex
|
||||
coordMu sync.Mutex
|
||||
coord *stageCoordinator
|
||||
sup *Supervisor
|
||||
}
|
||||
|
||||
// NewHost 创建共享段(平台层 allocShm + 布局初始化)。
|
||||
@ -63,22 +52,34 @@ type Host struct {
|
||||
// 三者共同点:全部插件看到同一份物理页,段内一律用相对偏移而非指针
|
||||
// (实验 2 已验证各进程 mmap 到不同虚拟地址时偏移解引用仍正确)。
|
||||
func NewHost() (*Host, error) {
|
||||
memfd, data, err := allocShm(shmDefaultSize)
|
||||
// 统一区域大小:SuperBlock + StageContext segment + EvtRing segment
|
||||
unifiedSize := superBlockSize + shmDefaultSize + evtTotalSize
|
||||
memfd, data, err := allocShm(unifiedSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seg, err := NewSegment(data)
|
||||
|
||||
// 初始化 SuperBlock + 两个 segment
|
||||
ur, err := initUnifiedRegion(data, shmDefaultSize, evtTotalSize)
|
||||
if err != nil {
|
||||
freeShm(memfd, data)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建事件环段(独立于 StageContext)
|
||||
evtRingFd, evtData, efd, err := allocEvtRing()
|
||||
// 创建 StageContext segment(位于 SuperBlock 之后)
|
||||
seg, err := NewSegment(ur.ctxData())
|
||||
if err != nil {
|
||||
freeShm(memfd, data)
|
||||
return nil, fmt.Errorf("事件环: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 初始化 EvtRing 子区域头部(magic/version/cap)
|
||||
evtData := ur.evtData()
|
||||
putU32(evtData[evtOffMagic:], evtRingMagic)
|
||||
putU32(evtData[evtOffVersion:], evtRingVersion)
|
||||
putU32(evtData[evtOffCap:], evtRingCap)
|
||||
|
||||
// 创建 EvtRing segment(位于 StageContext 之后)
|
||||
evtRing, err := NewEvtRing(evtData)
|
||||
if err != nil {
|
||||
freeShm(memfd, data)
|
||||
@ -86,17 +87,23 @@ func NewHost() (*Host, error) {
|
||||
}
|
||||
evtRing.Init()
|
||||
|
||||
// eventfd 独立于共享段,仍为单独 fd
|
||||
efd, err := evtfdCreate()
|
||||
if err != nil {
|
||||
freeShm(memfd, data)
|
||||
return nil, fmt.Errorf("创建 eventfd: %w", err)
|
||||
}
|
||||
|
||||
return &Host{
|
||||
sup: NewSupervisor(),
|
||||
memfd: memfd,
|
||||
data: data,
|
||||
unified: ur,
|
||||
seg: seg,
|
||||
shmSize: shmDefaultSize,
|
||||
shmSize: unifiedSize,
|
||||
evtfd: evtfdReadFile(efd),
|
||||
evtNotifyFd: efd,
|
||||
evtRing: evtRing,
|
||||
evtRingFd: evtRingFd,
|
||||
evtData: evtData,
|
||||
locks: &lockRegistry{},
|
||||
}, nil
|
||||
}
|
||||
@ -123,14 +130,7 @@ func (h *Host) Close() error {
|
||||
if err := freeShm(h.memfd, h.data); err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
h.data, h.memfd = nil, nil
|
||||
}
|
||||
if h.evtData != nil {
|
||||
if h.evtRingFd != nil {
|
||||
h.evtRingFd.Close()
|
||||
h.evtRingFd = nil
|
||||
}
|
||||
h.evtData = nil
|
||||
h.data, h.memfd, h.unified = nil, nil, nil
|
||||
}
|
||||
if h.evtfd != nil {
|
||||
h.evtfd.Close()
|
||||
@ -321,7 +321,7 @@ func (h *Host) EvtNotifyFd() int { return h.evtNotifyFd }
|
||||
func (h *Host) SetEvtSubscriber(sub EvtRingSubscriber) { h.evtSubscriber = sub }
|
||||
|
||||
// EvtData 返回事件环段 mmap 数据(子进程消费者用)。
|
||||
func (h *Host) EvtData() []byte { return h.evtData }
|
||||
func (h *Host) EvtData() []byte { return h.unified.evtData() }
|
||||
|
||||
// EvtfdReadFile 返回 eventfd 的 *os.File(供子进程读取消费)。
|
||||
func (h *Host) EvtfdReadFile() *os.File { return h.evtfd }
|
||||
|
||||
Reference in New Issue
Block a user