今天我们来讲动静态库!
首先我们来粗粒度的划分一下动态库和静态库。
动态库就是只有一份库文件,所有想用该库的文件与改库文件建立链接,然后使用。这样可以提高代码复用率,避免重复拷贝产生没必要的内存消耗。
静态库,就是那个文件想用,就直接把库文件的内容拷贝过去,这样可以减少出现文件丢失的情况,略微提高运行速度,但是会造成巨大的空间消耗,大多数情况我们都选择动态库!
我们来通过写一个简单的计算器去探究动静态库。
首先我们看一下相关文件,至于具体的计算器实现代码太简单就不摆了。
这样我们得到所有的.o文件以后给别人就只需要把.o文件和代码丢给别人就可以了。
不需要一大堆之前的头文件,源文件了。
于是得到我们的第一个结论
然后我们再来学习静态库的生成。
然后如果我们直接链接就会报一堆错误!
这是因为gcc默认不认识第三方库文件,所以我们必须要指明文件!
但是我们发现仍然不行,原因是其真正的名字必须去掉lib和.a才行
然后-L.是表示在什么目录下(如果不加报错就加上)
-l是用来指明库名字。
如果是c标准库就不用加这些东西,所以我们以前从来没有加上过这些东西。
但是如果这个时候我们查ldd,发现查不到。也即ldd查的就是动态库的信息,静态库一单生成以后就在一起了,没有办法查了。
也就是gcc后面如果不加static那么静态库就直接静态链接,如果能动态链接就会动态链接。
加上以后则全部静态链接,否则直接报错!
然后为了方便我们以后交程序给别人,所以我们可以创建include,lib文件夹分别存放头文件和静态库!并且把相关文件拷贝进去。
就可以得到下面的库形式。
然后这个时候再打包的时候再加上-I也即告诉gcc找头文件不仅要在当前目录找,系统目录找,也要到指定的目录进行寻找!!!
同理我们还要包括库文件,于是还要加上后面的语句去链接相关的库文件!
接下来我们来制作动态库!
与制作静态库的方法不同点是要加上-fPIC选项
具体含义后面解释!
然后shared含义也即把所有.o文件打包形成.so文件,但不是可执行程序!
在静态库的基础上进行修改即可!
然后正常进行链接即可,目前和静态库的使用没有任何区别。
但是如果我们想运行就会报错
其原因就是因为只告诉了编译器动态库在哪里,但是没有告诉操作系统动态库在哪里。(操作系统默认只在默认位置进行查找)
所有方法一:
然后我们直接添加头文件和库文件到操作系统默认位置,然后指明库进行链接即可!!!
方法二:建立软链接
我们在当前目录下建立一个软链接指向我们的库
同理我们也可以在我们的默认路径加上软连接也可以!
方法三:添加到环境变量里面
操作系统还会默认到下面环境变量包括的路径里面找!!!
方法四:直接更改系统配置文件
里面只要包含我们动态库的位置就可以了!!!
基本上这些方法就已经非常够用了,就不再多说!
如果我们就想连接静态库,那就加上-static
接下来我们讲如何使用外部库!
比如我们接下来就测试链接一个ncurses的库!
比如此处我就找了一个贪吃蛇的库,我们链接之后就可以通过了!!!
然后我们就可以正常使用了!
以上就是如何使用别人的库!
接下来我们来讲动态库加载原理!!
在Linux系统下可执行程序的格式是ELF
1:首先我们要知道库和程序必须全部加载!
2:程序没有加载的时候也有地址!!!
首先我们要知道在编译成二进制的时候没有变量名和函数名了,而会被直接替换成地址!
而这些名字只是用来给人看的!机器是不看这些名字的!
所有程序在磁盘的时候已经具有了虚拟地址(也叫逻辑地址)!
一般是基地址加偏移量的方式。(0~FFFFFFFF)
调用函数的时候就是直接调用虚拟地址!
这种模式在Linux就被称为平坦模式!(编址过程中也是规则编址,方便使用)
于是这样内部函数的相对位置不变,加载到内存以后只要知道入口地址,那么就可以通过偏移量进行使用相应的函数和变量了!!!!!!!!
所有也即与位置无关码!
3:动态库加载到内存并且映射到共享区
这样库在内存就可以只加载一份了!!就可以大大节约空间了!
并且由于我们不能把指定加载到指定空间,所以我们需要
并且我们在可执行程序中调用函数的时候我们也只需要记录库地址和方法的偏移量即可!!
所以我们只需要库加载之后,位置是确定的,我们之后就可以正常加载了!!!
只要库的位置不再改变!
于是我们就可以得出一个结论,我们在函数跳转的时候就是在地址之间跳转而已!
只不过平时我们只在正文处跳转,调用库函数的时候就要跳转到共享区而已!
至此,动静态库的内容也基本到尾!谢谢观看!