首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言動態加載共享庫:dlopen/dlsym 與 GLib GModule 實戰

     編輯:關於C語言

C 語言動態加載共享庫:dlopen/dlsym 與 GLib GModule 實戰

插件架構是實現模塊化、可擴展系統的關鍵。本文介紹如何在 C 語言中使用作業系統原生接口(POSIX dlopen/dlsym/dlclose)及 GLib 的跨平台封裝 GModule,實現運行時動態加載第三方共享庫(.so/.dll),並調用約定介面函數。

核心需求

  • 主程序與插件二進制分離發布,插件置於指定目錄(如 plugins/)。
  • 插件必須導出已知名稱的函數(如 foo()、bar()、baz() 或統一入口 init_module())。
  • 主程序掃描目錄,逐一加載、初始化、調用、卸載插件。

方案一:POSIX 原生 API

#include <dlfcn.h>
void *handle = dlopen("./plugins/foo.so", RTLD_LAZY);
if (!handle) { fprintf(stderr, "%s\n", dlerror()); return; }
void (*func)() = dlsym(handle, "foo");
if (func) func();
dlclose(handle);
  • RTLD_LAZY:延遲綁定,首次調用時解析符號;RTLD_NOW 立即解析。
  • dlerror() 必須在調用後立即檢查,因後續調用會覆蓋錯誤信息。
  • Windows 對應 LoadLibrary/GetProcAddress/FreeLibrary。

方案二:GLib GModule 跨平台封裝

GLib 將平台差異抽象為 GModule,代碼更簡潔、可移植:

#include <glib.h>
#include <gmodule.h>

GModule *module = g_module_open("plugins/foo.so", G_MODULE_BIND_LAZY);
if (!module) { g_print("Open failed: %s\n", g_module_error()); return; }

void (*init_func)(PluginInterface *);
if (g_module_symbol(module, "init_module", (gpointer*)&init_func)) {
    PluginInterface iface;
    init_func(&iface);
    iface.hello();
    iface.greet("world");
}
g_module_close(module);
  • g_module_open 返回 GModule*,相當於庫句柄。
  • g_module_symbol 獲取符號地址,需強轉為函數指針類型。
  • g_module_close 釋放資源,引用計數歸零時真正卸載。
  • 需鏈接 -lglib-2.0 -lgmodule-2.0。

完整主程序範例

const char * const PLUGINS_PATH = "plugins";
int main() {
    GDir *dir = g_dir_open(PLUGINS_PATH, 0, NULL);
    const gchar *filename;
    while ((filename = g_dir_read_name(dir))) {
        gchar *path = g_strdup_printf("%s/%s", PLUGINS_PATH, filename);
        GModule *module = g_module_open(path, G_MODULE_BIND_LAZY);
        if (module) {
            InitModuleFunc init_func;
            if (g_module_symbol(module, "init_module", (gpointer*)&init_func)) {
                PluginInterface iface;
                init_func(&iface);
                iface.hello();
                iface.greet("wks");
            }
            g_module_close(module);
        }
        g_free(path);
    }
    g_dir_close(dir);
    return 0;
}

插件開發約定

  • 導出 init_module(PluginInterface *iface),填充函數指針結構體。
  • 編譯為共享庫:gcc -shared -fPIC plugin.c -o plugins/foo.so。
  • 主程序與插件共享 PluginInterface 定義頭文件。

最佳實踐提醒

  • 符號可見性控制:使用 -fvisibility=hidden 並顯式導出 init_module,避免符號衝突。
  • 版本兼容:接口結構體加入版本號,主程序據此適配。
  • 錯誤處理:每步檢查返回值,記錄 g_module_error() 或 dlerror()。
  • 線程安全:GLib 模塊操作非線程安全,需外部鎖保護。

動態加載賦予 C 程序極大的擴展性,是實現微內核、插件系統、熱更新的基石技術。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved