动态库_动态库加载 - CSDN
精华内容
参与话题
  • 动态链接在企业级开发中无处不在,本课程包含Windows动态链接相关知识点,通过浅显易懂的代码与讲解,让你熟悉掌握动态链接技术!
  • 什么是动态库

    千次阅读 2014-02-20 00:28:32
    一直以来很困惑如何在linux中编译库文件和使用库文件,今天看了一篇文章才终于搞...转:如何用gcc编译生成动态链接库*.so文件 动态库 问:我源文件为main.c, x.c, y.c, z.c,头文件为x.h,y.h,z.h 如何编译成.s

    转自:http://gui323.blog.51cto.com/3168443/798329


    一直以来很困惑如何在linux中编译库文件和使用库文件,今天看了一篇文章才终于搞明白。以下为转载文章,仅供学习,如有侵权请告知。

    转:如何用gcc编译生成动态链接库*.so文件 动态库
    问:我源文件为main.c, x.c, y.c, z.c,头文件为x.h,y.h,z.h
    如何编译成.so动态库?
    编译器用gcc
    最好能给出详细参数解释,谢谢


    答:
    # 声称动代连接库,假设名称为libtest.so
    gcc x.c y.c z.c -fPIC -shared -o libtest.so


    # 将main.c和动态连接库进行连接生成可执行文件
    gcc main.c -L. -ltest -o main


    # 输出LD_LIBRARY_PATH环境变量,一边动态库装载器能够找到需要的动态库
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.


    # 测试是否动态连接,如果列出libtest.so,那么应该是连接正常了
    ldd main


    # 执行就不用说了吧 


    -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。


    -L.:表示要连接的库在当前目录中


    -ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称


    LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
    当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用
    /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。


    举例:
    动态链接库*.so的编译与使用- -
                                          


    动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一点帮助。
    1、动态库的编译


    下面通过一个例子来介绍如何生成一个动态库。这里有一个头文件:so_test.h,三个.c文件:test_a.c、test_b.c、test_c.c,我们将这几个文件编译成一个动态库:libtest.so。


    so_test.h:


    #include <stdio.h>
    #include <stdlib.h>
    void test_a();
    void test_b();
    void test_c();
    //---------------------------------------
    test_a.c:
    #include "so_test.h"
    void test_a()
    {
        printf("this is in test_a...\n");
    }
    //--------------------------------------
    test_b.c:
    #include "so_test.h"
    void test_b()
    {
        printf("this is in test_b...\n");
    }
    //------------------------------------------
    test_c.c:
    #include "so_test.h"
    void test_c()
    {
        printf("this is in test_c...\n");
    }


    将这几个文件编译成一个动态库:libtest.so
    $ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so
    2、动态库的链接


    在1、中,我们已经成功生成了一个自己的动态链接库libtest.so,下面我们通过一个程序来调用这个库里的函数。程序的源文件为:test.c。


    test.c:
    #include "so_test.h"
    int main()
    {
        test_a();
        test_b();
        test_c();
        return 0;
    }
    l         将test.c与动态库libtest.so链接生成执行文件test:
    $ gcc test.c -L. -ltest -o test


    l         测试是否动态连接,如果列出libtest.so,那么应该是连接正常了
    $ ldd test
             执行test,可以看到它是如何调用动态库中的函数的。
    3、编译参数解析
    最主要的是GCC命令行的一个选项:
              -shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件


             -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。


             -L.:表示要连接的库在当前目录中


             -ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称


             LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。


             当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。
    4、注意


           调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。
    展开全文
  • 动态库的使用方法

    千次阅读 2016-04-16 15:50:45
    在编写程序时,动态库是常常用到的工具,在vs等编程环境下只需要,完成工程的本地化配置或是直接将dll配置到环境变量即可(不推荐,毕竟小题大做了),而对于像楼主这样刚刚接触Linux操作系统的菜鸟来说,配置动态...

    在编写程序时,动态库是常常用到的工具,在vs等编程环境下只需要,完成工程的本地化配置或是直接将dll配置到环境变量即可(不推荐,毕竟小题大做了),而对于像楼主这样刚刚接触Linux操作系统的菜鸟来说,配置动态(共享)库还不是一个简单的问题。

    在介绍动态库的调用方法之前,先介绍一下动态库的编译(如何生成so文件)

    需要一个包含几个方法声明的头文件和对应的定义文件。例如

    //so_test.h 头文件
    #include "stdio.h"
    void test_a();
    void test_b();
    void test_c();
    //test_a.c:
    #include "so_test.h"
    void test_a()
    {
      printf("this is in test_a...\n");
    }
    //test_b.c:
    #include "so_test.h"
    void test_b()
    {
      printf("this is in test_b...\n");
    }
    
    #include "so_test.h"
    void test_c()
    {
      printf("this is in test_c...\n");
    }

    将上述so_test.h test_a.c test_b.c test_c.c 文件放与同一目录下,执行

    gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so

    这里写图片描述
    此时,我们可以看到 多出了一个libtest.so文件,这里的命名有些讲究,应该命名为lib+xxxx+.so后面再解释为什么,这就是我们刚刚生成的共享库文件。
    -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的(好像不是必须的哦)

    -shared : 该选项指定生成动态连接库,这个是必须的

    到这来似乎完成了共享库的编写,那么我们先来简单的介绍下两种调用方式:
    ①隐式调用
    ②显示调用

    简单来时,隐式调用就是指在我们的程序代码中,没有定义我们调用了哪个共享库,只是包含了某个贡献库的头文件而已,而在编译的时候需要指明共享库。我们继续用示例说明:

    #include "so_test.h"
    int main()
    {
    test_a();
    test_b();
    test_c();
    return 0;
    }

    很简单的调用,和普通的链接没什么区别啊!是的不过在编译的时候要花费一些功夫

     gcc test.c -L. -ltest -o test

    -L. : 表示需要链接的共享库在当前目录;

    -Itest : 表示调用的共享库的文件名为 lib+test+.so 即libtest.so

    我们运行一下生成的test文件
    这里写图片描述

    无法打开shared object file

    为什么不行,别急 我们借助ldd命令查看下 test文件的链接情况

    ldd text

    这里写图片描述
    我们刚刚写的libtest.so 就和你放在一个文件夹居然说找不到,服了(:зゝ∠)

    话说链接库可以从以下五个位置检索
    ①环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录;
    ②文件/etc/ld.so.cache中找到的库的列表,由ldconfig命令刷新;
    ③目录usr/lib;
    ④目录/lib;
    ⑤当前目录;

    我建议还是将我们生成的共享库拷贝到/usr/lib/或/lib目录下,修改LD_LIBRARY_PATH不是和windows环境下改环境变量一样小题大做吗?(个人看法请大神指正)

    cp libtest.so /usr/lib/
    

    这里写图片描述

    现在我们发现他能找到我们写的共享库了,也就能正常运行了。

    好的接下来看看显示调用

    此时我们已经写好了libtest.so共享库,并且已拷贝至/usr/lib/目录下

    显示调用,就是在代码中明确指明我们需要调用的共享库,而不需要在编译时指明,因此我们需要一些特殊的函数完成上述功能。

    我们需要用到一个头文件dlfcn.h 和其对应的四个函数。下面在介绍具体步骤再加以说明。
    ①把dlfcn.h系统头文件包含进来
    ②用dlopen()函数打开库文件,并指定打开方式;
    dllope()有两个参数
    第一个参数为共享库的名称,能取到的位置不在累述了。
    第二个参数为打开共享库的方式。有两个取值
    ①RTLD_NOW:将共享库中的所有函数加载到内存
    ②RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数
    返回值是一个void *指针
    ③用dlerror()函数测试是否打开成功,并进行错误处理,返回值是一个char *;
    ④用dlsym获得函数地址,存放在一个函数指针中,调用格式是dlsym(void *,”fcnname”);
    ⑤用获得的函数指针进行函数调用。
    ⑥程序结束时用dlclose(void *)关闭打开的动态库,防止资源泄露。

    #include <dlfcn.h>         //包含头文件
    #include "so_test.h"       //包含共享库头文件
    int main(int argc,char *argv[])
    {
       void(*pT)();            //申请一个与共享库对应的函数指针
       void *Handle=dlopen("libtest.so",RTLD_LAZY);
                               //打开共享库,并有void指针 Handle保存
       if(Handle==NULL)        //检测是否打开成功
       {
           printf("Fail load library\n");
           return -1;
       }
       char * pE=dlerror();    //检测函数是否打开成功
       if(pE!=NULL)
       {
           printf("%s\n",pE);
           return -1;
       }
       pT=dlsym(Handle,"test_a");    //调用具体的函数
       pE=dlerror();                 //再次检测函数打开是否成功
       if(pE!=NULL)
       {
           printf("%s\n",pE);
           return -1;
       }
       (*pT)();                      //调用共享库的函数
       dlclose(Handle);              //关闭共享库
    
       return 0;
    }
    

    编译命令为

    gcc -o main -ldl test.c 
    

    -ldl : 指明生成对象需要使用共享库,但不用具体指明哪个共享库

    这里写图片描述

    (^o^)/~ 调用动态(共享库)的方法就先说到这来喽!!

    展开全文
  • C++动态库的制作和调用

    万次阅读 2018-12-14 09:58:37
    1、dll的有点 代码复用是提高软件开发效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架,ATL、MFC等,它们都以...

    1、dll的有点

    代码复用是提高软件开发效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架,ATL、MFC等,它们都以源代码的形式发布。由于这种复用是“源码级别”的,源代码完全暴露给了程序员,因而称之为“白盒复用”。“白盒复用”的缺点比较多,总结起来有4点。 
    暴露了源代码;多份拷贝,造成存储浪费; 
    容易与程序员的“普通”代码发生命名冲突; 
    更新功能模块比较困难,不利于问题的模块化实现; 
    实际上,以上4点概括起来就是“暴露的源代码”造成“代码严重耦合”。为了弥补这些不足,就提出了“二进制级别”的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为“黑盒复用”。 

    说明:实现“黑盒复用”的途径不只dll一种,静态链接库甚至更高级的COM组件都是。

    2、ddl的创建

    2.1、创建及注意事项

    文件------>新建------>项目------>Win32控制台应用程序/Win32项目------>单击下一步------>应用程序类型选择DLL(图1)------>单击完成。

    创建出来原始项目结构:

    在附加选项中,选择空项目,生成的项目结构

    注意:解决方案配置问题,win32平台生成的dll文件,只能被win32平台运行的项目调用:x64平台生成的dll文件,只能被x64平台运行的项目调用。

    2.2、动态库制作方法

    extern "C" _declspec(dllexport)与project2.h中的#ifdef.......endif是将C++函数导出,才会生成lib文件

    2.2.1、方法一

    通过定义C的接口函数对类方法进行封装,及定义全局变量,源码如下(此方法定义的类,还可以进行多项目联合编程):

    Project1.h

    #include "stdafx.h"
    #include<iostream>
    #include<string>
    using namespace std;
    class project1
    {
    public:
    	project1();
    	~project1();
    	void project1_name();
    	void project1_budget(int money);
    	bool project1_run();
    	int project1_numPeople();
    	string project_name;
    };

    Project1.cpp

    #include "stdafx.h"
    #include"Project1.h"
    project1::project1(){}
    project1::~project1(){}
    project1 theApp;//定义一个全局变量,方便被封装函数调用类的方法
    void project1::project1_name()
    {
    	cout << "项目名称为:"<<endl;
    	cout << project_name << endl;
    }
    void project1::project1_budget(int money)
    {
    	cout << money << endl;
    }
    bool project1::project1_run()
    {
    	return true;
    }
    int project1::project1_numPeople()
    {
    	return 10;
    }
    extern "C" _declspec(dllexport) void name()
    {
    	theApp.project1_name();
    }
    extern "C" _declspec(dllexport) void budget(int money)
    {
    	theApp.project1_budget(money);
    }
    extern "C" _declspec(dllexport) bool run()
    {
    	return theApp.project1_run();
    }
    extern "C" _declspec(dllexport) int numPeople()//对numPeople进行封装,需要使用关键字extern "C" _declspec(dllexport),运用关键字后,才会生产lib文件
    {
    	return theApp.project1_numPeople();
    }

    2.2.2、方法二

    将类的成员函数直接封装成C接口,源码如下

    project2.h

    #ifdef TESTDLL_EXPORTS  
    #define TESTDLL_API __declspec(dllexport)   
    #else  
    #define TESTDLL_API __declspec(dllimport)   
    #endif 
    #include<iostream>
    #include<string>
    
    using namespace std;
    class project2
    {
    public:
    
    	project2();
    	~project2();
    	TESTDLL_API void project2_name();
    	TESTDLL_API void project2_budget(int money);//库制作不会报异常,但是传入参数会无效
    //应修改成 static TESTDLL_API void project2_budget(int money);
    	TESTDLL_API bool project2_run();
    	TESTDLL_API int project2_numPeople();
    	string project_name;
    };

    project2.cpp

    project2::project2(){}
    project2::~project2(){}
    void project2::project2_name()
    {
        project_name=“项目2”;//1
    	cout << "项目名称为:" << endl;
    	cout << project_name << endl;//2
        //调用时,应该把1和2注销,原因未理解
    }
    void project2::project2_budget(int money)
    {
    	cout << money << endl;
    }
    bool project2::project2_run()
    {
    	return true;
    }
    int project2::project2_numPeople()
    {
    	return 20;
    }

    2.3、查看动态库生成的接口

    运用的工具:单击Windows图标------>所有程序------>找到相应的Visual Studio文件夹------->选择Visual Studio tool(会打开文件夹)-------->寻找本机工具命令提示。切换到dll文件目录下,运行命令:dumpbin /EXPORTS 库名(例:dumpbin /EXPORTS Project2.dll)

    方法一生成的动态库结构图:

     方法二生成的动态库结构图:

    3、动态库的链接

    3.1、显示链接

    获取dll库的路径,无需配置环境,代码如下:

    #include<iostream>
    #include<Windows.h>
    using namespace std;
    void Display_Call_Project1_DLL()
    {
    	typedef void(*name)();
    	typedef void(*budget)(int money);
    	typedef bool(*run)();
    	typedef int(*numPeople)();
    	HMODULE hDLL = LoadLibrary("..\\..\\Make_Dll\\x64\\Debug\\Project1.dll");//dll的文件路径
    	if (hDLL == NULL)
    	{
    		cout << "动态库未找到" << endl;
    		return;
    	}
    	name n = name(GetProcAddress(hDLL, "name"));//运用函数名
    	n();
    	budget b = budget(GetProcAddress(hDLL, MAKEINTRESOURCE(1)));//运用序号调用,调用的为budget函数
    	b(2000);
    	
    	run r = run(GetProcAddress(hDLL, "run"));
    	cout << r() << endl;
    
    	numPeople np = numPeople(GetProcAddress(hDLL, "numPeople"));
    	cout << np() << endl;
    	FreeLibrary(hDLL);
    }
    //调用方法二生成的动态库
    void Display_Call_Project2_DLL()
    {
    	typedef void(*name)();
    	typedef void(*budget)(int money);
    	typedef bool(*run)();
    	typedef int(*numPeople)();
    	HMODULE hDLL = LoadLibrary("..\\..\\Make_Dll2\\x64\\Debug\\Project2.dll");//dll的文件路径
    	if (hDLL == NULL)
    	{
    		cout << "动态库未找到" << endl;
    		return;
    	}
    	name n = name(GetProcAddress(hDLL, "?project2_name@project2@@QEAAXXZ"));//运用函数名
    	n();
    	budget b = budget(GetProcAddress(hDLL, MAKEINTRESOURCE(1)));//运用序号调用,调用的为budget函数
    	b(1000);
    
    	run r = run(GetProcAddress(hDLL, MAKEINTRESOURCE(4)));
    	cout << r() << endl;
    
    	numPeople np = numPeople(GetProcAddress(hDLL, MAKEINTRESOURCE(3)));
    	cout << np() << endl;
    	FreeLibrary(hDLL);
    }
    int main()
    {
    	Display_Call_Project2_DLL();
    	system("pause");
    	return 0;
    }

    注意:运用方法二,生成的动态库,成员函数必须设置静态成员函数,并且不能调用类的成员。否则入传参无效,并且调用类成员会报错。

    3.2、隐式链接

    必须配置环境:

    项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件project2.h所在的目录

    项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加头文件project2.lib所在的目录

    项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加“project2.lib”(若有多个 lib 则以空格隔开)

    方法一生成的动态库,无法进行隐式链接。

    隐式链接动态库的制作方法,必须在类函数中加上宏定义,源码如下:

    project2.h

    #ifdef TESTDLL_EXPORTS
    
    #define TESTDLL_API __declspec(dllexport)   
    #else  
    #define TESTDLL_API __declspec(dllimport)   
    #endif 
    #include<iostream>
    #include<string>
    
    using namespace std;
    class project2
    {
    public:
    
    	TESTDLL_API project2();
    	TESTDLL_API ~project2();
    	TESTDLL_API void project2_name();
    	TESTDLL_API void project2_budget(int money);
    	TESTDLL_API bool project2_run();
    	TESTDLL_API int project2_numPeople();
    	string project_name;
    };

    project2.cpp

    #define TESTDLL_EXPORTS//不进行宏定义,或提示链接不一致,导致隐式调用失败
    #include"Project2.h"
    project2::project2(){}
    project2::~project2(){}
    void project2::project2_name()
    {
    	//project_name = "项目2";
    	cout << "项目名称为:";
    	//cout << project_name << endl;
    }
    void project2::project2_budget(int money)
    {
    	cout << money << endl;
    }
    bool project2::project2_run()
    {
    	return true;
    }
    int project2::project2_numPeople()
    {
    	return 20;
    }

     调用函数:

    main.cpp

    #include<iostream>
    #include<Windows.h>
    #include"Project1.h"
    #include"project2.h"
    using namespace std;
    void Call_Project2_DLL()
    {
    	project2 p;
    	p.project2_run();
    	p.project2_budget(1000);
    	
    }
    int main()
    {
    
    	Call_Project2_DLL();
    	system("pause");
    	return 0;
    }

    注:提示找不到dll库时,将dll库放在main.cpp同级目录下

    展开全文
  • 动态库

    2020-02-26 16:54:48
    总结一:动态库 前言  我们知道程序编译链接经常使用动态,同时我们可能还知道,动态库时程序运行时加载的。但是动态库到底有什么作用,如何生成、如何加载等,我们却很少关注。接下来,我给大家做一个简单的介绍。...

    总结一:动态库

    前言

    我们知道程序编译链接经常使用动态,同时我们可能还知道,动态库时程序运行时加载的。但是动态库到底有什么作用,如何生成、如何加载等,我们却很少关注。接下来,我给大家做一个简单的介绍。

    1.1 动态库和静态库的区别

    静态库特点(linux):

    • 命名上是以 *.o 结尾
    • 静态库在链接阶段直接就加入到可执行的文件中了,在执行过程中无需该静态库
    • 相对于动态库生成的文件,使用静态库生成的文件连接生成的可执行文件较大

    动态库的特点(linux)

    • 命名上是以 *.so
    • 目标文件在链接阶段只是指明链接的那个动态库,动态库与目标文件保持独立。在执行过程中需要该动态库
    • 使用动态库生成的目标文件较小

    对于工程中比较共通的源码文件,比如多个进程使用同一个模块的源码,我们最好将其制作成动态库,以节省系统空间。同时如果动态库出现bug,只需要重新生成一个动态库并将以前的替换即可。不需要重新编译其他模块。

    1.2 内存中的动态库

    在讲到动态库的装载时我们需要懂一定的背景知识,首先虚拟内存和物理内存,其次还有地址映射,这些知识就不在本文多加讲解,网上资料很多。我们动态库在整个内存空间是有一份,而每个进程都有自己的虚拟空间,虚拟空间会使用匿名映射(mmap使用MAP_PRIVATE方式进行映射),使自己的进程与动态库进行关联。本进程只会保留访问动态库时的一些数据。好了,打的方向就说这么多,这几句话随便抽出一个词来都够将好久。我们只需要对大方向有认识即可。以下就是映射表

    在这里插入图片描述

    此外我们说一些额外的小知识,在linux系统中 /proc 目录下有很多进程文件。在执行的进程都会创建一个文件。随便进入一个文件 /etc/1892。查看maps文件(sudo cat maps),这里面对应了动态库对应虚拟空间的位置,值得注意的是,这个文件最后一列有多少个[stack]就有多少个线程

    00008000-005ba000 r-xp 00000000 b3:01 11822      /usr/local/bin/ecTelematicsApp
    005c1000-005df000 rw-p 005b1000 b3:01 11822      /usr/local/bin/ecTelematicsApp
    005df000-00d7f000 rw-p 00000000 00:00 0          [heap]
    a8c00000-a8cff000 rw-p 00000000 00:00 0 
    a8cff000-a8d00000 ---p 00000000 00:00 0 
    a8e00000-a8e01000 ---p 00000000 00:00 0 
    a8e01000-a9600000 rw-p 00000000 00:00 0          [stack:1369]
    a9600000-a9700000 rw-p 00000000 00:00 0 
    a9700000-a9721000 rw-p 00000000 00:00 0 
    a9721000-a9800000 ---p 00000000 00:00 0 
    .......
    b5c5d000-b645e000 rw-p 00000000 00:00 0          [stack:1961]
    b645e000-b6470000 r-xp 00000000 b3:01 709        /lib/libresolv-2.19.so
    b6470000-b6477000 ---p 00012000 b3:01 709        /lib/libresolv-2.19.so
    b6477000-b6478000 r--p 00011000 b3:01 709        /lib/libresolv-2.19.so
    b6478000-b6479000 rw-p 00012000 b3:01 709        /lib/libresolv-2.19.so
    b6479000-b647b000 rw-p 00000000 00:00 0 
    .......
    bea5d000-bea7e000 rw-p 00000000 00:00 0          [stack]
    ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]
    

    同时我们也可以直接使用readelf -m [bin文件名],来读取该bin文件的链接信息。

    1.3 动态库的加载

    关于动态库我当初的直接认识是,程序运行到调用该动态库的接口时,会产生缺页,从而去磁盘加载动态库到内存,然后再执行。但事实并非如此。动态库也分为隐式链接和显示链接,不同的方式其载入内存的时间也是大相径庭。

    显示链接 隐式链接
    语法 不需要申明动态库先关的头文件,在调用时需要加载动态库的名称 只需要添加相应的头文件即可
    加载 执行到相应代码段时加载动态库(可以控制库的加载和卸载) 由系统控制加载时间,一般在程序启动时就加载

    由以上两点我们可以看出显示链接如果控制得当,对内存的消耗将下降许多,大型项目应该使用显示链接。但是缺点也有,就是如果库不存在,隐式链接可以再一开始就发现库不存在,而显示链接会被偶然触发。

    1.4 动态库的制作

    首先我们准备一个源文件 print.c

    #include<stdio.h>
    
    void printInter()
    {
        printf("%s\n", __FUNCTION__);
    }
    
    void printExtern()
    {
        printInter();
        printf("%s\n", __FUNCTION__);
    }
    

    输入指令

    gcc -fPIC -c print.c -o print.o
    gcc -shared print.o -o libprint.so -lstdc++
    

    由此我们生成了 libprint.so动态库。

    然后我们再创建libcurl.so 的接口头文件print.h

    #ifndef __PRINTT_H_
    #define __PRINT_H__
    
    void printExtern();
    
    #endif
    

    1.5 动态库的隐式链接

    我们创建main.c 去使用库

    #include<stdio.h>
    #include<unistd.h>
    #include "print.h"
    
    int main()
    {
    	printf("waite 5 seconds\n");
    	sleep(5);
    	printf("%s\n", __FUNCTION__);
    	printExtern();
    
    	return 0;
    }
    

    输入指令

    gcc -fPIC -c main.c -o main.o
    gcc -o target main.c -L./ -lprint -I./ -lstdc++
    

    生成target

    执行 ./target, 输出如下

    waite 5 seconds
    main
    printInter
    printExtern
    

    我们可以通过 readelf -d target,可以查看target链接了libprint.so

    root@user:/home/cjj/jianxiongs/so2# readelf -d target 
    
    Dynamic section at offset 0xf0c contains 25 entries:
      标记        类型                         名称/值
     0x00000001 (NEEDED)                     共享库:[libprint.so]
     0x00000001 (NEEDED)                     共享库:[libc.so.6]
     0x0000000c (INIT)                       0x80483fc
     0x0000000d (FINI)                       0x8048604
     0x00000019 (INIT_ARRAY)                 0x8049f00
     0x0000001b (INIT_ARRAYSZ)               4 (bytes)
     0x0000001a (FINI_ARRAY)                 0x8049f04
     0x0000001c (FINI_ARRAYSZ)               4 (bytes)
     0x6ffffef5 (GNU_HASH)                   0x80481ac
     0x00000005 (STRTAB)                     0x80482c8
     0x00000006 (SYMTAB)                     0x80481e8
     0x0000000a (STRSZ)                      208 (bytes)
     0x0000000b (SYMENT)                     16 (bytes)
     0x00000015 (DEBUG)                      0x0
     0x00000003 (PLTGOT)                     0x804a000
     0x00000002 (PLTRELSZ)                   32 (bytes)
     0x00000014 (PLTREL)                     REL
     0x00000017 (JMPREL)                     0x80483dc
     0x00000011 (REL)                        0x80483d4
     0x00000012 (RELSZ)                      8 (bytes)
     0x00000013 (RELENT)                     8 (bytes)
     0x6ffffffe (VERNEED)                    0x80483b4
     0x6fffffff (VERNEEDNUM)                 1
     0x6ffffff0 (VERSYM)                     0x8048398
     0x00000000 (NULL)                       0x0
    

    接下来我们删除 libprint.so。 然后再运行target,这时我们发现出现如下错误

    ./target: error while loading shared libraries: libprint.so: cannot open shared object file: No such file or directory
    

    这说明程序在一开始就要加载libprint.so的库,这就是动态库的隐式链接

    1.6 动态库的显式链接

    这时我们需要更改main.c的内容,修改最终如下

    #include<stdio.h>
    #include<unistd.h>
    #include<dlfcn.h>
    #include<stdlib.h>
    //#include "print.h"  删除引用头文件
    
    void test()
    {
        char *libname = NULL;
        char *err = NULL;
    
        //open the lib
        void *handle = dlopen("./libprint.so", RTLD_NOW);
        if(!handle)
        {
            err = dlerror();
            printf("Load libprint.so failed : %s \n", err);
            exit(1);
        }
        //clear error info
        dlerror();
    
        typedef void (*pf_t)(void);
        pf_t print = (pf_t)dlsym(handle, "printExtern");
    
        err = dlerror();
    
        if(err)
        {
            printf("fail to find function : %s \n", err);
            exit(1);
        }
    
        print(); //使用函数指针的方式引用,不能直接引用
    
        //close the lib
        dlclose(handle);
        if(dlerror())
        {
            printf("close libprint.so failed : %s \n", dlerror());
            exit(1);
        }
    
        printf("%s\n", __FUNCTION__);
    
    }
    
    
    int main()
    {
        printf("waite 5 seconds\n");
        sleep(5);
    
        test();
        return 0;
    }
    

    同时我们gcc 中还需要加上 libdl.so库

    gcc -fPIC -c main.c -o main.o
    gcc -o target main.c -L./ -lprint -I./ -lstdc++ -ldl
    

    此时我们删除 libprint.so。然后再执行target 文件。发现程序运行了一段时间,到使用库时才报错

    Load libprint.so failed : ./libprint.so: cannot open shared object file: No such file or directory 
    

    由此我们可以看出动态库的显示连接才能真正实现使用时才去调用。

    附录 : makefile

    在这里我给出这个小工程的makefile

    CXX = g++
    CC = gcc
    FLAGS = -fPIC
    TARGET = target
    CSOURCE = $(wildcard ./*.c)
    COBJS = $(CSOURCE:.c=.o)
    
    target: $(COBJS) libprint 
    	$(CC) $(FLAGS) -o $(TARGET) main.o -L./ -lprint -I./ -ldl
    
    %.c:%.o
    	$(CC) $(FLAGS) -c $< -o $@
    
    libprint:
    	$(CC) $(FLAGS) -shared -o libprint.so print.o
    
    rm:
    	rm print.o
    
    clean:
    	-rm -rf $(COBJS) $(TARGET) libprint.so
    
    test:
    	@echo $(CSOURCE)
    	@echo $(COBJS)
    
    展开全文
  • C++静态库与动态库的区别?

    万次阅读 多人点赞 2018-08-17 20:29:59
    C++静态库与动态库 这次分享的宗旨是——让大家学会创建与使用静态库、动态库,知道静态库与动态库的区别,知道使用的时候如何选择。这里不深入介绍静态库、动态库的底层格式,内存布局等,有兴趣的同学,推荐一本...
  • C++动态库封装及调用

    万次阅读 多人点赞 2017-05-10 16:10:49
    一直对动态库的封装理解不是很透彻,虽然之前写过一个Demo,不过并没有真正的理解。所以写下来,帮助自己理解下。 1、一个程序从源文件编译生成可执行文件的步骤: 预编译 --> 编译 --> 汇编 --> 链接 (1)预编译...
  • 动态库(共享库)的制作和使用

    千次阅读 2019-03-14 01:10:51
    Linux下的动态库为lib*.so格式的二进制文件(目标文件),对应于Windows下的.dll格式的文件。 (1)命名规则 lib+库名+.so (2)动态库的制作 1)生成与位置无关的代码(.o) 2)将.o文件打包成动态库(共享库)...
  • 通俗理解动态库与静态库区别

    万次阅读 多人点赞 2019-06-21 15:27:46
    引:最近做了算法产品化相关的一些工作,其中涉及到算法库封装的相关工作,封装为动态库。总结动态库和静态库区别和对应使用原则。 区别:静态库和动态库最本质的区别就是:该库是否被编译进目标(程序)内部。 ...
  • 动态库的创建与调用

    千次阅读 2019-09-15 12:26:34
    动态库的创建: 先创建一个目录用来后续操作: 然后在Dynamic目录下编辑两个文件,calculate.h和calculate.c,此两个文件和静态库用到的文件一样: 编译: 命令:gcc 源文件 -fPIC –shared –o 目标文件 ...
  • Linux中的动态库和静态库(.a.la.so.o)

    千次阅读 2018-04-22 00:07:40
    Linux中的动态库和静态库(.a/.la/.so/.o) 原文地址:https://www.cnblogs.com/findumars/p/5421910.html 在windows下,一般可以通过文件的后缀名来识别文件的类型。在Linux下大致上也是可以的。但是要明确的一点是...
  • 一、基本概念1.1什么是库在windows平台和linux平台下都大量存在着库。本质上来说库是一种可执行代码的二进制... 1.2库的种类linux下的库有两种:静态库和共享库(动态库)。二者的不同点在于代码被载入的时刻不同。静
  • 动态库编程详解

    千次阅读 2012-11-27 14:01:11
    一、动态库概念与分类 1、什么是动态库 2、动态库分类 4、动态库解决的问题 二、动态库的创建 1、规则动态库 2、声明导出函数的两种方式 2.1__declspec(dllexport)导出 2.2 .def文件导出 3...
  • 本文的目的是测试各种类型库的编译后的使用效果,包括库又链接其他库的编译方法,使用方法,依赖性等。 太长不看版:请跳至文章最后的总结对比表。...④动态库libbb.so依赖动态库libaa.so的测试; ...
  • 原文转自 :http://www.cnblogs.com/ljtknowns/p/5647793.html 文件目录结构如下 1 dynamiclibapp.c 2 Makefile 3 comm/inc/apue.h 4 comm/errorhandle.c 5 dynamiclib/Makefile ...7 d
  • C++静态库与动态库详解与使用

    千次阅读 2016-09-30 09:10:03
    这次分享的宗旨是——让大家学会创建与使用静态库、动态库,知道静态库与动态库的区别,知道使用的时候如何选择。这里不深入介绍静态库、动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书《程序员的自我修养...
  • VS2010 中编写动态库和调用动态库

    千次阅读 2018-11-12 23:05:42
    ... VS2010 中编写动态库和调用动态库 百度查了一下在VS中编写动态库的方法,亲测有效。 (1)启动VS2010》文件》新建》项目,按下图进行选择填写,选择Win32控制台应...
  • 动态库与静态库优缺点比较

    千次阅读 2017-11-14 11:17:22
    动态库与静态库优缺点比较我们在编写一个 C 语言程序的时候,经常会遇到好多重复或常用的部分,如果每次都重新编写固然是可以的,不过那样会大大降低工作效率,并且影响代码的可读性,更不利于后期的代码维护。...
  • VS下静态库与动态库的生成与使用

    千次阅读 2019-04-20 18:07:53
    静态库和动态库的区别与联系 什么是库 什么是静态库 什么是动态库 总结 VS下静态链接库的生成和使用 方法一 静态库和程序在同一目录下2 方法二 直接调用 MY_ADDlib3 方法三 建立自己的库函数推荐 VS下动态链接库...
  • 动态库链接boost静态库

    千次阅读 2018-08-22 15:34:09
    1. boost全部静态链接 2. c++静态链接 1,2点的改变如下,强制链静态的方法为参数下为-l:libXXXX.a; 对于boost log, 需要将宏-DBOOST_LOG_DYN_LINK去掉 LOCAL_STATICLIBS := boost_log boost_log_setup ...
1 2 3 4 5 ... 20
收藏数 809,541
精华内容 323,816
关键字:

动态库