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

Linux 共用函式庫 (.so) 開發完整指南:建立、載入與實戰範例

     編輯:VC 教學

前言

Linux 共用函式庫 (Shared Object, .so) 相當於 Windows 的 DLL,實現程式碼重用、模組化部署與記憶體共享。本文詳述建立流程、動態載入機制、關鍵 API 與完整範例。

一、建立共用函式庫

原始碼:mytestso.c

#include <stdio.h>
#include <stdlib.h>

int GetMax(int a, int b) {
    return (a >= b) ? a : b;
}

int GetInt(const char* psztxt) {
    if (!psztxt) return -1;
    return atoi(psztxt);
}

編譯指令

gcc -fPIC -shared mytestso.c -o mytestso.so
參數說明
-fPIC產生位置無關碼 (Position Independent Code),支援共用記憶體映射
-shared輸出共用函式庫而非執行檔
-o mytestso.so輸出檔名

二、動態載入與使用

主程式:mytest.c

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    void* handle;
    char* error;
    int (*GetMax)(int, int);
    int (*GetInt)(const char*);
    int a = 200, b = 600;
    const char* txt = "1024";

    // 1. 開啟共用函式庫
    handle = dlopen("./mytestso.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }

    // 2. 取得函數位址
    GetMax = (int(*)(int,int)) dlsym(handle, "GetMax");
    if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(1); }

    GetInt = (int(*)(const char*)) dlsym(handle, "GetInt");
    if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(1); }

    // 3. 呼叫函數
    printf("max=%d\n", GetMax(a, b));
    printf("txt=%d\n", GetInt(txt));

    // 4. 關閉
    dlclose(handle);
    return 0;
}

編譯主程式

gcc mytest.c -ldl -o mytest
  • -ldl:連結 libdl,提供 dlopen 等 API

三、關鍵 API 詳解

1. dlopen(const char* filename, int flags)

參數說明
filename函式庫路徑。搜尋順序:
① 環境變數 LD_LIBRARY_PATH
② /etc/ld.so.cache (由 ldconfig 維護)
③ /usr/lib, /lib
④ 當前目錄 (需 ./ 前綴)
flagsRTLD_LAZY:延遲解析符號 (預設,效能較好)
RTLD_NOW:立即解析所有符號 (利於除錯)

2. dlsym(void* handle, const char* symbol)

  • 回傳符號 (函數/變數) 位址,需轉型為正確函數指標型別
  • 失敗回傳 NULL,需緊接呼叫 dlerror() 檢查

3. dlerror(void)

  • 回傳最近一次 dlopen/dlsym/dlclose 的錯誤訊息,僅能呼叫一次 (第二次回傳 NULL)

4. dlclose(void* handle)

  • 減少參考計數,歸零時卸載函式庫
  • 若有其他處理持有 handle,實際卸載延後

四、進階主題

建構子/解構子函數

// 函式庫載入時自動執行
__attribute__((constructor)) void MyLibInit() {
    printf("Library loaded\n");
}

// 函式庫卸載時自動執行
__attribute__((destructor)) void MyLibFini() {
    printf("Library unloaded\n");
}

版本控制與 soname

# 建立帶版本的庫
gcc -fPIC -shared -Wl,-soname,libmylib.so.1 mylib.c -o libmylib.so.1.0

# 建立連結
ln -sf libmylib.so.1.0 libmylib.so.1
ln -sf libmylib.so.1 libmylib.so

符號可見性控制

// 隱藏內部符號 (GCC 4+)
__attribute__((visibility("hidden"))) void InternalFunc();

// 只匯出標記的符號
// 編譯加 -fvisibility=hidden,公開函數加 __attribute__((visibility("default")))

C++ 專案注意事項

  • 使用 extern "C" 避免名稱修飾:extern "C" int GetMax(int, int);
  • 或編譯主程式與庫使用相同編譯器/ABI,直接用 C++ 連結
  • 例外處理跨庫邊界需確保相同運行庫 (libstdc++ 靜態/動態一致)

五、除錯技巧

  • LD_DEBUG=libs ./mytest:顯示動態連結器載入過程
  • ldd mytest:檢查相依函式庫解析狀況
  • nm -D mytestso.so:列出匯出符號
  • GDB 中 set solib-search-path 設定符號檔路徑

結語

Linux 共用函式庫機制成熟靈活,掌握 dlopen/dlsym/dlclose 動態載入流程、-fPIC 編譯要點、版本管理與符號可見性控制,能構建模組化、可擴充的系統架構。現代專案建議搭配 CMake 管理建構流程,自動處理平台差異。

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