使用c++編寫和使用.so動態連接庫

參考:ios


《程序員的自我修養 ---- 連接,裝載與庫》c++

C++ dlopen mini HOWTOhttp://www.tldp.org/HOWTO/pdf/C++-dlopen.pdf程序員


1,使用 c 生成動態連接庫mylib.so的簡單示例ide


     聲明文件mylib.h函數

-spa

#ifndef __MY_LIB_H__
#define __MY_LIB_H__
void foobar(int i);
#endif /* __MY_LIB_H__ */

-指針

    實現文件mylib.c對象

-接口

#include <stdio.h>
#include "mylib.h"
void foobar(int i)
{
  printf("Printing from mylib.so %d\n", i);
}

-進程

    使用 gcc mylib.c 編譯成一個共享對象文件,以下命令:


[steven@sasd c++]$ gcc-fPIC -shared -o mylib.so mylib.c

[steven@sasd c++]$ ll

total 16

-rw-rw-r--. 1 steven steven  109 Apr 13 09:27 mylib.c

-rw-rw-r--. 1 steven steven   90 Apr 13 09:26 mylib.h

-rwxrwxr-x. 1 steven steven 6210 Apr 1309:28 mylib.so

-

注:

-

2這裏的共享對象其實全稱是動態共享對象文件(Dynamic Shared Objects,簡寫爲DSO);

2-fPIC:地址無關代碼(Position-Independent Code),該技術主要用於解決SO中對絕對地址的重定位問題;

-

2,動態連接.so

-

下面在咱們的c++程序中連接並使用此.so文件,在C++中,是使用 linkage directive 方式指出任意非C++函數所用的語言,也就是用extern 「C」來指示對應的聲明是使用C來實現的,這樣對於該聲明中的文件的編譯會採用對應的C編譯器來編譯處理,以下爲使用示例:

-

// use_mylib.cpp
#include <iostream>
#include <string>
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
#include "mylib.h" // for foobar()
#ifdef __cplusplus
}
#endif
int main()
{
  foobar(100);
  return 0;
}

-

編譯及運行結果:

-

[steven@sasd c++]$ g++ -o use_mylibuse_mylib.cpp ./mylib.so

[steven@sasd c++]$ ./use_mylib

Printing from mylib.so 100

[steven@sasd c++]$

-

3,運行時動態加載.so

-

動態庫的運行時加載時經過一系列由動態連接器(dynamic linker)提供的API來實現的,這幾個API的實現都是在libdl.so中的,且相關的聲明和常量定義在頭文件<dlfcn.h>中。

-

dlopen():主要用來打開一個.so,並將其加載到進程的地址空間,完成初始化過程,原型以下:

-

void *dlopen(const char *filename, int flag);

-

filenamethe dynamic library file named by the null-terminated string;對於路徑設置爲相對路徑或絕對路徑情形下,dlopen()是如何查找該動態庫文件的,這個直接參考man

-

flag:用於表示函數符號的解析方式,如:RTLD_LAZY表示使用延遲綁定,只有在函數第一次被使用時纔會去綁定,即PLT機制;相對應的爲RTLD_NOW表示當模塊被加載時即完成全部的函數綁定工做,若是有任何未定義的符號引用的綁定工做沒法完成,dlopen都直接返回錯誤;其餘的flag標識能夠直接參考man

-

返回值是被加載的模塊的句柄,該句柄會被其餘的幾個API使用到,若是加載失敗,則返回NULL

-

dlsym():經過此函數來找到所須要的符號,定義以下:

-

void *dlsym(void *handle, const char *symbol);

-

第一個參數handle就是dlopen返回的句柄,第二個參數symbol表示要查找的符號的名字,一個以‘\0’ 結尾的字符串;

若是dlsym找到了相應的符號,則返回該符號的值;若是沒有找到,則返回NULL

對於不一樣類型的符號,返回的指針意義不一樣:若是爲函數,則爲函數地址;若是爲變量,則爲變量的地址;若是爲常量,則應該爲常量的值;

-

dlerror():在調用dlopendlsymdlclose以後,均可以經過dlerror()來判斷上一次調用是否成功,dlerror的返回值爲char*,若是返回NULL,則表示調用成功;不然返回對應的錯誤消息;函數原型聲明以下:

-

char *dlerror(void);

-

dlclose():卸載一個已經加載的模塊。

-

在系統內部會維持一個加載引用計數器,每次調用dlopen()來加載模塊時,相應的計數器加1;每次調用dlclose()卸載某模塊時,相應計數器減1。當計數器等於0時,該模塊纔會真正被卸載掉。

-

下面直接上示例:

-

// ex_dload_so.cpp
#include <iostream>
using namespace std;
#include <dlfcn.h>
int main()
{
  // 爲上面生成的.so文件的絕對路徑
  const char *MYLIB_PATH = "/home/steven/test/c++/mylib.so";
                                                            
  // 加載指定的 .so 文件
  void *handle = dlopen(MYLIB_PATH, RTLD_NOW);
  if (handle == NULL)
  {
    cerr << "Open library " << MYLIB_PATH << " error: " << dlerror() << endl;
    return -1;
  }
                                                            
  // 查找函數foobar,並返回函數指針
  void (*fn_foobar)(int);
  fn_foobar = (void (*)(int))dlsym(handle, "foobar");
  char *error = NULL;
  if ((error = dlerror()) != NULL)
  {
    cerr << "Symbol foobar not found: " << error << endl;
    dlclose(handle);
    return -2;
  }
                                                            
  // 調用對應的foobar函數打印輸出
  fn_foobar(123);
                                                            
  dlclose(handle);
  return 0;
}

-

編譯運行效果:

-

[steven@sasd c++]$ g++-ldl -o ex_dload_so ex_dload_so.cpp

[steven@sasd c++]$ ./ex_dload_so

Printing from mylib.so 123

[steven@sasdc++]$

-

4,使用c++建立和使用含有class.so,採用多態機制實現接口發佈,示例摘自《C++ dlopen mini HOWTO

-

1)生成動態連接庫triangle.so文件

-

// polygon.h
#ifndef __POLYGON_H__
#define __POLYGON_H__
class Polygon
{
protected:
  double m_side_len;
public:
  Polygon() : m_side_len(0) {}
  virtual ~Polygon() {}
  void set_side_len(double side_len)
  {
    m_side_len = side_len;
  }
                                      
  virtual double area() const = 0;
};
typedef Polygon* create_t();
typedef void destroy_t(Polygon*);
#endif /* __POLYGON_H__ */

-

// triangle.cpp
#include "polygon.h"
#include <cmath>
class Triangle: public Polygon
{
public:
  virtual double area() const
  {
    return m_side_len * m_side_len * sqrt(3) / 2;
  }
};
extern "C" Polygon* create()
{
  return new Triangle;
}
extern "C" void destroy(Polygon *p)
{
  delete p;
}

-

編譯生成 triangle.so

-

[steven@sasd c++]$ g++ -fPIC -shared -o triangle.so triangle.cpp

-

2) 使用該動態連接庫

-

// ex_dload_cppso.cpp
#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
#include "polygon.h"
int main()
{
  // load triangle library
  void *triangle = dlopen("./triangle.so", RTLD_LAZY);
  if (!triangle)
  {
    cerr << "Can not load library: " << dlerror() << endl;
    return -1;
  }
         
  // reset errors
  dlerror();
         
  // load the symbols "create"
  create_t *create_triangle = (create_t*) dlsym(triangle, "create");
  const char *dlsym_err = dlerror();
  if (dlsym_err)
  {
    cerr << "Can not load symbol create: " << dlsym_err << endl;
    return -2;
  }
         
  // load the symbols "destroy"
  destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");
  dlsym_err = dlerror();
  if (dlsym_err)
  {
    cerr << "Can not load symbol destroy: " << dlsym_err << endl;
    return -2;
  }
         
  // create an instance of class <Trangle>
  Polygon *poly = create_triangle();
         
  // set side length of <Triagle>
  poly->set_side_len(8);
  cout << "The area is " << poly->area() << endl;
         
  // destroy the class
  destroy_triangle(poly);
         
  // unload the triangle.so library
  dlclose(triangle);
         
  return 0;
}

-

編譯運行:

-

[steven@sasd c++]$ g++ -ldl -o ex_dload_cppso ex_dload_cppso.cpp

[steven@sasd c++]$ ./ex_dload_cppso

The area is 55.4256

[steven@sasd c++]$

相關文章
相關標籤/搜索