动态共享库(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种库:静态库和共享库.静态库 ...
随机推荐
- escape encodeURI encodeURIComponent区别
escape() 函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串.使用unescape来解码. 有效的URI(统一资源标示符)是不能包含某些字符的,如空格,所以需要进行编码,编码方法有 ...
- hibernate初涉
好久都不曾写写总结一些东西了,惰性真的是令人难以克制!虽然和许多北漂族一样,艰苦而又迷茫,但是我总能找到一些方向,一点期盼,因为你就我的目标.我会坚持下去,重拾青春的热血,既然人生如戏,那我不当猪脚. ...
- ubuntu12.04管理员账户登录不了桌面,只能客人会话登录
ubuntu12.04管理员账户登录不了桌面,只能客人会话登录 求助!!ubuntu12.04管理员账户登录不了桌面,只能客人会话登录. 登录管理员账户时,输入密码后,一直在登录界面循环 费了好大劲啊 ...
- ceph主要数据结构解析3-Ceph_fs.h文件
(1)集群内部子版本协议类型宏定义:与公共协议保持独立性,以便消息类型和协议升级受影响 #define CEPH_OSDC_PROTOCOL 24 /* server/client */OSD服务 ...
- Mysql + keepalived 实现双主热备读写分离【转】
Mysql + keepalived 实现双主热备读写分离 2013年6月16日frankwong发表评论阅读评论 架构图 系统:CentOS6.4_X86_64软件版本:Mysql-5.6.12 ...
- sql为了实现转换的行列
全名 学科 成绩 牛芬 语文 81 牛芬 数学 88 牛芬 英语 84 张三 语文 90 张三 数学 98 张三 英语 90 (表一) 现有一个表如(表一) 姓名 语文 数学 英语 牛芬 81 88 ...
- Linux查看硬件信息以及驱动设备的命令
用硬件检测程序kuduz探测新硬件:service kudzu start ( or restart) 查看CPU信息:cat /proc/cpuinfo 查看板卡信息:cat /proc/pci 查 ...
- Ubuntu14.04 如何修改/etc/sudoers 和错误修改权限的解决办法
开始学习hadoop啦!!! 在Ubuntu14.04上新建了一个名为hadoop的用户,但总是遇到各种权限问题,于是就想干脆把这个账户变成root账户. 网上查到说是直接修改/etc/sudoers ...
- HDU -2524 矩形A + B
找规律题,这种题目比较巧妙,要仔细观察找出规律 1. 假设只有一行,一共有n列,那么由一个小矩形构成的矩形个数为n, 由两个小矩形构成的矩形个数为 n - 1个 .... 由 n 个小矩形构成的矩形个 ...
- 计算方法(一)用C#实现数值迭代
平时,经常会遇到解方程,计算方法中常用的有二分法(精度太低,迭代次数多,一般没人用),牛顿迭代法,弦截法,网上大多都是C++或者Java的实现代码,很少有C#的,我在本科毕业论文中用到了这些,那时也需 ...