显式链接、隐式链接和显式加载、隐式加载以及动态库路径查找

我们知道库一般有静态库和动态库2种:

  • 静态库是编译时就链接到可执行文件中的;
  • 动态库是在程序运行时再进行加载的。

故本文讨论的链接与加载方式是指对动态库而言的。

一、动态库的加载方式

1、隐式加载

    就是我们需要准备好.h、.lib或者.so,对头文件进行包含,并添加对lib的链接命令,来完成对库函数的调用。这种链接方式,称之为隐式链接

    在程序从开始运行时,就会按照系统中一定的搜索路径,寻找动态库,找到就自动加载它,才能成功运行程序,这些步骤,是系统自动完成的。

    一般来说,隐式链接的动态库,在程序加载时,无需用户干涉,便称之为隐式加载

    一般通常使用隐式加载的方式,比显示加载,代码编写更简单。

2、显式加载

    我们对动态库的调用,是在代码中直接使用LoadLibrary,或其他加载函数,直接对dll或so进行加载,然后解析文件中的函数符号,并调用该函数。这种链接方式,称之为显式链接

    这种加载方式依赖于用户的加载代码,所以是程序运行起来后,属于延迟加载。需要用户明确知道库文件名称,故称之为显式加载

    这种方式,解析函数符号的代码较多,编写不方便。

二、Windows下程序如何查找dll

1、搜索顺序

在win系统下,应用程序搜索其依赖的dll时,按如下顺序进行查找:

  • 应用程序所在的路径;
  • Windows的SYSTEM目录,如C:\Windows\System,通过调用GetSystemDirectory函数可以获取这个目录的路径;
  • Windows目录,如C:\Windows,通过调用GetWindowsDirectory函数可以获取这个目录的路径;
  • PATH环境变量指定的路径。

上述查找顺序,对隐式加载、显式加载方式的dll均有效

隐式加载dll时,没什么好说的,必定通过上述顺序查找dll。

显式加载dll时,由于使用了LoadLibrary函数,如下:

HMODULE WINAPI LoadLibrary(
  _In_ LPCTSTR lpFileName
);
  • 如果lpFileName字符串指定完整路径,则该函数仅搜索该dll的路径。
  • 如果lpFileName字符串指定一个没有路径的dll名称或者相对路径(如lpFileName=“Hello.dll”),则该函数使用标准搜索策略来查找模块,也就是使用上述顺序进行查找"Hello.dll"。

2、指定隐式加载dll的路径

对于需要隐式加载的dll,由于其加载过程由系统接管。即隐式加载的dll是在exe主体模块之前加载进来的,执行顺序上是先通过PE文件的导入表加载隐式的dll,然后再加载EXE本体。

所以在程序exe中无法通过代码,来指定隐式加载dll的路径。

只能通过修改PATH环境变量来指定加载dll的路径,或者直接将dll移入可以被搜索到的路径中来实现。

3、指定显式加载dll的路径

对于需要显式加载的dll,由于其LoadLibrary函数参数,即可指定dll的路径;若只提供了文件名,则也可以将dll的路径设置到PATH环境变量中,或者直接将dll移入可以被搜索到的路径中。

4、小结

dll的搜索顺序,对于无论是显式、隐式加载dll,都是有效的,因为都需要查找dll。只不过显式加载时,它通过传递参数直接定位到具体dll时,不需要额外搜索。

牢记:使用隐式加载时,程序中无法通过代码,来指定该dll搜索路径。

无论显式、隐式加载,通常最简单的办法,就是将dll与exe放于同一目录下。

三、Linux下程序如何查找so

1、搜索顺序

在linux系统下,应用程序搜索其依赖的so时,按如下顺序进行查找:

  • gcc编译时指定的运行时库路径 -Wl,-rpath
  • 环境变量LD_LIBRARY_PATH指定的路径
  • 从cache文件/etc/ld.so.cache中查找,该文件包含了先前在库路径中找到的编译好的候选库列表。
  • 系统默认库位置/lib和/usr/lib路径

上述查找顺序,对隐式加载、显式加载方式的so均有效

隐式加载so时,没什么好说的,必定通过上述顺序查找so。

显式加载so时,由于使用了dlopen函数,如下:

void* dlopen(const char* filename, int flags);
  • 如果filename字符串指定完整路径,则该函数仅搜索该dll的路径。
  • 如果filename字符串指定一个没有路径的dll名称或者相对路径(如lpFileName=“Hello.so”),则该函数使用标准搜索策略来查找模块,也就是使用上述顺序进行查找"Hello.so"。

关于linux下显式加载so的例子,可参考《Linux 动态加载并调用动态库(.so)方法介绍》

2、指定隐式加载so的路径

对于需要隐式加载的so,由于其加载过程由系统接管。即隐式加载的so是在程序主体模块之前加载进来的,执行顺序上是先加载隐式的so,然后再加载程序本体。

所以在程序中无法通过代码,来指定隐式加载so的路径。

但是可以通过修改上述顺序中任一环节,来指定隐式加载的so路径。

linux与win有区别,win下直接将dll和exe放于同一目录就可以找到。但是在linux下不行,这显得有些智障。。。主要是linux的理念不一样,系统设计者希望大家把so库都放到系统指定的目录下,但是个人并不喜欢这样做,很多的应用so糅合在一起很乱。

推荐使用第一种,即指定gcc编译选项来实现。

假设,我们使用Qt开发的程序,希望在程序所在目录下lib/中去寻找so,那么可以在程序工程的.pro文件中,添加如下编译选项:

QMAKE_LFLAGS += -Wl,-rpath=./lib

则该程序启动时,会在./lib/去找so。当然也可以改成./当前目录下,这就和win下时很像了。

另外,如果程序显示加载1.so,1.so隐式加载调用2.so,出现找不到2.so的问题。那么,可以在1.so代码所属工程.pro中,添加上述的编译选项,就可以解决这个问题。

3、指定显式加载so的路径

对于需要显式加载的so,由于其dlopen函数参数,即可指定so的路径;若只提供了文件名,则可以通过修改上述顺序中任一环节,来指定so路径。

4、小结

so的搜索顺序,对于无论是显式、隐式加载,都是有效的,因为都需要查找so。只不过显式加载时,它通过传递参数直接定位到具体so时,不需要额外搜索。

牢记:使用隐式加载时,程序中无法通过代码,来指定该so搜索路径。但是可以通过编译选项来指定。

四、总结

win、linux下动态库的加载和搜索机制很相似,但是有一些不同,主要不同之处体现在,隐式加载动态库时:

  • win下,只能通过修改PATH指定库路径,但是默认支持搜索程序当前目录。
  • linux下,支持gcc编译选项指定库路径,但是默认不支持搜索程序当前目录。

另外,在win下,使用隐式加载时,程序中无法通过代码,来指定该dll搜索路径。

在linux下,虽然依然不能通过程序中代码来,指定该so搜索路径;但是可以使用gcc编译选项的方式,提前指定该程序搜索so的路径,也算是一种很好的补充方式。



若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

在这里插入图片描述

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百里杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值