plugin: 卸载/重载后 dlclose 动态库句柄(cabiPlugin.Close),修复同路径 dlopen 复用旧句柄导致插件更新不生效

This commit is contained in:
root
2026-08-03 08:04:18 +08:00
parent 3f252edf20
commit 649e31269c
2 changed files with 29 additions and 1 deletions

View File

@ -51,6 +51,13 @@ func (p *cabiPlugin) Stop() error {
return nil
}
// Close 卸载动态库dlclose。卸载/重载后必须调用,否则同一路径的 dlopen
// 会复用旧句柄Linux dlopen 语义),新版本的 plugin.so 不会生效。
func (p *cabiPlugin) Close() error {
p.handle.Close()
return nil
}
func tryLoadSO(dir, name string, config map[string]interface{}) (sdk.Plugin, error) {
soPath := filepath.Join(dir, soEntry)
if _, err := os.Stat(soPath); os.IsNotExist(err) {

View File

@ -424,6 +424,7 @@ func (r *Registry) ReloadOne(name string) error {
plgDir := filepath.Join(r.plgDir, name)
r.mu.Lock()
var removed sdk.Plugin
if p, ok := r.plugins[name]; ok {
r.runStopHandlers(name)
if err := p.Stop(); err != nil {
@ -437,9 +438,12 @@ func (r *Registry) ReloadOne(name string) error {
break
}
}
removed = p
}
r.mu.Unlock()
r.closeDynamic(removed)
ok := r.loadOne(plgDir, name)
if !ok {
return fmt.Errorf("reload plugin %s failed", name)
@ -448,6 +452,20 @@ func (r *Registry) ReloadOne(name string) error {
return nil
}
// closeDynamic 释放动态加载插件的共享库句柄dlclose
// Linux dlopen 对同一路径返回已加载的旧句柄,若不释放,插件二进制更新后
// 重载/卸载仍会执行旧代码。Go plugin.Open 路径dynamicPlugin不可卸载跳过。
func (r *Registry) closeDynamic(p sdk.Plugin) {
if p == nil {
return
}
if c, ok := p.(interface{ Close() error }); ok {
if err := c.Close(); err != nil {
log.Printf("[plugin] close dynamic %s: %v", p.Name(), err)
}
}
}
func (r *Registry) List() []string {
r.mu.RLock()
defer r.mu.RUnlock()
@ -588,10 +606,11 @@ func (r *Registry) DisablePlugin(name, by string) error {
func (r *Registry) EnablePlugin(name string) error { return r.Enable(name) }
// RemovePlugin 卸载插件先停止stop handlers + Stop再执行插件注册的 onRemove
// 回调(删除专用,重载不触发),最后从注册表移除并清理禁用/工具注册。
// 回调(删除专用,重载不触发),最后从注册表移除并清理禁用/工具注册/配置
// 插件目录的物理删除由调用方pluginmgr负责。
func (r *Registry) RemovePlugin(name string) error {
r.mu.Lock()
var removed sdk.Plugin
p, ok := r.plugins[name]
if ok {
r.runStopHandlers(name)
@ -606,6 +625,7 @@ func (r *Registry) RemovePlugin(name string) error {
break
}
}
removed = p
}
r.runOnRemoveHandlers(name)
r.mu.Unlock()
@ -617,6 +637,7 @@ func (r *Registry) RemovePlugin(name string) error {
r.cfgReg.RemoveDisabledPlugin(name)
r.cfgReg.RemovePlugin(name)
}
r.closeDynamic(removed)
log.Printf("[plugin] removed: %s", name)
return nil
}