回顾, 站在库的制作者角度
我们自己设计一个静态库
现在我们想把我们刚刚写的这个方法给别人使用
我们有两种选法:
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的