linux动态库接口调用

2016-01-20 13:58:23 u011191259 阅读数 638

这是一个小知识点,今天拿出来说说。我们有一个应用test,它调用了一个动态库so1.so,而这个so1.so又调用了so2.so。

so1.so的编译命令行gcc -O so2.so -lso1 -lrt -lstdc++

test的编译命令行gcc -O test -lso1 -lrt -lstdc++会报错,说是在so1.so中找不到一些接口定义,而这些接口定义是在so2.so中实现的。

正确的编译命令行是gcc -O test -lso1 -lso2 -lrt -lstdc++,也就是说虽然so2只是被test间接调用,但链接器会顺着藤一直摸下去,如果摸到哪根藤下面没有瓜,就报错。

2018-07-15 11:52:10 lq_fly_pig 阅读数 2208

主要是为了平时的学习记录,不妥的地方,烦请指点。

一.下面主要是dlopen打开动态库.so相关的API接口函数。

1. void* dlopen(const char* filename,int flag);

filename 是动态库的path路径,flag是动态库加载的几种方法。

RTLD_LAZY:dlopen返回前,对动态库中存在的未定义的变量不进行解析,只对函数符号有效,变量符号会被立即加载。

RTLD_NOW: 需要在dlopen返回前,解析出所有未定义符号,如果解析不出来,dlopen时会返回NULL,提示error信息: undefined symbol: xxxx.......

RTLD_GLOBAL:这个库中的符号可以被随后的动态库使用。

2. char* dlerror(void)

 返回相关的错误,如果从初始化到最后结束都没有错误,则返回NULL。

3. void * dlsym(void* handle,const char* symbol);

返回符号为symbol的函数的地址,需要用到dlopen中返回的handle,需要使用dlerror来判断是否是错误,

二.下面举个简单的例子说明下。

定义一个基类CTestDL,test()和print() 都是纯虚函数。C++支持了命名空间、类以及重载等机制,所以符号的命名规则和C不同。而dl API属于C的部分,使用的C的命名规则,所以在实现上述两个函数时(也就是说需要通过动态链接的C++函数)需要使用C的符号命名规则。这需要实现extern 'C'实现。

#ifndef __CTEST_DL_H__
#define __CTEST_DL_H__

class CTestDL
{
	public:
		CTestDL(int a,int b)
		{
			length =a;
			width =b;
		}
	    virtual ~ CTestDL()
		{
		}
	public:
		virtual void test(const char*) =0;
		virtual void print()=0; 
	protected:
		int length;
		int width;
};

extern "C" CTestDL* GetClass(int,int);  //导出函数名

#endif

下面是一个派生类Test,继承上面的基类

#include <iostream>

#include "ctest.h"

using namespace std;

class Test :public CTestDL
{
	public:
		Test(int a,int b):CTestDL(a,b) //虚基类已经赋值了
		{
			length = a;
			width = b;
		}
		~Test(){}
	public:
		void test(const char*);
		void print();
	private:
		int length ;
		int width;
};

Test类的cpp文件:

#include <iostream>
#include <stdio.h>

#include "test.h"
using namespace std;

CTestDL* GetClass(int a,int b)
{
	return new Test(a,b);
}
void Test::test(const char* data)
{
	cout << "--this is a test program !--" <<"**: "<< data << endl;
}

void Test::print()
{
  int area = length * width;
  cout << "**** the area is **** :" << area << endl;
}

编译的命令: g++ -shared -lc -s -o libtdlc.so test.cpp -fPIC    生成动态库libtdlc.so

测试程序:

#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <iostream>
#include "ctest.h"
using namespace std;


typedef CTestDL* (*pTestDLObj)(int,int);  //定义一个函数指针

int main(int argc,char* argv[])
{
	//CTestDL *(*pTestDLObj)(int,int);
	dlerror();
	
	void *lib_dl = dlopen("./libtdlc.so",RTLD_LAZY);
    if(NULL == lib_dl)
    {
        printf("load library libtdlc.so error.\nErrmsg:%s\n",dlerror());
        return -1;
    }
    //pTestDLObj = (CTestDL * (*)(int,int))dlsym(lib_dl,"GetClass");
	pTestDLObj create_class = (pTestDLObj)dlsym(lib_dl,"GetClass");
	
    const char *dlmsg = dlerror();
    if(NULL != dlmsg)
    {
        printf("get class testdl error\nErrmsg:%s\n",dlmsg);
        dlclose(lib_dl);
        return -1;
    }
 
    //CTestDL *pTestdl = pTestDLObj(2,3);
	//CTestDL *pTestdl = (*pTestDLObj)();
	CTestDL* tmp = create_class(3,3);
	tmp->print();
	tmp->test("success full !");
    //pTestdl->print();
    //pTestdl->test("success full ");
 
    delete tmp;
    dlclose(lib_dl);
    return 0;
}

编译: g++ main.cpp -rdynamic -ldl -s -o dl_main

程序执行的结果:


2015-02-04 17:25:31 lc_910927 阅读数 20553

进入主题前,先看看两点预备知识。

一、显式调用和隐式调用的区别

        我们知道,动态库相比静态库的区别是:静态库是编译时就加载到可执行文件中的,而动态库是在程序运行时完成加载的,所以使用动态库的程序的体积要比使用静态库程序的体积小,并且使用动态库的程序在运行时必须依赖所使用的动态库文件(.so文件),而使用静态库的程序一旦编译好,就不再需要依赖的静态库文件了(.a文件)。

        动态库的调用又分为显示和隐式两种方式,区别如下:

        1、 隐式调用需要调用者写的代码量少,调用起来和使用当前项目下的函数一样直接;而显调用则要求程序员在调用时,指明要加载的动态库的名称和要调用的函数名称。

        2、隐式调用由系统加载完成,对程序员透明;显调用由程序员在需要使用时自己加载,不再使用时,自己负责卸载。

        3、由于显调用由程序员负责加载和卸载,好比动态申请内存空间,需要时就申请,不用时立即释放,因此显调用对内存的使用更加合理, 大型项目中应使用显示调用。

        4、当动态链接库中只提供函数接口,而该函数没有封装到类里面时,如果使用显调用的方式,调用方甚至不许要包含动态链接库的头文件(需要调用的函数名是通过dlsym函数的参数指明的),而使用隐式调用时,则调用方不可避免要加上动态库中的头文件,g++编译时还需要要用参数-I指明包含的头文件的位置。需要注意的是,动态链接库中的接口函数是作为成员函数封装在里面时,即使使用显调用的方式,调用方也必须包含动态库中的相应头文件(详见五、显示调用动态链接中的类成员函数)。

        5、显调用更加灵活,可以模拟多态效果(具体见后文)。


二、extern "C"的作用

        C++程序(或库、目标文件)中,所有非静态(non-static)函数在二进制文件中都是以“符号(symbol)”形式出现的。这些符号都是唯一的字符串,从而把各个函数在程序、库、目标文件中区分开来。在C中,符号名正是函数名,两者完全一样。而C++允许重载(不同的函数有相同的名字但不同的参数,甚至const重载),并且有很多C所没有的特性──比如类、成员函数、异常说明──几乎不可能直接用函数名作符号名。为了解决这个问题,C++采用了所谓的name mangling。它把函数名和一些信息(如参数数量和大小)杂糅在一起,改造成奇形怪状,只有编译器才懂的符号名。例如,被mangle后的foo可能看起来像foo@4%6^,或者,符号名里头甚至不包括“foo”。

        其中一个问题是,C++标准并没有定义名字必须如何被mangle,所以每个编译器都按自己的方式来进行name mangling。有些编译器甚至在不同版本间更换mangling算法(尤其是g++ 2.x和3.x)。前文说过,在显示调用动态库中的函数时,需要指明调用的函数名,即使您搞清楚了您的编译器到底怎么进行mangling的,从而知道调用的函数名被C++编译器转换为了什么形式,,但可能仅仅限于您手头的这个编译器而已,而无法在下一版编译器下工作。

extern "C"即可以解决这个问题。用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。因此,只有非成员函数才能被声明为extern "C",并且不能被重载。尽管限制多多,extern "C"函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了,相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。所以extern "C" 只是告诉编译器编和链接的时候都用c的方式的函数名字,函数里的内容可以为c的代码也可以为c++的。


       有了上面两个预备知识后,下面以实际例子来演示两种不同的动态库调用方式。例子的结构组织为如下:

    so1.h和so1.cc是第一个动态库中的文件,会编译链接为libso1.so;so2.h和so2.cc是第一个动态库中的文件,会编译链接为libso2.so;test.cc是调用两个动态库的程序。


三、调用

so1.h:

extern "C" void fcn();
so1.cc:

#include <iostream>
#include "so1.h"

void fcn() {
	std::cout << "this is fcn in so1" << std::endl;
}

so1的makefile:

libso1.so:so1.o
    g++ so1.o -shared -o libso1.so
so1.o:so1.cc so1.h
    g++ -c so1.cc -fPIC -o so1.o

.PHONY:clean
clean:
    rm so1.o libso1.so
make之后,将生成的libso1.so拷贝到test.cc所在目录下。


so2.h:

extern "C" void fcn();
so2.cc:
#include <iostream>
#include "so2.h"

void fcn() {
	std::cout << "this is fcn in so2" << std::endl;
}
so2的makefile:

libso2.so:so2.o
	g++ so2.o -shared -o libso2.so
so2.o:so2.cc so2.h
	g++ -c so2.cc -fPIC -o so2.o

.PHONY:clean
clean:
	rm so2.o libso2.so
make之后,将生成的libso2.so拷贝到test.cc所在目录下。


test.cc:

#include <iostream>
#include <cstdlib>
#include <dlfcn.h>

using namespace std;

int main(int argc, char **argv) {
	if(argc != 2) {
		cout << "argument error!" << endl;
		exit(1);
	}

	//pointer to function
	typedef void (*pf_t)();
	
	char *err = NULL;
	//open the lib
	void *handle = dlopen(argv[1], RTLD_NOW);
	
	if(!handle) {
		cout << "load " << argv[1] << "failed! " << dlerror() << endl;
		exit(1);
	}

	//clear error info
	dlerror();

	pf_t pf  = (pf_t)dlsym(handle, "fcn");
	err = dlerror();
	if(err) {
		cout << "can't find symbol fcn! " << err << endl;
		exit(1);
	}

	//call function by pointer
	pf();

	dlclose(handle);

	return 0;
}
test的makefile:

test:test.o
	g++ test.o -lso1 -L. -lso2 -L. -ldl -Wl,-rpath=. -o test
test.o:test.cc 
	g++ -c test.cc -o test.o

make之后,终端运行结果如下:

可以看到这里,通过输入不同的参数,调用了不同的共享库中的fcn函数,是一种多态的表现,许多软件的不同插件就是这样实现的。

需要注意的是,要使用显调用的方式,必须加入头文件dlfcn.h,makefile中的链接命令中要加入参数-ldl,否则报错。

dlfcn.h中提供的API说明如下:

1)        dlopen

函数原型:void *dlopen(const char *libname,int flag);

功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。

参数中的libname一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:

a.根据环境变量LD_LIBRARY_PATH查找

b.根据/etc/ld.so.cache查找

c.查找依次在/lib和/usr/lib目录查找。

flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。

2)        dlerror

函数原型:char *dlerror(void);

功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。

3)        dlsym

函数原型:void *dlsym(void *handle,const char *symbol);

功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,

4)        dlclose

函数原型:int dlclose(void *);

功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。


四、隐式调用

隐式调用不需要包含头文件dlfcn.h,只需要包含动态链接库中的头文件,使用动态库中的函数也不需要像显示调用那么复杂。


五、显式调用动态链接中的类成员函数

显示调用动态链接库的类成员函数,有单独的写法,但比较少用。推荐的写法是为每个要被外部调用的类成员函数设计一个普通的借口函数,在接口函数内部使用类的成员函数。当然这就需要将类设计为单例模式,因为不可能在每个接口函数中都构造一个类的对象。


2019-04-22 11:25:12 ganbelieve 阅读数 0

Linux 下生成动态链接库并调用(.so)项目实例

[查看Windows下生成动态库调用详解]
基于OpenCV图像处理项目生成动态链接库并进行.so的调用,首先生成动态库是基于OpenCV开发环境进行,而调用部分通过服务器远程连接进行测试调用。

Linux下和Windows下生成动态库基本类似,手动创建目录用于存放所有的项目文件,并在本目录下进行所有的操作

hello.h文件

#ifndef _HELLO_H
#define _HELLO_H

extern "C"{
        int opencv_main(char str[]);
}
#endif

这里头文件中也需要添加extern “C”,具体查看Windows下讲解

hello.cpp文件

#include "hello.h"

#include<opencv2/opencv.hpp>   //加上函数实现需要的头文件
#include<iostream>
#include<string.h>

using namespace std;
using namespace cv;

extern "C"
int opencv_main(char str[])
{
        cout << "str=" << str << endl;
        Mat Image = imread(str);
        imshow("Image", Image);
        waitKey(0);
        return 0;
}

到现在就可以编译生成.so文件了

g++ -shared -fPIC -o libhello.so hello.cpp

运行这行命令行生成libhello.so文件

注意,-shared参数和-fPIC参数非常重要:
-shared 告诉gcc要生成的是动态链接库;
-fPIC 告诉gcc生成的生成的代码是非位置依赖的,方面的用于动态链接。

最后进行测试
main.cpp

#include "hello.h"
#include<iostream>
#include<string>

int main()
{
        char str[]="/opencv/nine.png";
        opencv_main(str);
        return 0;
}

这是在本地环境下(带有OpenCV运行环境),程序的图片路径需要自己进行设置

g++ -o main main.c -L. -lhello

运行可生成可执行文件main

-L.: 在当前路径下寻找.so文件
-lhello: 要链接这个libhello.so文件

如果出现如下问题
在这里插入图片描述
表示没有链接到本地OpenCV的环境,命令行后面加上下面命令

`pkg-config --cflags --libs opencv`

注:命令的引号不是普通的引号,是ESC下面的按键

最后文件夹下的文件如下所示
在这里插入图片描述
运行main会弹出一张图片表示成功!!!

Gtk-WARNING **: cannot open display

如果运行不成功,报上面的错误表示你的系统没有安装图形显示界面
安装过程提供链接参考:图形界面的安装

本地环境测试OK!

可实际的项目生成的.so文件不可能在任何的服务器下安装本地需要的环境吧,所有调用.so文件也是处于一个无OpenCV环境的情况下进行调用。
从而在服务器上进行测试,具体步骤和上面雷同,这里就不在过多累述!

和上面不同的点本地环境是链接下载安装的环境,而服务器是不依赖与环境,但依赖各个OpenCV的.so库,所有需要将程序所需要的依赖库copy进服务器。

继而进行.so库的链接将so库加载进入共享文件中,设置方式进入/etc/ld.so.conf.d/目录下添加**.conf**文件,里面添加.so路径。

最后运行ldconfig

在这里插入图片描述
出现这种情况,表示系统链接不到这些库文件,但可以连接*.so.2.4.5文件,故把所有的*.so.2.4.5文件留下再试一次OK!
在这里插入图片描述
接着运行测试文件如下
在这里插入图片描述
这里表示不能识别*.so文件需要进行软链接才能辨认识别出,进入.so存放的文件夹运行命令

**sudo ln -s .so.3 .so 产生软链接

最后运行结果基本没有什么多大问题了,可以任服务器调用了。

欢迎关注微信公众号–木木夕算法笔记,与博主交流!

2016-06-15 18:18:09 zzd121333 阅读数 0

 关于动态调用动态库方法说明 
一、  动态库概述 
1、  动态库的概念 
日常编程中,常有一些函数不需要进行编译或者可以在多个文件中使用(如数据库输入/输出操作或屏幕控制等标准任务函数)。可以事先对这些函数进行编译,然后将它们放置在一些特殊的目标代码文件中,这些目标代码文件就称为库。库文件中的函数可以通过连接程序与应用程序进行链接,这样就不必在每次开发程序时都对这些通用的函数进行编译了。

       动态库是一种在已经编译完毕的程序开始启动运行时,才被加载来调用其中函数的库。其加载方式与静态库截然不同。

2、  动态库的命名 
Linux下,动态库通常以.so(share object)结尾。(通常/lib和/usr/lib等目录下存在大量系统提供的以.so结尾的动态库文件)

Windows下,动态库常以.dll结尾。(通常C:\windows\System32等目录下存在大量系统提供的以.dll结尾的动态库文件)

3、  动态库与静态库之间的区别 
静态库是指编译连接时,把库文件的代码全部加入到可执行文件中,所以生成的文件较大,但运行时,就不再需要库文件了。即,程序与静态库编译链接后,即使删除静态库文件,程序也可正常执行。

动态库正好相反,在编译链接时,没有把库文件的代码加入到可执行文件中,所以生成的文件较小,但运行时,仍需要加载库文件。即,程序只在执行启动时才加载动态库,如果删除动态库文件,程序将会因为无法读取动态库而产生异常。

二、        Linux下动态调用动态库 
备注:以下linux实例说明都是在RedHat 5.1系统+ gcc 版本 4.1.2 20080704 (Red Hat 4.1.2-46)上实现。

1、  .so动态库的生成 
可使用gcc或者g++编译器生成动态库文件(此处以g++编译器为例)

g++ -shared -fPIC -c XXX.cpp

g++ -shared -fPIC -o XXX.so XXX.o

2、  .so动态库的动态调用接口函数说明 
动态库的调用关系可以在需要调用动态库的程序编译时,通过g++的-L和-l命令来指定。例如:程序test启动时需要加载目录/root/src/lib中的libtest_so1.so动态库,编译命令可照如下编写执行:

g++ -g -o test test.cpp –L/root/src/lib –ltest_so1

(此处,我们重点讲解动态库的动态调用的方法,关于静态的通过g++编译命令调用的方式不作详细讲解,具体相关内容可上网查询)

 

Linux下,提供专门的一组API用于完成打开动态库,查找符号,处理出错,关闭动态库等功能。

下面对这些接口函数逐一介绍(调用这些接口时,需引用头文件#include <dlfcn.h>):

1)        dlopen

函数原型:void *dlopen(const char *libname,int flag);

功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。

参数中的libname一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:

a.根据环境变量LD_LIBRARY_PATH查找

b.根据/etc/ld.so.cache查找

c.查找依次在/lib和/usr/lib目录查找。

flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。

2)        dlerror

函数原型:char *dlerror(void);

功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。

3)        dlsym

函数原型:void *dlsym(void *handle,const char *symbol);

功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,

4)        dlclose

函数原型:int dlclose(void *);

功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。


二、动态加载类

加载类有点困难,因为我们需要类的一个实例,而不仅仅是一个函数指针。我们无法通过new来创建类的实例,因为类是在动态库中定义的而不是在可执行程序中定义的,况且有时候我们连动态库中具体的类的名字都不知道。

解决方案是:利用多态性!我们在可执行文件中定义一个带虚成员函数的接口基类,而在模块中定义派生实现类。通常来说,接口类是抽象的(如果一个类含有虚函数,那它就是抽象的)。因为动态加载类往往用于实现插件,这意味着必须提供一个清晰定义的接口──我们将定义一个接口类和派生实现类。

接下来,在模块中,我们会定义两个附加的类工厂函数(class factory functions)(或称对象工厂函数)。其中一个函数创建一个类实例,并返回其指针;另一个函数则用以销毁该指针。这两个函数都以extern "C"来限定修饰。

实例如下:

       testBase.h中定义一个含有纯虚函数virtual void display() const = 0的基类。

       test1.cpp中定义继承类test1,并实现虚函数virtual void display() const的定义,并实现一个创建类函数和一个销毁类指针函数。

       test2.cpp中定义继承类test2,并实现虚函数virtual void display() const的定义,并实现一个创建类函数和一个销毁类指针函数。

       main.cpp中实现动态的调用不同库中的display()方法。

testBase.h

#ifndef __TESTBASE_H__
#define __TESTBASE_H__

class testBase
{
public:
        testBase(){}
        virtual ~testBase(){}
        virtual void display() = 0;
};

typedef testBase* create_t();
typedef void destroy_t(testBase *p);
#endif

test1.cpp

</pre><pre name="code" class="cpp">#include<stdio.h>
#include "testBase.h"

class test1:public testBase
{
public:
        void display()
        {
                printf("display in test1\n");
        }
};

extern "C" testBase* create()
{
        return new test1;
} 

extern "C" void destroy(testBase *p)
{
        delete p;
}
test2.cpp
#include<stdio.h>
#include "testBase.h"

class test2:public testBase
{
public:
        void display()
        {
                printf("display in test2\n");
        }
};

extern "C" testBase* create()
{
        return new test2;
} 

extern "C" void destroy(testBase *p)
{
        delete p;
}

main.cpp

#include<stdio.h>
#include"testBase.h"
#include<dlfcn.h>

int main()
{
        void *handle;
        char *error;
        handle = dlopen("./libtest2.so", RTLD_LAZY);
        if (!handle)
        {
                printf("%s\n", dlerror());
                return -1;
        }
        create_t* create = (create_t *)dlsym(handle, "create");
        if ( (error = dlerror()) != NULL)
        {
                printf("%s\n", error);
                return -1;
        }
        destroy_t* destroy = (destroy_t *)dlsym(handle, "destroy");
        if ( (error = dlerror()) != NULL)
        {
                printf("%s\n", error);
                return -1;
        }
        testBase *test = create();
        test->display();
        destroy(test);

        dlclose(handle);
        return 0;
}

编译:

g++ test1.cpp -fPIC -shared -olibtest1.so

g++ test2.cpp -fPIC -shared -olibtest2.so
g++ main.cpp -ldl -ltest1 -L./

三、总结:

1、必须使用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。因此,只有非成员函数才能被声明为extern "C",并且不能被重载。尽管限制多多,extern "C"函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了,相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。所以extern "C" 只是告诉编译器编和链接的时候都用c的方式的函数名字,函数里的内容可以为c的代码也可以为c++的。

2、链接时注意加动态连接参数-ldl

3、链接时要指定动态库路径(-L./)不然会会找不到动态库:cannot find -ltest1

4、运行时程序会提示如下错误

 error while loading shared libraries: libtiger.so: cannot open shared object file: No such file or direct

这是因为程序运行时没有找到动态链接库造成的。程序编译时链接动态库和运行时使用动态链接库的概念是不同的,在运行时,程序链接的动态链接库需要在系统目录下才行。

使用以下方法可以解决此问题

a. 在linux下最方便的解决方案是拷贝libtest1.so到绝对目录 /lib 下(但是,要是超级用户才可以,因此要使用sudo哦,亲)。就可以生成可执行程序了

b.第二种方法是:将动态链接库的目录放到程序搜索路径中,可以将库的路径加到环境变量LD_LIBRARY_PATH中实现:

export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH