Linux下實現C++類的動態連結__儲存

來源:互聯網
上載者:User
1. 背景

在java中,jvm支援類的動態連結(Class.forName(String className)),用起來也很方便。動態連結是實現IOC(Inversion of Control,控制反轉,更形象的稱作依賴注入)的關鍵,用於將類間依賴從程式移到設定檔中。在架構不重新編譯的情況下,替換被依賴的類。

在linux下,C++只能通過C的dl API實現動態連結,需要先將動態連結程式庫編譯成.so,然後再調用API進行連結。下面看一下如何?動態連結C++類。和java實現IOC一樣,連結類需要依賴於介面,先定義一下架構需要依賴的介面部分:

class UserInterface {public:    virtual bool func() = 0;};
動態連結程式庫實現部分繼承這個介面,進行實現。

class ConcretInterface: public UserInterface {public:    virtual bool func() {        printf("A implementition of UserInterface");    }};
2. dl API

先介紹dl API部分,這些函數需要串連dl動態庫,-ldl:

void *dlopen(const char *filename, int flag);
用於開啟.so,並進行連結,返回連結後的handle。flag表示連結標誌:

RTLD_LAZY:消極式載入,在.so中第一次使用某個符號是再進行連結。只對函數符號有效,變數符號會被立即載入。

RTLD_NOW:在dlopen返回後,所有未定義的符號都需要進行連結,如果符號解析失敗,dlopen會返回失敗。

RTLD_GLOBAL:使用這個標誌,則這個庫中的符號可以被隨後的動態庫使用。

RTLD_LOCAL:與RTLD_GLOBAL相反,不可以以被隨後動態庫使用。

char *dlerror(void);
返回dl相關錯誤,如果從初始化或者上一次調用到目前為止沒有錯誤則返回NULL。

void *dlsym(void *handle, const char *symbol);
返回符號名為symbol的符號的地址,需要用到dlopen返回的動態連結程式庫的handle。需要使用dlerror來檢驗dlsym是否出錯,不能進通過返回的指標是否為NULL判斷是否出錯,因為符號地址為NULL屬於正常情況。
int dlclose(void *handle);
減少.so的引用計數,如果引用計數為0,則.so會被卸載。

3. 類載入實現

通過上面dl API可知,只能返回動態連結程式庫中的符號的地址,這個符號函數符號或者是變數符號,不能像java一樣直接返回一個類。而我們在動態連結類時,實際上只是需要這個類的執行個體對象,所以可以通過返回建立類對象的函數符號實作類別的串連。由於在C++中,可以對new操作符重載,進行其他的一些操作,分配資源等,此時調用delete釋放對象,會造成資源流失。所以為了支援重載new操作符,還需要釋放對象的函數。定義如下:

typedef bool (*user_interface_creator_t)(UserInterface **ui);typedef void (*user_interface_destroyer_t)(UserInterface *ui);
上面定義了兩個函數指標,分別是連結類的建立器和銷毀器。動態連結程式庫需要實現這兩個函數,從而是架構可以建立和銷毀對象。如果預設不支援new操作重載,可以去掉destroyer,簡化介面。

還有一點比較重要,由於C++支援了命名空間、類以及重載等機制,所以符號的命名規則和C不同。而dl API屬於C的部分,使用的C的命名規則,所以在實現上述兩個函數時(也就是說需要通過動態連結的C++函數)需要使用C的符號命名規則。這需要實現extern 'C'實現,具體如下:

extern 'C' {bool my_creator(UserInterface **ui){    if (NULL == ui) {        printf("invalid param");        return false;    }    *ui = new (std::nothrow) ConcrectInterface;    return true;}void my_destroyer(UserInterface *ui){    if (ui != NULL) {        delete ui;    }}};
my_creator和my_destroyer就是動態連結程式庫對介面的實現,可以通過函數名調用dlsym找到對應的地址,完成對象的建立和銷毀。下面,介紹一下架構,也就是動態連結的部分。

#include <dlfcn.h>#include <stdio.h>#include "user_interface.hpp"const char *so_path = "./user_impl.so";const char *creat_func = "my_creator";const char *destroy_func = "my_destroyer";int main (){    // load .so    void *handle = dlopen(so_path, RTLD_LAZY);    if (NULL == handle) {        printf("failed to open %s, error: %s", so_path,                dlerror());        return 1;    }       // reset errors    dlerror();    user_interface_creator_t creator =         (user_interface_creator_t)dlsym(handle, creat_func);    const char* err = dlerror();    if (err != NULL) {        printf("failed to load creator, error: %s", err);        return 1;    }       // reset errors    dlerror();    user_interface_destroyer_t destroyer =         (user_interface_destroyer_t)dlsym(handle, destroy_func);    err = dlerror();    if (err != NULL) {        printf("failed to load destroyer, error: %s", err);        return 1;    }       UserInterface *ui;    if (!(*creator)(&ui)) {        printf("failed to creat ui");        return 1;    }       ui->func();    (*destroyer)(ui);    dlclose(handle);}
這裡需要注意每次調用dl API函數後需要調用dlerror清除error狀態。
4. 坑

如果動態連結程式庫和可執行程式依賴同一個庫,比如依賴同一個日誌庫,在編譯動態連結程式庫和可執行程式時需要注意一下。要確保對於庫的符號引用的解析會指向同一個符號。具體做法:

(1)編譯.so時,不連結依賴的庫,對於庫的符號的引用都是undefined。
(2)可執行程式需要連結依賴的庫,並且要指定連結選項-rdynamic,將全域符號可以用於動態連結程式庫的符號引用的解析。

如果沒有做到以上兩點,會出現同一個符號引用會被解析到兩個符號,若符號依賴的是一個變數的符號(比如符號本身就是一個變數,或者是函數符號,但依賴於某個變數),那麼就會出現個別古怪、異常的現象。

對於.so依賴一個庫,但是可執行程式沒有依賴,那麼編譯.so時必須把這個庫連結,否則會出現undefined symbol。

再介紹下調試動態連結程式庫時用到的工具,因為經常會出現各種未定義的符號,所以需要查看.so或者可執行程式的符號資訊,可以使用nm,比如,類型U或者類型T的符號。還可以使用c++filt將符號名解析成可讀的字串。




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.