动态共享库(so)开发精悍教程
动态共享库(so)开发精悍教程
翻译并根据实际情况进行了小小修改,仅关注Linux下动态共享库(Dynamic shared library .so)的开发.
1 简单的so实例
源文件
//test1.c
return 1;
}
//test2.c
return2;
}
//mytest.c
int test1();
int test2();
int main(){
printf("result of test1:= %d\n",test1());
printf("result of test2:= %d\n",test2());
}
打包成so文件
在代码的目录下运行如下命令:
gcc -shared -Wl,-soname,libctest.so.1 -o libctest.so.1.0 *.o
sudo mv libctest.so.1.0 /usr/lib
sudo ln -sf /usr/lib/libctest.so.1.0 /usr/lib/libctest.so
sudo ln -sf /usr/lib/libctest.so.1.0 /usr/lib/libctest.so.1
参数详解:
- -Wall: 包含warning信息
- -fPIC: 编译动态库必须,输出不依赖位置的代码(原文 :Compiler directive to output position independent code)
- -shared: 编译动态库必需选项
- -W1: 向链接器(Linker)传递一些参数.在这里传递的参数有
"-soname libctest.so.1" - -o: 动态库的名字. 在这个例子里最终生成动态库 libctest.so.1.0
两个符号链接的含义:
- 第一个:允许应用代码用 -lctest 的语法进行编译.
- 第二个:允许应用程序在运行时调用动态库.
2 so路径设置
为了使应用程序能够在运行时加载动态库,可以通过3种方式指定动态库的路径(以下例子均假定/opt/lib是动态库所在位置):
用ldconfig指定路径
运行
/opt/lib 是动态库所在路径. 这种方式简单快捷,便于程序员开发.缺点是重启后即失效.
修改/etc/ld.so.conf文件
打开/etc/ld.so.confg 文件,并将/opt/lib 添加进去.
(注: 在Ubuntu系统中, 所有so.conf文件都在/etc/ld.so.conf.d目录. 你可以仿照该目录下的.conf文件写一个libctest.conf并将/opt/lib填入)
用环境变量LD_LIBRARY_PATH指定路径
环境变量的名字一般是LD_LIBRARY_PATH, 但是不同的系统可能有不同名字. 例如
Linux/Solaris: LD_LIBRARY_PATH, SGI: LD_LIBRARYN32_PATH, AIX: LIBPATH, Mac OS X: DYLD_LIBRARY_PATH, HP-UX: SHLIB_PATH) (注: 此说法未经验证)
修改~/.bashrc , 增加以下脚本:

if [ -d /opt/lib ];
then
LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH
fi

export LD_LIBRARY_PATH
在第一章的简单例子中, /usr/lib 是Ubuntu默认的动态库目录,所以我们不须指定动态库目录也能运行应用程序.
3 简单的动态调用so例子
C调用例子
保留第一章的test1.c和test2.c文件,并增加ctest.h头文件如下:
#define CTEST_H
#ifdef __cplusplus
extern "C" {
#endif
int test1();
int test2();
#ifdef __cplusplus
}
#endif
#endif
我们继续使用第一章生成的libctest.so,仅需增加一个新的应用程序 prog.c:
//prog.c
#include <dlfcn.h>
#include "ctest.h"
int main(int argc, char **argv)
{
void *lib_handle;
int (*fn)();
char *error;
lib_handle = dlopen("libctest.so", RTLD_LAZY);
if (!lib_handle)
{
fprintf(stderr, "%s\n", dlerror());
return 1;
}
fn = dlsym(lib_handle, "test1");
if ((error = dlerror()) != NULL)
{
fprintf(stderr, "%s\n", error);
return 1;
}
int y=fn();
printf("y=%d\n",y);
dlclose(lib_handle);
return 0;
}
然后用如下命令运行(由于没有使用其他库,所以忽略-L等参数):
./progdl
方法简介
dlopen("libctest.so", RTLD_LAZY): 加载动态库,如果加载失败返回NULL. 第二个参数可以是:
- RTLD_LAZY: lazy模式. 直到源码运行到改行才尝试加载.
- RTLD_NOW: 马上加载.
- RTLD_GLOBAL: 不解(原文: Make symbol libraries visible.)
dlsym(lib_handle, "test1"): 返回函数地址. 如果查找函数失败则返回NULL.
和微软的动态加载dll技术对比如下:
- ::LoadLibrary() - dlopen()
- ::GetProcAddress() - dlsym()
- ::FreeLibrary() - dlclose()
C++调用例子
增加一个prog2.cpp
#include <iostream>
#include "ctest.h"
using namespace std;
int main(){
void *lib_handle;
//MyClass* (*create)();
//ReturnType (* func_name)();
int (* func_handle)();
string nameOfLibToLoad("libctest.so");
lib_handle = dlopen(nameOfLibToLoad.c_str(), RTLD_LAZY);
if (!lib_handle) {
cerr << "Cannot load library: " << dlerror() << endl;
}
// reset errors
dlerror();
// load the symbols (handle to function "test")
//create = (MyClass* (*)())dlsym(handle, "create_object");
//destroy = (void (*)(MyClass*))dlsym(handle, "destroy_object");
func_handle =(int(*)())dlsym(lib_handle, "test1");
const char* dlsym_error = dlerror();
if (dlsym_error) {
cerr << "Cannot load symbol test1: " << dlsym_error << endl;
}
cout<<"result:= "<<func_handle()<<endl;
dlclose(lib_handle);
return 0;
}
然后用如下命令运行:
./prog2
编译命令简介
假设C文件是prog.c, C++调用文件是prog2.cpp,那么编译脚本分别是:
C语言:
C++语言:
参数详解:
- -I: 指定头文件目录.
- -L: 指定库目录.
- -lctest: 调用动态库libctest.so.1.0. 如果在打包so时没有创建第一个符号链接,那么这个参数会导致编译不成功.
- -ldl: C++编译必须
相关知识
命令ldd appname 可以查看应用程序所依赖的动态库,运行如下命令:
在我的机器输出:
libctest.so.1 => /usr/lib/libctest.so.1 (0xb80be000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7f5b000)
/lib/ld-linux.so.2 (0xb80d5000)
4 C++开发带class的so
//myclass.h
#define __MYCLASS_H__
class MyClass
{
public:
MyClass();
/* use virtual otherwise linker will try to perform static linkage */
virtual void DoSomething();
private:
int x;
};
#endif
//myclass.cpp
#include <iostream>
using namespace std;
extern "C" MyClass* create_object()
{
return new MyClass;
}
extern "C" void destroy_object( MyClass* object )
{
delete object;
}
MyClass::MyClass()
{
x = 20;
}
void MyClass::DoSomething()
{
cout<<x<<endl;
}
//class_user.cpp
#include <iostream>
#include "myclass.h"
using namespace std;
int main(int argc, char **argv)
{
/* on Linux, use "./myclass.so" */
void* handle = dlopen("./myclass.so", RTLD_LAZY);
MyClass* (*create)();
void (*destroy)(MyClass*);
create = (MyClass* (*)())dlsym(handle, "create_object");
destroy = (void (*)(MyClass*))dlsym(handle, "destroy_object");
MyClass* myClass = (MyClass*)create();
myClass->DoSomething();
destroy( myClass );
}
编译和运行:
g++ class_user.cpp -ldl -o class_user
./class_user
动态共享库(so)开发精悍教程的更多相关文章
- 【转载】Linux下动态共享库加载时的搜索路径详解
转载自:http://www.eefocus.com/article/09-04/71617s.html 对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while loading ...
- Linux下动态共享库加载及使用详解【转】
原文地址:http://blog.chinaunix.net/uid-29025972-id-3855500.html 对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while l ...
- Linux动态共享库
Linux操作系统上面的动态共享库大致分为三类: 一.操作系统级别的共享库和基础的系统工具库 libc.so, libz.so, libpthread.so等等,这些系统库会被放在/lib和/us ...
- Linux下动态共享库加载时的搜索路径详解
对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while loading shared libraries”这样的错误,这是典型的因为需要的动态库不在动态链接器ld.so的搜索路径 ...
- Linux下动态共享库加载及使用详解
转载;http://blog.chinaunix.net/uid-29025972-id-3855500.html 对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while loa ...
- <摘录>Linux下动态共享库加载时的搜索路径详解
对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while loading shared libraries”这样的错误,这是典型的因为需要的动态库不在动态链接器ld.so的搜索路径 ...
- 72)MFC测试动态共享库
动态共享库: 首先我建立一个新的动态库: 然后不选择空项目了,因为我们普通的cpp文件 入口是main win32入口是winmain 那么这个动态库的入口在哪里 我们就是为了看一看: 出来这样 ...
- python调用c++/c 共享库,开发板上编译的一些坑!
1.对于python,ctypes只能load动态库,但现在我的对象是一个静态库,而且我没有源代码,静态库在编译过程中没有加--fPIC参数,所以我也没办法将其编译为动态库,有没有什么方法在pytho ...
- 第二课 GCC入门之静态库以及共享库
序言: 前面一课讲了gcc的简单入门,包括gcc编译步骤:预处理:编译:汇编:链接.今天这节课就来讲下linux的库也欢迎大家吐糟共同学习. 原理: linux系统中分为2种库:静态库和共享库.静态库 ...
随机推荐
- 管道(Pipe)/createPipe
BOOL CreatePipe(PHANDLE hReadPipe, // 指向读句柄的指针 PHANDLE hWritePipe, // 指向写句柄的指针 LPSECURITY_ATTRIBUTES ...
- 高性能MySql进化论(一):数据类型的优化_上
在数据库的性能调优的过程中会涉及到很多的知识,包括字段的属性设置是否合适,索引的建立是否恰当,表结构涉及是否合理,数据库/操作系统 的设置是否正确…..其中每个topic可能都是一个领域. 在我看来, ...
- Cocos2d-x 3.x 头像选择,本地相册图片+图片编辑(Android、IOS双平台)
大连游戏产业不是很发达,最后,选择一个应用程序外包公司.积累的工作和学习过程中的一点业余生活微信体验,我想分享的游戏小朋友的爱. 在应用开发过程中会经常实用户上传头像的功能,在网上找了N多资料发现没有 ...
- ANDROID内存优化(大汇总——上)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...
- SpringMVC04controller中定义多个方法
public class MyController extends MultiActionController { // 新增 方法修饰符要是public public ModelAndView ad ...
- codevs 2241 排序二叉树
/* WTF 写了好久了 开始的时候题目读错了 建图建错了 搜索写的也不好 感觉会T 总之 第一次写的很low 贴一下吧 */ #include<iostream> #include< ...
- web第一节课 sql 数据库连接 查询
1.数据库连接语句 <connectionStrings> <add name="yhotel" connectionString="Database= ...
- hdu 2200
bc上的题目,很水,有很多方法做吧,题意大概就是给定你票数,然后让你求出票数最多的那个下标...... 之前我用两个for循环分开写,一个是读入,然后是判断,提交就wa,后来网上看了别人的,就是不能分 ...
- setTimeout()与setInterval()方法区别介绍
计时器setTimeout()和setInterval()两个都是js的计时功能的函数两个有些区别,下面为大家简单介绍下,希望对大家有所帮助 计时器setTimeout()和setInterval ...
- 类名.this与类名.class
1..当在内部类中使用this指的就是内部类的对象, 为了访问外层类对象,就可以使用外层类名.this来访问. 2.在java中,每个class都有一个相应的Class对象,当编写好一个类,编译完成后 ...