专栏首页计算机视觉理论及其实现静态链接库和动态链接库的区别

静态链接库和动态链接库的区别

交流、咨询,有疑问欢迎添加QQ 2125364717,一起交流、一起发现问题、一起进步啊,哈哈哈哈哈

1、链接库概述

Linux下得库有动态与静态两种,动态通常用.so为后缀,静态用.a为后缀。面对比一下两者:

静态链接库:当要使用时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。

动态库而言:某个程序在运行中要调用某个动态链接库函数的时候,操作系统首先会查看所有正在运行的程序,看在内存里是否已有此库函数的拷贝了。如果有,则让其共享那一个拷贝;只有没有才链接载入。在程序运行的时候,被调用的动态链接库函数被安置在内存的某个地方,所有调用它的程序将指向这个代码段。因此,这些代码必须使用相对地址,而不是绝对地址。在编译的时候,我们需要告诉编译器,这些对象文件是用来做动态链接库的,所以要用地址无关代码(Position Independent Code (PIC))

动态链接库的加载方式有两种:隐式加载和显示加载。

注意:linux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接(见本文第四部分)。

2、静态链接库

下面就通过实际的例子来向大家演示一下,该怎样编译和使用静态和动态链接库:

2.1、编辑测试文件

二个文件:add.c、 sub.c、add.h 、sub.h 和 main.c

/*add.h */

#ifndef _ADD_H_

#define _ADD_H_

int add(int a, int b);

#endif

-------------------------------------------------------------------------------------------------

/*add.c*/

#include "add.h"

int add(int a, int b)

{

    return a+b;

}

-------------------------------------------------------------------------------------------------

/*sub.h*/

#ifndef _SUB_H_

#define _SUB_H_

int sub(int a, int b);

#endif

-------------------------------------------------------------------------------------------------

/*sub.c*/

#include "add.h"

int sub(int a, int b)

{

    return a-b;

}

-------------------------------------------------------------------------------------------------

/*main.c*/

#include <stdio.h>

#include "add.h"

#include "sub.h"

int main(void)

{

       printf("1 + 2 =%d\n", add(1, 2));

       printf("1 - 2 =%d\n", sub(1, 2));

       return 0;

}

-------------------------------------------------------------------------------------------------

2.2、将.c 编译生成 .o文件

gcc -c add.c

gcc -c sub.c

生成的文件:sub.o ,add.o

无论是静态库文件还是动态库文件,都是由 .o 文件创建的。

2.3、由 .o 文件创建.a静态库

ar crlibmymath.a sub.o add.o

ar:静态函数库创建的命令

-c :create的意思

-r :replace的意思,表示当前插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误信息,并不替换其他同名的模块。默认的情况下,新的成员增加在库德结尾处。

库文件的命名规范是以lib开头(前缀),紧接着是静态库名,以 .a 为后缀名。

2.4、在程序中使用静态库

gcc -o main main.c -L. –lmymath

-L 指定函数库查找的位置,注意L后面还有'.',表示在当前目录下查找

-l则指定函数库名,其中的lib和.a(.so)省略。

注意:-L是指定查找位置,-l指定需要操作的库名。

静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名(是mymath 而不是libmymath.a ),gcc将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来查找静态库文件。在程序main.c中,我们包含了静态库的头文件add.h和sub.h,然后在主程序main中直接调用公用函数add()和sub()即可。

2.5、生成目标程序main,然后运行

./main

1 + 2 = 3

1 - 2 = -1

3、动态库(隐式链接)

3.1、由 .o创建.so动态库

动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为mymath,则动态库文件名就是libmamath.so。用gcc来创建动态库。在系统提示符下键入以下命令得到动态库文件libmamath.so。

gcc -fPIC-o add.o -c add.c

gcc -fPIC-o sub.o -c sub.c

gcc -shared-o libmamath.so add.o sub.o

或者:

gcc –c –o add.oadd.c

gcc –c –o sub.osub.c

gcc -shared -fPCI-o libmyhello.so add.o sub.o

这里:

-fpic:产生代码位置无关代码

-shared :生成共享库

3.2、隐式方式使用动态库

在程序中隐式使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件,再运行它看看结果。

gcc -o main main.c -L. -lmymath

./main

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

出错了!!!

快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。

动态库的搜索路径搜索的先后顺序是:

1.编译目标代码时指定的动态库搜索路径;

2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径;

3.配置文件/etc/ld.so.conf中指定的动态库搜索路径;//只需在在该文件中追加一行库所在的完整路径如"/root/test/conf/lib"即可,然后ldconfig是修改生效。

4.默认的动态库搜索路径/lib;

5.默认的动态库搜索路径/usr/lib。

为此解决方法:

1. 我们将文件libmyhello.so复制到目录/usr/lib中:

mv libmyhello.so/usr/lib/

2. 将libmyhello.so拷贝到可执行文件main的同一目录下。

再次运行:./main

1 + 2 = 3

1 - 2 = -1

成功了!这也进一步说明了动态库在程序运行时是需要的。

3.3、动态库的初始化和解析

Windows下的动态库加载,卸载都会有初始化函数以及卸载函数来完成库的初始化以及资源回收,linux当然也可以实现,这些初始化函数主要包含两个部分:动态库的构造和析构函数机制、动态库的全局变量初始化工作。

1、动态库的构造和析构函数机制

在Linux中,提供了一个机制:在加载和卸载动态库时,可以编写一些函数,处理一些相应的事物,我们称这些函数为动态库的构造和析构函数,其代码格式如下:

void __attribute__ ((constructor)) my_init(void);  // my_init为自定义的构造函数名

void __attribute__ ((destructor)) my_fini(void);  //my_fini为自定义的析构函数名

在编译共享库时,不能使用"-nonstartfiles"或"-nostdlib"选项,否则构建与析构函数将不能正常执行(除非你采取一定措施)。

注意,构造函数的参数必须为空,返回值也必须为空。

举个例子,动态库文件a.c的代码如下:

void __attribute__((constructor)) my_init(void)

{ 

    printf("init library\n"); 

}

编译成动态库:

gcc -fPIC -shared a.c -o liba.so

主程序main.c如下:

#include<stdlib.h>

#include<stdio.h>

int main()

{ 

    pause(); 

    return 0; 

}

编译:

gcc -L./ -la main.c -o main.bin

运行main.bin程序:

也就是说,在运行main时,加载完liba.so后,自动运行liba.so的初始化函数。

2、全局变量初始化

①先看如下例子:

//文件名:b1.c 

#include<stdlib.h> 

#include<stdio.h> 

int reti()

{ 

    printf("reti\n"); 

    return 10; 

} 

int g1=reti();  // g1是个全局变量

使用GCC对其进行编译:

gcc -fPIC -shared b1.c -o libb.so

编译错误!使用G++对其进行编译:

g++ -fPIC -shared b1.c -o libb.so

编译成功!可见GCC和G++对于这种全局变量初始化的方法,支持力度是不一样的。

//主程序

//文件名:main.c

#include <stdlib.h>

#include <stdio.h>

int main()

{ 

    pause(); 

    return 0; 

}

编译执行文件:

gcc -L./ -lb main.c -o main.bin

运行main.bin:

这说明,进程在加载libb.so后,为了初始化全局变量g1,其会运行reti来初始化g1。

②再来看一个C++的例子:

//文件名:b2.cpp

class Myclass

{ 

public: 

   Myclass(); 

   int i; 

}; 


Myclass::Myclass()

{ 

   printf("constructMyclass\n"); 

}; 

Myclass g1;

编译动态库:

g++ -fPIC -shared b2.cpp-o libb.so

在动态库libb.so中,声明了一个类型为Myclass的全局变量g1。

//主程序

//文件名:main.cpp

#include <stdlib.h>

#include <stdio.h>

#include<unistd.h>


int main()

{ 

    pause(); 

    return 0; 

}

编译执行文件:

g++ -L./ -lb main.cpp -o main.bin

运行main.bin:

这说明,进程在加载liba.so后,为了初始化全局变量g1,程序在进入main函数前将会运行Myclass的构造函数。

4、动态链接库(显式链接)

4.1、重要的dlfcn.h头文件

LINUX下使用动态链接库,源程序需要包含dlfcn.h头文件,此文件定义了调用动态链接库的函数的原型。下面详细说明一下这些函数。

函数dlerror:

原型为: const char *dlerror(void);

当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

函数dlopen:打开指定的动态链接库文件

原型为: void *dlopen (const char *filename, int flag);

dlopen用于打开指定名字(filename)的动态链接库,并返回操作句柄。

filename: 如果名字不以/开头,则非绝对路径名,将按下列先后顺序查找该文件:

(1) 用户环境变量中的LD_LIBRARY值;

(2) 动态链接缓冲文件/etc/ld.so.cache

(3) 目录/lib,/usr/lib

flag表示在什么时候解决未定义的符号(调用)。取值有两个:

1) RTLD_LAZY : 表明在动态链接库的函数代码执行时解决。

2) RTLD_NOW : 表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。

dlopen调用失败时,将返回NULL值,否则返回的是操作句柄。

函数dlsym : 取函数执行地址

原型为: void *dlsym(void *handle, char *symbol);

dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。

如程序代码: void (*add)(int x,int y); /* 说明一下要调用的动态函数add */

add=dlsym("xxx.so","add"); /* 打开xxx.so共享库,取add函数地址 */

add(89,369); /* 带两个参数89和369调用add函数 */

函数dlclose : 关闭动态链接库

原型为: int dlclose (void *handle);

dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

4.2、显加载示动态链接库的实例

在下面这个实例中将通过动态加载libmymath.so链接库,来调用add()和sub()两个函数。

/*main.c*/

#include <stdio.h>

#include <dlfcn.h>


int main(void)

{

       void*dp=dlopen("libmymath.so",RTLD_LAZY);

       if(NULL==dp)

       {

                printf("打开动态链接库时失败!");

                return1;

       }


       //定义函数指针

       int(*fn_add)(int,int)=NULL;

       int(*fn_sub)(int,int)=NULL;

       fn_add=dlsym(dp,"add");

       fn_sub=dlsym(dp,"sub");

       if(NULL==fn_add|| NULL==fn_sub)

       {

                printf("在动态链接库中寻找函数失败!");

                return1;

       }

       printf("1+ 2 = %d\n", fn_add(1, 2));

       printf("1- 2 = %d\n", fn_sub(1, 2));


       dlclose(dp);

       return0;

}

将libmymath.so和main.c放在同一个目录下,执行如下命令:

gcc -rdynamic -s -o main.bin main.c -ldl

-rdynamic选项以指定输出文件为动态链接的方式

-s指定删除目标文件中的符号表,

-ldl则指示装配程序ld需要装载dl函数库。

最后运行main.bin的结果同上。

4.3、Windows下和Linux下显示加载动态链接库的比较

Windows下动态链接库以“.dll”为后缀,而Linux下得动态链接库是以”.so”为后缀的。

函数功能

Windows下

Linux下

打开加载动态链接库

LoadLibrary

dlopen

获取动态链接库中的函数地址

GetProcAddress

dlsym

关闭动态链接库

FreeLibrary

dlclose

在使用时应包含的头文件

Winbase.h(include Windows.h)

dlfcn.h

5、特殊情况

我们回过头看看,发现使用静态库和隐式方式使用动态库时编译成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?抱着对问题必究到底的心情,来试试看。先删除除.c和.h外的所有文件,恢复成我们刚刚编辑完举例程序状态。

gcc -c add.c

gcc -c sub.c

ar crlibmymath.a sub.o add.o

gcc -shared -fPCI -olibmyhello.so sub.o add.o

现在目录有两个同名的库文件(动态库文件和静态库文件同名):

libmymath.a 、 libmymath.so

编译运行程序:

gcc -o main main.c -L. -lmymath

./main

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

从程序./main运行的结果中很容易知道,当Linux静态库和Linux动态库同名时, gcc命令将优先使用动态库。如果强制使用静态库则需要加-static选项支持,即:

gcc-static -o main main.c -L. -lmymath

链接静态库的可执行程序明显比链接动态库的可执行文件大。

6、查看库中的符号

1、使用nm命令可以打印出库中涉及到的所有符号。库既可以是静态库也可以是动态的

常见的三种符号:

①在库中被调用,但没有在库中定义(表明需要其他库支持),用U表示

②在库中定义的函数,用T表示

③“弱态”符号,他们虽然在库中被定义但是可能被其他库中同名的符号覆盖,用W表示。

2、用ldd命令可以查看一个可执行程序依赖的共享库。

7、Linux下so导出指定函数

Linux下编译so导出源文件里面的指定函数:

1、在文件里面最前面加上:#defineDLL_PUBLIC __attribute__((visibility("default")))

2、在文件里面需要导出的函数前加上:extern "C" DLL_PUBLIC

3、Linux下动态库(so)编译时默认不导出,在Makefile中需要添加:-fvisibility=hidden

各位看官老爷,如果觉得对您有用麻烦赏个子,创作不易,0.1元就行了。下面是微信乞讨码:

添加描述

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.csdn.net/weixin_36670529复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • C语言 | 什么是静态链接库和动态链接库?

    今天分享的是静态链接库和动态链接库的相关知识,并且自己动手写一个简单的静态函数库和动态函数库,来体验这个流程。

    飞哥
  • Eclipse C++配置静态链接库和动态链接库

    转:https://blog.csdn.net/iteye_20658/article/details/82650699

    墨文
  • 在linux下制作静态库和动态链接库的方法

    制作 ar -cr libxxx.a xxx1.o xxx2.o xxx3.o ...

    杨源鑫
  • 浅谈Linux的动态链接库

    上一篇我们分析了Hello World是如何编译的,即使一个非常简单的程序,也需要依赖C标准库和系统库,链接其实就是把其他第三方库和自己源代码生成的二进制目标文...

    PP鲁
  • Linux动态链接库的使用

    动态链接库与普通的程序相比而言,没有main函数,是一系列函数的实现。通过shared和fPIC编译参数生产so动态链接库文件。程序在调用库函数时,只需要连接上...

    砸漏
  • 动态链接库的使用:加载和链接

    在部分SDK的对接中,有些平台除了DLL外并没有提供导入库来供我们使用,那就只能使用代码中加载DLL的办法来调用DLL内的函数,本文来记录一下两种用法,再分析一...

    查利鹏
  • visual studio静态,动态链接库开发工具简单使用

    这里我不会使用visual studio的图形界面工具,作为专业人士,还是搞懂自己的工具是怎么运转的,这样比较好。

    byronhe
  • cgo构建引用c的静态链接库

    项目中免不了要使用c/c++的工程代码,因此使用cgo引入c的静态库也是不可避免(虽然官方要求尽量使用go构建你的项目,而不是偷巧的导入c代码,尽量保持go项目...

    DifficultWork
  • linux 动态链接库查找方法;查找动态链接库位置; LIBRARY_PATH 和 LD_LIBRARY_PATH 的区别;LD_LIBRARY_PATH and LD_RUN_PATH的区别;MAC

    今天配置之前项目的时候,发现有些动态链接库变了,想看看现在应用在使用哪些动态链接库的时候,进一步查了点资料;

    xuyaowen
  • cmake:动态链接库(so)中静态链接tcmalloc(gperftools2.4)暨静态链接libstdc++

    将tcmalloc作为动态库使用,非常方便,网上有很多资料介绍了。tcmalloc.a也可以以静态链接的方式加入应用程序中,大概因为使用太方便,网上关于这方面的...

    用户1148648
  • c语言里面静态链接库的制作和使用

    今天在交流群里面看到有一个网友问了一个内联函数的问题,原本想写这个文章的;由于已经提前说写静态链接库的制作和使用,所以内联函数的文章,明天来写!在开始写这个文章...

    用户6280468
  • gcc编译参数:如何包含头文件和动态链接库

    以上一个代码实例gdal计算NDVI为例: 如何在Linux下使用gcc进行编译? (顺便说一下,上次的代码只能在gdal1下编译,因为gdal2和1的...

    卡尔曼和玻尔兹曼谁曼
  • Linux下HOOK动态链接库中API的方法

            2012年,我写了一篇介绍Windows系统下Ring3层API的hook方案——《一种注册表沙箱的思路、实现——Hook Nt函数》,其在底层...

    方亮
  • C++调用C语言写成的动态链接库

    C++在语法上是兼容C的,但是这不代表使用C语言不做任何处理直接写成的动态链接库就可以被C++给调用。由于C++引入了函数重载的机制,而这个机制的实现是在编译器...

    zy010101
  • Python 调用C编译的动态链接库DLL方法

    Ning@
  • C++基础语法梳理:Windows 的动态链接库

    GUI(Graphical User Interface)应用,链接器选项:/SUBSYSTEM:WINDOWS

    玖柒的小窝
  • C/C++中动态链接库的创建和调用

    DLL 有助于共享数据和资源。多个应用程序可同时访问内存中单个DLL 副本的内容。DLL 是一个包含可由多个程序同时使用的代码和数据的库。下面为你介绍C/C++...

    古时的风筝
  • QT5 动态链接库的创建和使用

    记录一下QT5 动态链接库的创建和使用 在文章的最后有完成的代码供下载 1.创建动态链接库 先新建一个库项目 ? 选择chose进入下一下页面,类型选择共享库,...

    lpxxn
  • 使用python创建生成动态链接库dll的方法

    如今,随着深度学习的发展,python已经成为了深度学习研究中第一语言。绝大部分的深度学习工具包都有python的版本,很多重要算法都有python版本的实现。...

    砸漏

扫码关注腾讯云开发者

领取腾讯云代金券