目录

linuxlinux基础IO八动态库的制作与使用

【linux】linux基础IO(八)动态库的制作与使用




倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己!
https://i-blog.csdnimg.cn/direct/316a3966ce1c4b1d8c0fbcae3d4936ac.gif#pic_center



前言

【linux】linux基础IO(七)静态库的制作与使用——书接上文 ,本文会在上文的基础上进行讲解,所以对上文不了解的读者友友请点击前方的蓝字链接进行学习
本文由小编为大家介绍——【linux】linux基础IO(八)动态库的制作与使用


一、动态库的制作

关于下面的讲解需要用到的一些铺垫性的知识,在上一篇文章中小编已经进行讲解了,这里就不再赘述,请读者友友点击后方蓝字链接进行学习

  1. 和上一篇文章的静态库一样,今天小编带领大家先站在制作者的角度制作一下动态库,之后再站在用户的角度去使用一下动态库
  2. 那么小编接下来小编编写两个配套的源文件以及头文件,分别用于打印,以及显示log日志信息,相信编写如下代码对于读者友友已经没有障碍了
    https://i-blog.csdnimg.cn/direct/03fe621aade843ab80cf850aeb9f7165.png
//print.h
#pragma once //防止头文件被重复包含

#include <stdio.h>

void Print();
//print.c
#include "myprint.h"

void Print()
{
  printf("hello linux\n");
  printf("hello linux\n");
  printf("hello linux\n");
}
//mylog.h
#pragma once //防止头文件被重复包含

#include <stdio.h>

void Log(const char* info);
//mylog.c
#include "mylog.h"


void Log(const char* info)
{
  printf("Waring: %s\n", info);
}
  1. 首先,动态库的制作和静态库的类似,但是选项以及打包工具有所不同
  2. 在编译源文件形成.o文件的时候,相较于静态库的gcc -c,对于动态库,使用gcc进行编译需要额外添加-fPIC选项(fPIC是产生位置无关码)
  3. 并且动态库进行打包不需要使用ar归档工具,动态库进行打包而是直接采用gcc,额外添加-shared选项,即可将.o文件打包形成动态库,那么有了如上知识的铺垫之后,我们就可以编写我们的自动化构建工具makefile了
dy-lib=libmymethod.so //定义dy-lib变量,其实代表的是libmymethod.so后面便于进行修改

$(dy-lib):myprint.o mylog.o//$(dy-lib)的意思是libmymethod.so, libmymethod.so作为目标文件的依赖方法是myprint.o mylog.o
	gcc -shared -o $@ $^ //gcc使用shared选项对.o文件进行打包,形成动态库

myprint.o:myprint.c
	gcc -fPIC -c $^ //动态库的.o文件的编译需要添加-fPIC选项,
					//gcc不使用-ox选项进行命名的时候, 进行汇编后的结果命名会形成myprint.c名称的同名.o后缀的名称,即myprint.o
mylog.o:mylog.c
	gcc -fPIC -c $^ //动态库的.o文件的编译需要添加-fPIC选项

.PHONY:clean //.PHONY伪目标clean既然可以执行rm指令,那么同样也可以执行其它指令
clean:
	rm -rf *.o *.so mylib

.PHONY:output //那么我们同样也可以执行其它指令
output:
	mkdir -p mylib/include //递归创建头文件目录
	mkdir -p mylib/lib   //递归创建库文件目录
	cp *.h mylib/include //将.h为后缀的头文件放到指定头文件目录路径下
	cp *.so mylib/lib //将.so为后缀的动态库放到指定库目录路径下
  1. 此时使用自动化构建工具的make指令就可以创建出动态库libmymethod.so,使用make output指令就可以创建出发送给用户的mylib库,将动态库libmymethod.so放到mylib库的库文件目录中,将头文件放到mylib库的头文件目录中了,此时我们的可以发送给用户的库就制作完成了
    https://i-blog.csdnimg.cn/direct/ce23adea861649a0a956703bbb622cf1.png
  2. 同时我们观察一下动态库libmymethod.so其中它的权限中有x可执行权限,如何理解呢?那么我们是否可以执行它呢?如下结果演示,自然是不可以去执行的,其实当用户形成可执行程序之后,用户运行它的可执行程序,当需要用到动态库的某种函数的时候,就会以某种形式跳转到动态库中去执行对应的函数方法,由于可执行程序的权限中有x可执行,而且可执行程序又可以跳转到动态库中去执行动态库的函数方法,所以这里我们也可以理解为动态库也变相的拥有了可执行权限x
    https://i-blog.csdnimg.cn/direct/7b2b4c8ed1b345efb767a2f703f078f5.png
  3. 那么此时思考静态库为什么不需要加载?因为静态库不同于动态库,静态库是在链接的时候,直接将对应用户需要用到的方法拷贝进用户的源文件中,拷贝完成之后静态库就已经完成了历史使命,所以静态库不需要进行加载,此时静态库的方法已经属于用户的可执行程序的一部分,那么此时直接将形成的可执行程序中也就不需要依赖其他人,即不需要依赖静态库,只需要将可执行程序加载到内存以进程的形式能执行就可以了,同时这也是静态库没有可执行权限x的原因,因为可执行程序不需要跳转到静态库中去执行,而是直接在自己程序内部去执行,此执行过程和静态库无关,所以静态库没有可执行权限x

二、动态库的使用

  1. 上面我们已经完成了库的制作,下面小编以一个用户的视角,带领大家实际使用一下动态库,首先我们同样创建一个test目录作为用户的目录,使用mv将当前目录的mylib库移动到test用户目录中,模拟我们将库发送给用户,进入用户目录,我们tree一下mylib库,我们看到头文件和动态库已经被放到了对应的目录路径下了
    https://i-blog.csdnimg.cn/direct/a4c32bfda49a434bb1c0ffdf94dee8af.png
  2. 接下来就是编写对应的main.c主函数,即用户包头文件,以及使用我们的方法
#include "myprint.h"
#include "mylog.h"

int main()
{
  Print();
  Log("log function");

  return 0;
}
  1. 此时我们使用gcc编译链接一下main.c,同时和静态库一样的调用方法,使用-I(大写的i)选项给gcc指示头文件搜索路径,使用-L选项给gcc指示动态库的搜索路径,使用-l(小写的l)选项给gcc指明要链接哪一个动态库,动态库的名字要掐头去尾,以libXXX.so为例,即去掉前面的lib以及后缀.so,所以XXX为动态库的实际名称,此时gcc就编译链接形成了可执行文件a.out,无误
    https://i-blog.csdnimg.cn/direct/ef717a019a214154a11a80ff1541d090.png
  2. 终于将我们的main.c与库进行了链接,形成了可执行文件,终于可以执行可执行文件了,动态库不过如此,可是当我们真的实际去运行可执行程序的时候,居然是找不到运行失败,此时我们再使用ldd去查看一下可执行程序的所链接的共享库,什么?居然找不到共享库,你逗我呢?gcc,在编译时候,我明明都使用选项-L告诉了你动态库的所在路径,使用选项-l(小写的l)告诉了你要去链接哪一个动态库,你在这的却告诉我无法找到对应的动态库???
    https://i-blog.csdnimg.cn/direct/2aa01df607ea45a7886ddd393f4ac1a8.png
  3. 是的,gcc确实无法找到你用户所谓的动态库,因为我gcc仅仅是一个编译器,用户使用选项-L告诉了你动态库的所在路径,使用选项-l(小写的l)告诉了你要去链接哪一个动态库,这个你指的是gcc这个编译器,而动态库最开始进行使用是要从磁盘中加载到物理内存中的,加载这个工作由谁做?加载器,加载器属于谁?加载器属于操作系统,所以说,我们还要告诉操作系统,需要将动态库加载进来,如何解决加载找不到动态库的方法?小编有四种方法可以进行处理

方法一

  1. 方法一:将动态库拷贝到系统默认的库目录路径下:/lib64 或者是 /usr/lib64 这里小编就以 /lib64为例进行讲解,那么接下来小编就将动态库拷贝到系统目录下,由于是向系统目录中进行写入,并且我们当前是普通用户,所以会出现权限问题,这时候我们sudo提权一下即可,当向系统目录下写入之后,此时操作系统就可以自动识别到我们要进行加载的动态库了,所以ldd此时查看用户的可执行程序链接的动态库就可以找到了,所以自然可执行程序也就可以进行运行了
    https://i-blog.csdnimg.cn/direct/7a6db9bc922f4cee978a0b66b1d504b4.png
  2. 那么为了方法一的结果不影响方法二的讲解,所以此时小编sudo提权一下,rm卸载一下系统的默认库文件路径中我们的动态库libmymethod.so,此时卸载了之后,ldd可执行程序,自然就无法找到对应的动态库libmymethod.so了,并且可执行程序也无法执行,此时不影响我们方法二的讲解了
    https://i-blog.csdnimg.cn/direct/cb6ae39947ba4726a3a755540289b0a7.png

方法二

  1. 方法二:在系统默认的库路径 /lib64 或 /usr/lib64 下建立软链接,那么接下来小编就sudo提权一下在系统目录/lib64下建立软链接,此时ldd此时查看用户的可执行程序链接的动态库就可以找到了,所以自然可执行程序也就可以进行运行了
    https://i-blog.csdnimg.cn/direct/40122042a7b943ad8211d77278831ebf.png
  2. 那么为了方法二的结果不影响方法三的讲解,所以此时小编sudo提权一下,unlink卸载一下系统的默认库文件路径中我们的软链接的动态库libmymethod.so,此时卸载了之后,ldd可执行程序,自然就无法找到对应的动态库libmymethod.so了,并且可执行程序也无法执行,此时不影响我们方法三的讲解了
    https://i-blog.csdnimg.cn/direct/181ee6300a8f456fa2cfab81f08c6b9b.png

方法三

  1. 方法三:将自己的库所在的路径,添加到系统的环境变量LD_LIBRARY_PATH中,环境变量LD_LIBRARY_PATH是操作系统专门提供给用户的,用于操作系统搜索的自定义的动态库路径的一个环境变量,将动态库的路径添加到环境变量LD_LIBRARY_PATH中,操作系统就可以找到并加载动态库,导入环境变量的指令为export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wzx/lesson/lesson28/test/mylib/lib,其中的export LD_LIBRARY_PATH=$LD_LIBRARY_PATH是为了保证环境变量LD_LIBRARY_PATH原有的路径不丢失,这样就可以在原有的路径的基础上添加新的路径了,此时ldd此时查看用户的可执行程序链接的动态库就可以找到了,所以自然可执行程序也就可以进行运行了
    https://i-blog.csdnimg.cn/direct/5c4edc840c6745d6b68dbd8bdab2e4fa.png
  2. 那么为了方法三的结果不影响方法四的讲解,很简单,我们关掉xshell重新启动即可,此时系统的环境变量LD_LIBRARY_PATH就恢复为原来的路径了,此时我们添加的路径就没有了,即我们的添加仅仅是暂时性添加,所以当我们重新启动xshell的时候,此时ldd可执行程序,自然就无法找到对应的动态库libmymethod.so了,并且可执行程序也无法执行,此时不影响我们方法四的讲解了
    https://i-blog.csdnimg.cn/direct/3834b605107a42f3a284c7910725ba02.pnghttps://i-blog.csdnimg.cn/direct/3c16c234ad3e4925b579a877035b620c.png

方法四

  1. 方法四:在/etc/ld.so.conf.d下新建自己的动态库路径的配置文件,新建完成后输入指令ldconfig进行刷新即可,由于是系统目录,并且我们的用户是普通用户,权限不够,所以su - 提升权限,使用root用户进行操作,那么进入对应目录之后,在路径下新建文件,写入我们的动态库的路径,再使用ldconfig进行刷新,此时右侧此时ldd此时查看用户的可执行程序链接的动态库就可以找到了,所以自然可执行程序也就可以进行运行了
    https://i-blog.csdnimg.cn/direct/f524b523e16e49e282aba8f14fee3245.png

https://i-blog.csdnimg.cn/direct/376d2d499846492ca8d4b558959e699f.png

  1. 此时方法四这个配置不同于方法三,方法三的配置是暂时的,对于方法三关掉或者重启xshell之后配置就无效了,对于方法四来讲,这个配置文件是永久的,只要不删除这个配置文件并且动态库的路径不改变,那么这个配置就永久有效
  2. 在实际的使用中,一般我们使用到的第三方库都是成熟的,采用的是直接安装到系统默认的头文件以及库文件目录下,所以实际使用中反而更简单,用户使用gcc通过-l(小写l)选项指定需要哪一个库即可

总结

以上就是今天的博客内容啦,希望对读者朋友们有帮助
水滴石穿,坚持就是胜利,读者朋友们可以点个关注
点赞收藏加关注,找到小编不迷路!