插件架構是實現模塊化、可擴展系統的關鍵。本文介紹如何在 C 語言中使用作業系統原生接口(POSIX dlopen/dlsym/dlclose)及 GLib 的跨平台封裝 GModule,實現運行時動態加載第三方共享庫(.so/.dll),並調用約定介面函數。
plugins/)。foo()、bar()、baz() 或統一入口 init_module())。#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() 必須在調用後立即檢查,因後續調用會覆蓋錯誤信息。LoadLibrary/GetProcAddress/FreeLibrary。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()。動態加載賦予 C 程序極大的擴展性,是實現微內核、插件系統、熱更新的基石技術。