文章目录
一、动静态库的基本介绍
在Linux操作系统中静态库文件是以 .a 作为后缀的,动态库以 .so 作为后缀。
- 静态库(.a):静态库的代码是在程序编译链接的时候就被链接到可执行文件当中的,在程序运行的时候就不需要静态库代码了。
- 动态库(.so):动态库的代码是程序运行的时候才去链接的,多个程序可以共享使用动态库的代码。
- 一个动态库链接的可执行文件只包含这个可执行文件用到的动态库内函数入口地址的一个表,而不是该函数所在目标文件的整个机器码。也就是说,当可执行文件用到动态库的某个函数时,链接到可执行文件当中的只有这些函数入口的地址,并没有这些函数的具体实现方法。
- 动态链接是指在可执行文件运行之前,操作系统就会将该可执行文件运用到的动态库函数的具体实现代码从磁盘加载到内存里。
- 静态链接是在可执行文件编译链接时就将静态库拷贝到可执行文件当中。
- 动静态库的命名方式为 lib + 库名 + 后缀 ,例如libxxx.a表示静态库,libxxx.so表示动态库。
二、静态库
1.生成静态库
在写C++的STL模拟实现的时候,我们经常会定义三个文件,一个(.h)头文件用来放类和函数的声明,一个(.cpp)源文件来放函数的定义,一个(.cpp)文件来写main函数做测试或者使用类。我们可以举一个简单的例子来看一下平时我们是怎么做的。
创建mymath.h头文件用来声明addToVal函数:
创建mymath.c文件用来写addToVal函数的具体实现方法:
创建test.c文件写main函数,测试addToVal函数:
然后我们创建makefile文件,平常我们就是用这种方式来生成可执行文件的:
我们现在不想像以前那样生成可执行文件了,我们想要把头文件和源文件打包成一个静态库,我们在原有的mymath.h头文件和mymath.c源文件基础上,再增加myprint.h和myprint.c文件,把这四个文件打包成一个静态库。
首先我们要将(.c)源文件变成 .o 文件,输入指令gcc -c mymath.c
、gcc -c myprint.c
,生成了对应的 .o 文件。
链接就是将所有的 .o 文件链接形成一个可执行程序。如果我们不打包,直接将所有的 .o 文件给别人,别人也可以用来生成可执行程序,例如:我们创建一个mytest.c文件作为测试代码调用myprint.h的函数,然后输入指令gcc -c mytest.c
先生成自己的 .o 文件,然后输入指令gcc mymath.o myprint.o mytest.o
,就可以生成一个可执行程序。
因此在链接的时候,其实就是将上面的所有 .o 文件合起来,拷贝到一起形成最后的可执行程序,这也就是所谓的静态链接。但是有个问题是,如果我们的 .o 文件有很多,我们每次生成可执行程序都要讲所有的 .o 文件写出来,这样会非常麻烦。所以我们要将 .o 文件进行打包形成静态库,就可以解决这个问题了。
打包成静态库我们需要用到 ar -rc命令。例如我们在makefile文件中生成静态库,使用指令ar -rc libmymath.a mymath.o myprint.o
我们可以使用make指令生成一下,我们会发现目录中出现了(.a)静态库和所有的(.o)文件,至此我们就完成了静态库的打包操作。
2.发布静态库
我们生成了静态库要怎么发布出去给别人能够使用呢?我们在使用一个库的时候,必须拥有该库的库文件以及头文件,其中库文件就是我们刚刚生成的(.a)静态库文件,头文件就是后缀为 .h 的文件,我们可以将这两种文件复制到一个目录下,最后将这个目录发布出去就可以了。
我们在makefile文件里再添加一个static_ouput的伪目标,表示静态库的发布命令,执行该命令的时候就会创建lib_static目录以及该目录下的lib目录和include目录,然后将所有的 .a 文件复制到lib目录下,将所有的 .h 文件复制到include目录下。
输入指令make static_ouput
查看结果:我们可以看到lib_static目录确实被创建出来了,有了这个目录别人就可以使用我们打包好的静态库了。
4.使用静态库
当我们使用别人制作好的静态库时,我们拿到静态库,要在代码中使用,首先要拿到该静态库的头文件,引入相应的头文件即可。编译器寻找头文件的方式有两种:
- 在当前路径下寻找头文件。
- 在系统库文件路径下寻找头文件。
Linux系统库文件的头文件路径在usr/include
,系统的下载好的库文件一般存放在/lib64/
这个路径下。而当我们将打包好的静态库的目录放到当前目录下时,我们需要的头文件并不在当前目录下,而是在lib_static/include
路径下。
(1)带路径的头文件
所以我们创建的mytest.c文件想要使用静态库时,需要带上路径包头文件:
但是这种方法太麻烦了,写头文件的时候带的路径太长了,我们希望用简单方便一点的方法。
(2)将库文件和头文件拷贝到系统路径下
第一种简单粗暴的方法就是:将静态库的头文件和库文件拷贝到对应的系统路径中去。
输入指令sudo cp lib_static/include/* /usr/include/
将库的头文件拷贝到系统库的头文件路径下,输入指令sudo cp -rf lib_static/lib/* /lib64/
将库文件拷贝到系统库文件路径下。然后我们就可以在mytest.c里不带路径地包头文件,我们再输入指令gcc mytest.c
生成可执行程序。
但是我们却发现依旧是无法生成可执行程序,只不过现在报错就不再是找不到头文件了,而是出现了链接错误,找不到函数。这是因为gcc或者g++编译器默认会链接C/C++语言的库文件,但我们这种第三方库gcc和g++编译器是不认识的。
所以我们在编译的时候需要加上-l库文件名这个选项,库文件名就是去掉前缀lib和去掉后缀剩下的部分,例如我们的库文件,输入指令gcc mytest.c -lmymath
。这样我们就可以生成可执行程序了。
我们上述的将库文件和头文件拷贝到系统路径下的做法就叫作库的安装。
(3)指定头文件和库文件的搜索路径
既然我们是gcc或者g++编译器去寻找头文件的,那么编译器还提供了可以指定头文件搜索路径的选项。这个选项是-I 头文件的搜索路径,或者是-I头文件的搜索路径(意思就是-I后面可以带空格也可以不带)。这样就可以手动地指定头文件的搜索路径。
头文件搜索路径指定了以后,还需要告诉编译器库文件的路径,否则会有链接错误。所以还需要使用选项-L 库文件搜索路径,这样就可以指定库文件的搜索路径。
最后,我们还需要告诉编译器在库文件的搜索路径下需要链接哪一个库文件(因为可能存在多个库文件在同一路径下的情况),所以使用选项-l库文件名,即可链接库文件。
输入指令gcc mytest.c -I ./lib_static/include/ -L ./lib_static/lib/ -lmymath
即可生成可执行文件。
三、动态库
1.生成动态库
动态库和静态库一样也是要先形成 .o 文件,但是动态库需要形成与位置无关的 ** .o** 文件,所以需要加上-fPIC
选项,输入指令gcc -fPIC -c mymath.c -o mymath.o
、gcc -fPIC -c myprint.c -o myprint.o
生成 .o 文件,然后输入指令gcc -shared -o libmymath.so mymath.o myprint.o
将所有的 .o 文件打包。
2.发布动态库
发布动态库和静态库的操作方法是一样的:
3.使用动态库
使用动态库的方法和静态库一样,要么是使用带路径的头文件,要么是将库文件和头文件拷贝到系统路径下,要么是指定头文件和库文件的搜索路径。前两种方法都不太经常使用,下面演示一下第三种方法。输入指令:gcc mytest.c -I ./dyl/include/ -L ./dyl/lib/ -lmymath
,即可生成可执行文件。
虽然是生成了可执行文件,但是与静态库不同的是,我们直接运行这个可执行文件会报错。它报错的是动态库文件没有找到。
我们再输入指令ldd a.out
查看程序依赖的动态库文件,发现我们自己写的库文件没有被找到。
这是因为我们前面gcc带的选项让编译器找到了库头文件和库文件的搜索路径从而生成了可执行程序,但是动态链接不像静态链接那样在编译器链接的时候就已经将代码拷贝进了可执行文件当中,而是在程序运行的时候也要依赖动态库,所以我们还需要让可执行程序在运行的时候能够找到我们的动态库。当然如果我们直接将库安装到系统路径下,这些问题都可以解决。如果不安装到系统路径下,可以使用下面这几种方法:
(1)导入环境变量
我们输入指令echo $LD_LIBRARY_PATH
查看这个环境变量:
然后我们将我们库文件路径导入进去,注意要使用绝对路径,并且只需要导入库文件的路径,不用导入头文件的路径。
- 输入指令
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/hkx/study23.01.17/linux_blog/mklib/test/dyl/lib
- 导入进去之后再输入指令
echo $LD_LIBRARY_PATH
查看这个环境变量:环境变量导入成功。
最后再运行可执行程序,此时就可以成功地运行了。但是这种方法有一个不好的点就是,当我们关闭xshell,再次进入的时候,环境变量又被清空了。
(2)添加系统配置文件
Linux操作系统下有一个路径是存放配置文件的,输入指令ls /etc/ld.so.conf.d/
可以查看一下这个路径下的配置文件:
我们要做的也是非常简单,在系统的这个路径下创建一个配置文件,配置文件里写上我们动态库的库文件存在路径即可。
- 输入指令
sudo touch /etc/ld.so.conf.d/mymath.conf
创建一个我们自己的配置文件 - 输入指令
sudo vim /etc/ld.so.conf.d/mymath.conf
编辑我们的配置文件,将我们的库文件路径写到配置文件中 - 最后输入指令
sudo ldconfig
让我们的配置文件生效即可
此时,我们可以输入指令ldd a.out
查看一下我们的可执行文件依赖的库文件:现在就可以找到库文件的路径了。
最后运行程序,可以成功运行了。
(3)在系统库文件路径下建立软链接
如果前面两种方法都觉得特别复杂,可以尝试一下第三种方法。我们在lib64路径下建立一个软链接,链接上我们写的库文件的路径即可。输入指令sudo ln -s /home/hkx/study23.01.17/linux_blog/mklib/test/dyl/lib/libmymath.so /lib64/libmymath.so
即可创建软链接。这样也可以正常运行可执行程序。
四、一个makefile文件一次生成多个目标文件
这个是非常常用到的,我们要定义一个伪目标为all,将我们想要同时生成的目标文件写在一起即可,例如我们想要一次性生成一个动态库和一个静态库,就可以将makefile文件改写成下面的样子: