linux动静态库

Posted 可乐不解渴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux动静态库相关的知识,希望对你有一定的参考价值。

让春风化雨成诗,让光芒迎风展翅。

库是什么

本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。通俗的来说库是一组预先编译好的函数的集合,这些函数都是按照可重用的原则编写的。在库中的函数是相互关联的,通过这些函数的组成可以实现相应的功能。
而库又分两种:静态库与动态库。而动静态库的本质就是一堆 .o文件的集合。

我们知道头文件和源文件经过以下四个步骤才变成了可执行程序。
1、预处理
这个阶段要进行头文件的展开、去除注释、宏替换、以及条件编译等内容。我们利用gcc/g++-E选项编译我们写好一个 .c/.cpp文件, 最后会形成一点 .i文件。
示例: gcc -E hello.c -o hello.i
-E该选项的作用是让gcc/g++ 在预处理结束后停止编译过程。后紧跟的是要编译的文件名。
-o后紧跟的是要生成的文件名。

2、编译
在这个阶段中,gcc/g++ 首先要检查代码的规范性、检查是否有语法错误等工作,最后gcc/g++ 把代码翻译成汇编语言。我们可以利用 -S选项,将预处理阶段生成的 .i文件编译成**.s**文件。
示例: gcc -S hello.i -o hello.s

3、汇编
汇编阶段是将汇编指令(刚刚编译阶段转换好的汇编代码)转换成二进制指令,最后生成了 .o文件。
示例:gcc -c hello.s -o hello.o

4、链接
将生成的**.o**文件进行链接,最终形成可执行程序。
示例:gcc hello.o -o hello

故我们要将生成的一堆 .o文件进行打包,最后形成库。

在这之前我们可以利用ldd命令来查看该可执行程序利用的是动态库还是静态库。

静态库 .a

静态库的制作

首先我们创建头文件和对应的源文件,这里我们以add.cc、sub.cc、add.h、sub.h以及main.cc为例。(这里的 .cc.cpp指的都是c++的源文件)

  • 利用gcc/g++编译器将我们要编译的文件源文件形成.o文件,并将其打包至静态库(后缀为.a)的一个文件集合(lib)中。
  • 然后并将对应的头文件也打包进一个文件夹集合(include中)中。
  • 最后我们利用ar命令来制作静态库。
    下面我们利用makefile自动书写上面所说的过程:

    其中制作静态库的命令为ar,选项为rc
    -r:表示的是replace ,相当于如果我们的库中有文件更新了,那么我们就要对这个库进行更新。
    -c:表示的是create, 建立备存文件。

创建好静态库与后我们还可以利用ar命令来查看静态库的文件内容。
一定要注意是静态库,动态库是查看不到的。
指令:ar -tv 库名
-t: 列出静态库中的文件
-v: verbose 详细信息

静态库的使用

我们将我们最后含有main函数的main.cc源文件进行编译。
但此时直接利用 gcc/g++ 编译会出现错误,必须加入三个选项才能正常使用。
-I(大写i) 表示的是:告诉编译器你把头文件放在了那里。
-L 表示的是:告诉编译器你把库文件放在了哪里。
-l(小写的L) 表示的是:链接那一个库,去掉前缀lib和后缀.a就是库的名称。
这样编译器就可以找到这个静态库了。在上面我们的我们的就为cal。

用g++编译好后我们就形成了一个名为main的可执行程序,我们去执行这个可执行程序,如下图所示:

动态库 .so

动态库的制作

制作动态库同样需要对应的源文件和头文件,这里我们同样以add.cc、sub.cc、add.h、sub.h以及main.cc为例。我们先创建好后,此时操作和静态库有点不一样。

  • 我们要先利用gcc或g++生成.o文件,但生成.o文件前需要添加一个选项
    -fPIC(后面三个都是大写的字母) -fPIC是告诉编译器让它产生位置无关码(position independent code), 作用于编译阶段, 则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。如下图所示:
  • 之后我们再次利用gcc/g++来得到动态库(后缀名为.so)
    -shared:表示的是生成动态共享库的意思,利用.o文件来生成libcal.so文件

    生成完动态库后,我们将所有的头文件放入到mylib中的include目录下,动态库放入到mylib下的lib目录下,如下图所示:

动态库的使用

之后我们利用与静态库相同的操作来生成可执行程序(此时动态库是去点前缀lib,去掉后缀.so就是我们的动态库的库名),并去运行该可执行程序,却发现运行不起来。

这是为什么呢?
在上面我们说到上面生成的可执行我们只让编译器找到了这个动态库,但我们去让这个可执行程序运行起来的时候就变成了进程,变成了进程就一定要将当前的代码加载到内存,但是加载的时候和它同步关联的动态库OS 却找不到这个动态库,怎么证明呢?我们可以利用ldd来查看一下

那该怎么办呢?
这里有三种方法如下:

  • 1、拷贝**.so文件到系统共享库路径下, 一般指/usr/lib** (不推荐,会污染别人写好的库)
  • 2、更改 LD_LIBRARY_PATH
    这个LD_LIBRARY_PATH代表的是程序运行时动态查找库时搜索的路径。

    其中我们可以利用echo指令来查看这个环境变量,我们会发现有一个东西,但该东西是我之前配置vim形成的。如果其中没有内容也是正常的现象。

    此时我们在利用ldd指令就可以发现OS就能找到该动态库文件了。

    最后我们就可以去执行该可执行程序了。

    如果不想要我们添加的动态库的路径时,只需要重新登录下Xshell或者重新导入一遍之前LD_LIBRARY_PATH的即可。
  • 3、ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新 (用的较少)

动静态库的区别

静态库:
静态库是程序在编译链接的时候把库的代码复制到可执行文件当中的,生成的可执行程序在运行的时候将不再需要静态库,因此使用静态库生成的可执行程序的大小一般比较大。

**缺点:**可执行程序大小占用比较大的内存空间。
**优点:**使用静态库生成可执行程序后,该可执行程序就可以独自运行,不再需要库了。
动态库:
动态库即动态链接库(Windows 下的 .dll,Linux 下的 .so)。与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。 动态库的优点是,不需要拷贝到目标程序中,不会影响目标程序的体积,而且同一份库可以被多个程序使用(因为这个原因,动态库也被称作共享库)。同时,编译时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行。
缺点: 依赖动态库,如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行。
**优点:**占用内存空间小。

注意: 要注意动态库是在程序运行的时候采取链接动态库的代码,但是静态库程序在编译链接的时候就把库的代码链接到可执行文件当中。

以上是关于linux动静态库的主要内容,如果未能解决你的问题,请参考以下文章

linux入门动静态库

linux入门动静态库

Linux动静态库以及动静态链接

Linux编译过程与动静态库制作

Linux下的动态库和静态库详解

Linux篇第十篇——动静态库(动静态库的介绍+动静态库的打包与使用)