Linux中的动静态库

回顾, 站在库的制作者角度

我们自己设计一个静态库

在这里插入图片描述
现在我们想把我们刚刚写的这个方法给别人使用
我们有两种选法:
1 我把源文件直接给他
2 我把源代码想办法打包成库发给他库 + .h头文件

在这里插入图片描述
我们先生成一个库
在这里插入图片描述

可以看到我们的库已经生成了。
接下来我们站在使用者的角度, 来使用一下我们这个静态库。

我们自己设计一个动态库

在这里插入图片描述
我们创建myprint.h 和 myprint.c
在这里插入图片描述
然后再创建mylog.h 和 mylog.c

动态链接和静态链接的思路大致是一样的, 就是先将.c都编译成.o文件, 然后将.o文件打包就好了;

在这里插入图片描述
具体这些选项什么含义我们后面再讲, 值得一提的是:
动态库的打包是gcc内置了的, 而静态库的打包是需要借助ar来实现的;
在这里插入图片描述
编写进makefile里就是这样的。
现在我们来使用一下试试

站在库的使用者的角度

使用静态库

我们来使用一下我们刚刚创建的静态库
在这里插入图片描述
在这里插入图片描述

直接编译, 发现编译器提醒我们找不到该头文件。

因为我们gcc在找头文件的时候, 如果你的头文件不带路径, 那么就会直接在他自己设定的一个目录里和当前文件在的目录查找, 我们可以在gcc编译的是后带上-I选项, 指明在那个目录下去找
在这里插入图片描述
我们指明了后发现报错变了, 说明是成功的找到了头文件, 但是通过这个报错我们可以看到是没有找到add的定义, 说明是在连接的时候出错了。

这是因为我们gcc只会去他自己设定的目录下去找库连接, 比如c的标准库。所以我们这里使用自己的库是需要带-L选项来自己指定路径;

在这里插入图片描述

我们发现, 即使指定了路径, 还是无法成功编译这是为什么呢?

这是因为, 即使我们知道了库的路径, 但是这个目录下可能有多个文件, gcc不知道该链接哪个
所以我们需要使用-l选项来指明要连接哪个库

在这里插入图片描述

我们发现, 我什么都指明了为什么还是编译不了呢?

这是因为库的名字应该是去掉前后缀的所以我们编写的库的名字其实是mymath
在这里插入图片描述

此时我们就成功的编译然后正常运行了。

但是我们平时使用的时候没有用这么多的选项啊?
这是因为gcc和g++有自己默认的地方去找头文件, 去链接库, 但是如果我们要连接第三方库的话, 就得带上这些选项来制定我们要在哪里找

如果我们不想这么麻烦呢?
第一种方法是:
可以把我们用的第三方的头文件拷贝到系统的include目录里
把我们使用的第三方的库拷贝到系统的lib64目录下
这样的话只需要用-l来指明我们要连接哪个库即可

不用在意, 只需要知道还有建立软链接的方法, 具体操作后面会讲的。
**
或者说我们可以在库里面对头文件做软链接, 把软链接放在系统当中, 对于链接对应的库, 我们把这个库当前所在的路径连接在系统的lib64下也建立软链接把软链接放在系统目录下, 此时我们也不用带这些臃肿的选项了, 这个我们后面再说。
**

在这里插入图片描述
在这里插入图片描述
如果将我们写的头文件和库都放在系统默认的目录里, 我们就可以不带-I 和 -L选项了
这两步 其实就是在做库的安装

在这里插入图片描述
除了这种方法, 还有方法能实现这样吗? 当然还有

我们在系统目录下建立一个我们自己头文件的软链接在这里插入图片描述
在这里插入图片描述

然后再把我们的头文件该为这个, 因为软链接其实就是路径嘛, 所以这本质上其实就是把我们自己写的头文件的路径写了上去;

在这里插入图片描述
我们在系统目录下建立我们静态库的软链接

在这里插入图片描述

结论

1 在使用第三方库的时候, 必定是要带上-l选项的
2 深刻理解errno的本质(其实就是个全局变量)
3 如果系统中只提供静态库, gcc则只能对该库进行静态链接
4 如果系统中需要链接多个库, gcc可以链接多个库。

使用动态库

在这里插入图片描述
我们使用这段代码来调用我们两个库里的函数

在这里插入图片描述
我们在使用gcc编译的时候使用两次-l指明要连接的两个库即可编译出对应的可执行程序

在这里插入图片描述

但是当我们去执行的时候, 却发现执行不了, 这是为什么呢?

在这里插入图片描述
可是我们在编译的时候, 不是已经告诉他了库的位置了吗?
为什么他还会找不到呢?

这是因为我们告诉的是编译器, 但是这里是动态库在加载的时候找不到, 所以这个库在哪里我们还得告诉系统 -- 加载器

怎么告诉系统呢?
1 直接将我们的动态库拷贝进系统默认路径下;
2 我们建立软链接放在系统默认路径(/lib64或/usr/lib64)下
在这里插入图片描述
在这里插入图片描述
可以发现已经是可以成功运行了

3 往LD_LIBRARY_PATH环境变量里将我们库的地址添加进去即可
在这里插入图片描述
4 在/etc/ld.so.conf.d下建立自己的动态库路径配置文件, 然后重新ldconfig即可

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

动态库是怎么被加载的

在这里插入图片描述
我们的系统中会同时存在多个动态库, 他们一样被操作系统先描述再组织, 最后对这些动态库的管理就变成对链表的增删查改;

动态库是怎么做到被所有的进程共享的呢?

在这里插入图片描述
在这里插入图片描述
初步结论:

建立映射关系后, 我们执行的任何代码, 都是在我们的进程地址空间中进行执行的。

后面如果有新的进程想要链接这个库, 操作系统会先识别这个库是否被加载到了内存里, 然后再根据是否已经加载, 决定是否要将库加载到内存, 最后将该进程通过页表将共享区和库的物理地址做好映射即可;

但是到了这里就有了个问题, 我们的库里面有时候会有一些全局变量如errno 那么我们多个进程在用这个一个库的时候, 如果更改了这个errno是不是就会对别的进程造成影响呢?

答案是不会对别的进程造成影响, 因为我们在写入的时候会发生写实拷贝
因为errno对应的块的引用计数是大于1的

  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值