linux类动态库,Linux动态库(一)

起因

博主在以Linux下做开发。在软件需求中,需要动态库带来的灵活性。

比如说博主主导的智能主机的开发。它需要支持很多种类的设备控制,如普通的开关灯、RGB灯、窗帘、百叶窗等等。我们将这些设备抽象成Device类,具体的设备就从这个类上派生出来。每支持一个新设备就派生一个Device的子类进行具体能力的实现。如下:

80794499_1.png

如果我们在代码里将其写死。那么,将来我们每新添一个设备,我们都得更换整个应用程序。升级程序有风险,而且在升级过程中主机是会停止服务的。

那么,能不能在不停止应用程序的情况下直接支持新的设备的呢?我想是能的,用动态库实现的插件思想来做。当主机发现一个种类的设备,比如:门锁。主机之前并不认识,它便会拿着设备的model_id去向服务器请求其“驱动”。而这个驱动,则是该设备从Device派生的类DoorlockDevice的动态库文件 doorlock_device.so。主机将该动态库加载进来,并实例化其DoorlockDevice对象。于是,主机就可以正常控制门锁了。

这样做除了灵活以外,还有另一个优点:通常用户的智能家居系统中只有两三种设备,如果将死在代码里或者静态链接。主机程序在加载的时候会将100多个设备代码加载到内存中运行。这对于内存匮乏的嵌入式系统而言,这是莫大的浪费。如果采用动态库加载方式,主机程序不需要在启动的时候将所有的设备驱动加载到内存运行,而是按需加载。如此可以大大地节省内存开销。

于是,我一定要好好研究一下动态库加载与插件模式。

正题

好了,回归正题。下面是一个非常简单的例子,看看是如何实现的:

文件组织结构:

.

├── main.cpp

├── Makefile

└── plugins

├── device.cpp

├── device.h

├── devices

│   ├── rgb_device.cpp

│   ├── rgb_device.h

│   ├── switch_device.cpp

│   └── switch_device.h

└── Makefile

plugins/device.{h,cpp} 定义了设备的接口:

plugins/device.h

#ifndefDEVICE_H_20160508

#defineDEVICE_H_20160508

class Device {

public:

Device();

virtual ~Device();

public:

virtualvoidwork()= 0;

};

typedefDevice*create_func_t();

#endif//DEVICE_H_20160508

plugins/device.cpp

#include"device.h"

#include

using namespace std;

Device::Device() {

cout <

}

Device::~Device() {

cout <

}

定义的比较简单,就一个方法:work()

plugins/devices/ 目录下为Device的两个派生类:

RGBDevice类

#ifndefRGB_DEVICE_H_20160508

#defineRGB_DEVICE_H_20160508

#include"../device.h"

class RGBDevice: public Device {

public:

virtualvoidwork();

};

#endif//RGB_DEVICE_H_20160508

#include"rgb_device.h"

#include

using namespace std;

extern "C" Device*create(){

return new RGBDevice;

}

void RGBDevice::work() {

cout <

}

SwitchDevice类

#ifndefSWITCH_DEVICE_H_20160508

#defineSWITCH_DEVICE_H_20160508

#include"../device.h"

class SwitchDevice: public Device {

public:

virtualvoidwork();

};

#endif//SWITCH_DEVICE_H_20160508

#include"switch_device.h"

#include

using namespace std;

extern "C" Device*create(){

return new SwitchDevice;

}

void SwitchDevice::work() {

cout <

}

上面两个类的cpp里,我们都定义了一个create() 函数。它是用来创建一个对应的对象的。对于动态库里的对象,我们不能用new来创建。因为在编译main程序时,编译器根本就不知道如果new RGBDevice。

我们也不能提前在编译main程序时包含rgb_device.h(我们那时可能就不知道会有这个一个类)。综上,我们需要一个动态库内部指定的函数来创建。

plugins/Makefile的内容如下:

CFLAGS+=-shared -fPIC

CXXFLAGS+=$(CFLAGS)

all: librgb_device.so libswitch_device.so

librgb_device.so: devices/rgb_device.cpp device.cpp

$(CXX) $(CXXFLAGS) -o $@ $^

libswitch_device.so: devices/switch_device.cpp device.cpp

$(CXX) $(CXXFLAGS) -o $@ $^

clean:

-rm -f lib*.so

博主不太精于写Makefile,只能写成这样了。见笑了~

大致是这样的,每个派生类都要单独编译成一个libxxxx.so文件。如rgb_device,编译命令是:g++ -shared -o librgb_device.so -fPIC device/rgb_device.cpp device.cpp

为什么要加device.cpp ?因为RGBDevice继承于Device,而Device有自己的方法。如果编译时不加device.cpp,那么RGBDevice在链接时,不知道该怎么执行继承于Device的成员函数了。

好了,在plugins/下make一下,正常情况下,两个动态库就生成了。

下面看看main中如何加载动态库:

main.cpp

#include

#include

#include"device.h"

using namespace std;

intmain(){

cout <

void *handle = dlopen("./plugins/libswitch_device.so", RTLD_NOW);

if (handle == NULL) {

cerr <

return 0;

}

create_func_t *func = (create_func_t*)dlsym(handle, "create");

if (func != NULL) {

Device *d = (*func)();

if (d != NULL) {

d->work();

delete d;

} else

cerr <

}

dlclose(handle);

handle = NULL;

cout <

return 0;

}

Makefile

all: test

make -C plugins all

test: main.cpp

$(CXX) -o $@ $^ -I./plugins/ -ldl

clean:

make -C plugins clean

-rm test

注意:在链接main时,一定要添加 -ldl,否则不能支持dlopen, dlsym, dlclose 函数。

make 之后,运行结果:

start

Device construct

SwitchDevice work

Device destruct

done

dlopen("./plugins/libswitch_device.so", RTLD_NOW)

换成:

dlopen("./plugins/librgb_device.so", RTLD_NOW)

后再运行,结果是:

start

Device construct

RGB work

Device destruct

done

可见动态的效果了。如果我们将动态库名写在配置文件里。那么,只要修改一下配置文件,那么运行的效果就不同了。

这只是实现上面我的设想的第一步。后面再研究动态库的插件。

下次见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值