您的位置:首页 > 运维架构 > Linux

linux C++/C 动态链接库使用

2014-12-09 11:18 239 查看
linux下的生成动态库是so

gcc -shared 就可以。

 1.对Linux操作,一般都推荐在普通用户模式下,如果需要超级用户的权限,则可以通过su root,输入root用户密码切换。我是个人学习使用,同时又有很多的操作都要使用root用户,因此就直接在root用户下进行编译。

  2.编译生成动态库的命令为:gcc (-fpic) -shared -o libmyfunction.so myfunction.c

  -fpic 使输出的对象模块是按照可重定位地址方式生成的。

  -shared指定把对应的源文件生成对应的动态链接库文件。

有关动态链接库的概念,何时使用,使用的优点就不在此多多解释了,下面,简单记录一下使用的具体过程。

1、涉及到的库<dlfcn.h>,该库中提供了四个轻松调用动态链接库的API

a) void *dlopen (const char *so_file_path, int open_mode)

dlopen是打开动态链接库文件的API,这里so_file_path是so文件的路径,open_mode是打开so文件的模式,

常用的有两种:RTLD_NOW和RTLD_LAZY,

RTLD_NOW:在dlopen()方法调用完成之前就去动态的解析so文件里面的所有未定义的符号,如果无法解析,则打开失败。

RTLD_LAZY: 只有当so文件里面的未定义的符号在真正使用的时候才去解析。

这里要注意的是:如果加载的动态库还依赖其他的动态库,必须使用RTLD_NOW。

函数调用成功,则返回该so文件的句柄(指针)so_handle,否则返回NULL。

b) void *dlsym (void *so_handle,const char *method_name)

dlsym的调用是用来得到so文件某个具体的函数的指针的,函数调用成功则返回method_name函数的指针,否则返回NULL。

so_handle:使用dlopen()返回的so句柄

method_name:定义在so中的方法名(这里要注意的是,该so方法中不能有重载的函数,当然,c语言是不支持函数重载的)

c) int dlclose (void *so_handle)

关闭dlopen()返回的so句柄

这里要注意:如果在使用dlsym()返回的函数指针的时候调用了该方法,那么,肯定会出现Segment fault的错误,因为调用此方法之后,代表对so动态库的资源回收。

d) char *dlerror (void)

返回在调用上述方法失败时的具体错误信息。

2、下面来看一个简单的实例:

该实例的场景是:把student.cpp和teacher.cpp写成动态库的形式提供给callPlugin.cpp调用,如果再需要开发一个其他的类如:police.cpp,只需要按照有关约定编写好,再编译成动态库,则可不要更改任何应用程序的框架代码,即 实现了插件式开发。

1)base.h,该文件的作用是定义两个基本方法,也就是接口方法,提供给接口调用者和接口实现者使用。

// base.h

#include<iostream>

#include<string>

using namespace std;

class BasePeople;

// 下面两个方法必须都用extern "C"进行修饰,这个主要是因为C++中的符号命名方法和C的不一样,具体可以查看有关资料

// 在C++中,方法method(int a, float b)可能会被命名为:method_int_float,而在C++中则命名为:method

// 这其实就是C++能支持函数重载,而C却不能的主要原因之一

extern "C" BasePeople* create_BasePeople(const string & name, const int age);

extern "C" void delete_BasePeople(BasePeople * pp);

static const string __CREATE_OBJECT__ = "create_BasePeople";

static const string __DELETE_OBJECT__ = "delete_BasePeople";

class BasePeople{

public:

BasePeople(const string & name, const int age) : name_(name),age_(age){

cout << "BasePeople's constructor invoked" << endl;

}

virtual ~BasePeople(){

cout << "BasePeople's de-constructor invoked" << endl;

}

virtual void speak() = 0;

void test(){

cout << "This is the test method!" << endl;

}

protected:

int age_;

string name_;

};

2)student.cpp,该文件是class BasePeople的继承类,同时,该文件将被生成libstudent.so动态库,同时提供给callPlugin.cpp调用

// student.cpp

#include "base.h"

class student : public BasePeople{

public:

student(const string & name, const int age) : BasePeople(name,age)

{

cout << "student's constructor invoked" << endl;

}

virtual ~student(){

cout << "student's de-constructor invoked" << endl;

}

virtual void speak(){

int i = 0;

while(i < 10){

cout << "I am a student, my name is " << this->name_ << ", and my age is " << this->age_ << endl;

sleep(1);

++ i;

}

}

};

// 动态库文件libstudent.so中必须包含有在base.h中申明的两个extern "C" 方法

BasePeople* create_BasePeople(const string & name, const int age){

//student* sd = new student(name,age);

return new student(name,age);

}

void delete_BasePeople(BasePeople * pp){

if(pp)

delete pp;

}

3)teacher.cpp,该文件和student.cpp一样,只是会生成libteacher.so动态库。

// teacher.cpp

#include "base.h"

class teacher : public BasePeople{

public:

teacher(const string & name, const int age) : BasePeople(name,age)

{

cout << "teacher's constructor invoked" << endl;

}

virtual ~teacher(){

cout << "teacher's de-constructor invoked" << endl;

}

virtual void speak(){

int i = 0;

while(i < 10){

cout << "I am a teacher, my name is " << this->name_ << ", and my age is " << this->age_ << endl;

sleep(1);

++ i;

}

}

};

BasePeople* create_BasePeople(const string & name, const int age){

//teacher* sd = new teacher(name,age);

return new teacher(name,age);

}

void delete_BasePeople(BasePeople * pp){

if(pp)

delete pp;

}

4)callPlugin.cpp调用类的实现

// callPlugin.cpp

#include "base.h"

#include <dlfcn.h>

#include <pthread.h>

class callPlugin{

typedef BasePeople* ObjectPtr;

typedef ObjectPtr (*createObjectPtr)(const string &, const int);

typedef void (*deleteObjectPtr)(ObjectPtr);

public:

callPlugin(const string & soPath, const int openMode = RTLD_LAZY) : soPath_(soPath), openMode_(openMode){

cout << "callPlugin constructor invoked!" << endl;

}

~callPlugin(){

cout << "callPlugin de-constructor invoked!" << endl;

}

int excute(){

void * pluginHandler = openPlugin();

if(pluginHandler){

// 得到create_BasePeople方法的指针

createObjectPtr createFunc = (createObjectPtr) dlsym(pluginHandler,__CREATE_OBJECT__.c_str());

ObjectPtr people = NULL;

if(createFunc){

people = createFunc("luoxiaoyi",23);

people->speak();

people->test();

}else{

cout << "callPlugin::excute() createFunc error :" << dlerror() << endl;

}

// 得到delete_BasePeople方法的指针

deleteObjectPtr deleteFunc = (deleteObjectPtr) dlsym(pluginHandler,__DELETE_OBJECT__.c_str());

if(deleteFunc){

deleteFunc(people);

}else{

cout << "callPlugin::excute() deleteFunc error :" << dlerror() << endl;

}

// 关闭pluginHandler,回收资源

dlclose(pluginHandler);

pluginHandler = NULL;

}else{

cout << "callPlugin::excute() pluginHandler error :" << dlerror() << endl;

}

}

private:

// so动态库的路径,可以是绝对路径,也可以是相对路径

string soPath_;

// so动态库的打开模式RTLD_NOW,RTLD_LAZY

int openMode_;

/**

* 调用dlopen,同时得到相关so文件的句柄

*/

void* openPlugin(){

if(soPath_.size() < 1){

cout << "plugin file[soPath=" << soPath_ << "] cannot be empty!" << endl;

return NULL;

}

if(openMode_ == RTLD_NOW || openMode_ == RTLD_LAZY)

return dlopen(soPath_.c_str(),openMode_);

else

return dlopen(soPath_.c_str(),RTLD_LAZY);

}

};

// 供多线程调用的方法

void* callFun(void *arg){

callPlugin* cp = (callPlugin*) arg;

cp->excute();

}

/*************************************************** main() **********************************************/

int main(){

// 加载动态库

callPlugin cp1("libstudent.so"); // ----------------------------->标记1

callPlugin cp2("libteacher.so"); // ----------------------------->标记2

// 创建新线程,用于调用libteacher.so的执行

pthread_t thread;

pthread_create(&thread,NULL,callFun,&cp2);

// 执行

cp1.excute();

// 等待thread线程执行完毕

pthread_join(thread,NULL);

return 0;

}

5)编译指令

g++ teacher.cpp -fPIC -shared -o /usr/local/cep/libteacher.so

g++ student.cpp -fPIC -shared -o /usr/local/cep/libstudent.so

g++ callPlugin.cpp base.cpp -o callPlugin -ldl -pthread

6)可能出现的问题以及参考解决方案

编译完成之后,如果你此时运行./callPlugin,则可能会报无法找到动态库的错误。解决的方法如下:

a)讲main方法中的标记1和2里面so文件的路径改为绝对路径/usr/local/cep/libstudent.so、/usr/local/cep/libteacher.so,重新编译callPlugin.cpp,然后运行,如果还不行尝试b);

b)如果你有root权限,可以通过修改/etc/ld.so.conf来达到此目的,修改方法为:在/etc/ld.so.conf中加入你的so所在的目录,在这里如:/usr/local/cep,完了之后,再运行ldconfig命令,至于为何要这样,您可以网上搜下/etc/ld.so.conf的作用,如果没有root权限,则可尝试c);

c)没有root权限的情况,可以通过用环境变量LD_LIBRARY_PATH指定路径,但是这里要注意的是,不同的系统可能环境变量不一样,指定方法如:LD_LIBRARY_PATH=.;这样就把LD_LIBRARY_PATH指定为当前目录,然后使用export LD_LIBRARY_PATH命令即可。

通过以上三步,你基本可以解决库文件无法找到的问题了,如果还是无法解决相关问题,那么请网上找找对应的错误咯

注意:添加了-c参数,即g++ -c那么很有可能出现only ET_DYN and ET_EXEC can be loaded错误,解决方法简单,去掉-c即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: