动态共享库(so)开发精悍教程

翻译并根据实际情况进行了小小修改,仅关注Linux下动态共享库(Dynamic shared library .so)的开发.

1 简单的so实例

源文件

//test1.c

int test1(){
    return 1;
}

//test2.c

int test2(){
    return2;
}

//mytest.c

#include <stdio.h>
int test1();
int test2();
int main(){
    printf("result of test1:= %d\n",test1());
    printf("result of test2:= %d\n",test2());
}

打包成so文件

在代码的目录下运行如下命令:

gcc -Wall -fPIC -c *.c
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指定路径

运行

sudo ldconfig -n /opt/lib

/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头文件如下:

#ifndef 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 <stdio.h>
#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等参数):

gcc -Wall prog.c -lctest -o prog -ldl
./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 <dlfcn.h>
#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;
}

然后用如下命令运行:

g++ -Wall prog2.cpp -lctest -o prog2 -ldl
./prog2

编译命令简介

假设C文件是prog.c, C++调用文件是prog2.cpp,那么编译脚本分别是:

C语言:

 gcc -Wall -I/path/to/include-files -L/path/to/libraries prog.c -lctest -o prog

C++语言:

 g++ -Wall -I/path/to/include-files -L/path/to/libraries prog2.cpp -lctest -ldl -o prog2

参数详解:

  • -I: 指定头文件目录.
  • -L: 指定库目录.
  • -lctest: 调用动态库libctest.so.1.0. 如果在打包so时没有创建第一个符号链接,那么这个参数会导致编译不成功.
  • -ldl: C++编译必须

相关知识

命令ldd appname 可以查看应用程序所依赖的动态库,运行如下命令:

ldd prog

在我的机器输出:

    linux-gate.so.1 =>  (0xb80d4000)
    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

#ifndef __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 "myclass.h"
#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 <dlfcn.h>
#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++ -fPIC -shared myclass.cpp -o myclass.so
 g++ class_user.cpp -ldl -o class_user
 ./class_user

动态共享库(so)开发精悍教程的更多相关文章

  1. 【转载】Linux下动态共享库加载时的搜索路径详解

    转载自:http://www.eefocus.com/article/09-04/71617s.html 对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while loading ...

  2. Linux下动态共享库加载及使用详解【转】

    原文地址:http://blog.chinaunix.net/uid-29025972-id-3855500.html 对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while l ...

  3. Linux动态共享库

    Linux操作系统上面的动态共享库大致分为三类:   一.操作系统级别的共享库和基础的系统工具库 libc.so, libz.so, libpthread.so等等,这些系统库会被放在/lib和/us ...

  4. Linux下动态共享库加载时的搜索路径详解

    对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while loading shared libraries”这样的错误,这是典型的因为需要的动态库不在动态链接器ld.so的搜索路径 ...

  5. Linux下动态共享库加载及使用详解

    转载;http://blog.chinaunix.net/uid-29025972-id-3855500.html 对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while loa ...

  6. <摘录>Linux下动态共享库加载时的搜索路径详解

    对动态库的实际应用还不太熟悉的读者可能曾经遇到过类似“error while loading shared libraries”这样的错误,这是典型的因为需要的动态库不在动态链接器ld.so的搜索路径 ...

  7. 72)MFC测试动态共享库

    动态共享库: 首先我建立一个新的动态库: 然后不选择空项目了,因为我们普通的cpp文件 入口是main  win32入口是winmain  那么这个动态库的入口在哪里  我们就是为了看一看: 出来这样 ...

  8. python调用c++/c 共享库,开发板上编译的一些坑!

    1.对于python,ctypes只能load动态库,但现在我的对象是一个静态库,而且我没有源代码,静态库在编译过程中没有加--fPIC参数,所以我也没办法将其编译为动态库,有没有什么方法在pytho ...

  9. 第二课 GCC入门之静态库以及共享库

    序言: 前面一课讲了gcc的简单入门,包括gcc编译步骤:预处理:编译:汇编:链接.今天这节课就来讲下linux的库也欢迎大家吐糟共同学习. 原理: linux系统中分为2种库:静态库和共享库.静态库 ...

随机推荐

  1. finally与return

    finally关键字:和try块使用,一般做资源释放操作,比如关闭流.关闭数据库连接,释放锁. return:用于返回值. finally块可保证一定执行,当逻辑处理有返回值时,会首先执行finall ...

  2. wxPython学习笔记(一)

    创建最小的空的wxPython程序 frame = wx.Frame(parent=None, title='Bare') frame.Show() return True app = App() a ...

  3. Android Toast封装

    package com.whoop.mobile.trace.util; import android.content.Context; import android.content.res.Reso ...

  4. URL具体解释

    浏览器因特网资源:URL是浏览器寻找信息时所需的资源位置.通过URL.应用程序才干找到并使用共享因特网上大量的数据资源. 大部分URL都遵循一种标准的格式: ①HTTP协议(http://或者http ...

  5. SQL优化(Oracle)

    (转)SQL优化原则 一.问题的提出 在应用系统开发初期.因为开发数据库数据比較少.对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,可是假设将应用系统提交实际应用后,随着数 ...

  6. Laravel No such file or directory in /bootstrap/autoload.php on line 17

    具体错误如下: Warning: require(../vendor/autoload.php) [function.require]: failed to open stream: No such ...

  7. python复制--笔记

    对象引用: >>> songs = ["Bee","Core","Love"] >>> bat = so ...

  8. Android Activity间传值

    Android中不同的Activity之间的传值方式(Main为当前Activity,Login为目标Activity) 1.使用Intent Intent intent = new Intent(M ...

  9. php调试mysql信息。

    print_r(mysql_error());会返回执行myql的成功或者失败的信息.数据库的编码方式是UTF-8.获取手机号,返回的页面的编码是gb2312.需要转换

  10. 解决ajax请求cors跨域问题

    ”已阻止跨源请求:同源策略禁止读取位于 ***** 的远程资源.(原因:CORS 头缺少 'Access-Control-Allow-Origin').“ ”已阻止跨源请求:同源策略禁止读取位于 ** ...