我们通过stat指令查看文件信息:
[lyl@VM-4-3-centos 2022-3-14]$ stat log.txt
File: ‘log.txt’
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fd01h/64769d Inode: 790871 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1001/ lyl) Gid: ( 1001/ lyl)
Access: 2022-03-13 22:18:13.571644848 +0800
Modify: 2022-03-13 22:18:13.571644848 +0800
Change: 2022-03-13 22:18:13.571644848 +0800
Birth: -
可以看到在文件信息下有3个时间,这三个时间分别代表着:
那么这三个时间有什么作用呢? 我们在使用自动化构建工具Makefile时,如果连续make会发现:
这便是由Modify与Change两个时间决定的,若Modify时间不早于Change,则gcc指令可以执行,否则会显示此时mytest is up 同date.
我们在实际开发中,经常要使用别人已经实现好的功能,这是为了开发效率和鲁棒性(健壮性);因为那些功能都是顶尖的工程师已经写好的,并且已经践行多年的代码。 那么如何使用他人开发的功能呢?可以使用:1.库,包括静态库与动态库。2.开源代码。3.基本的网络功能调用,比如各种网络接口、语音识别等等。 这其中,我们将详细介绍静态库和动态库。
一般情况下,为了更好的支持开发,第三方库或者是语言库,都必须提供静态库和动态库,这是方便程序员根据需要进行bin(二进制文件)的生成。
动态库是动态链接生成的,而静态库是静态链接生成的。
一般来说,我们直接gcc编译默认是动态链接的而如果加上-static选项,那么生成的可执行文件将为静态生成的
在使用-static选项时可能出现yum -y install glibc-static
的报错,这其实是静态链接时没有找到libc.a。使用yum -y install glibc-static
指令安装即可解决问题。
可以很明显的看到动态链接的文件大小明显要比静态链接的文件大小要小多了,这是为什么呢? 其实,动态链接是当执行到要调用的接口时,编译器会自动去搜寻所链接的库,而静态链接则是暴力的将所要用的库中可执行程序使用的二进制代码全部拷贝到我们生成的可执行文件中,这也就是为什么静态链接生成的文件这么大的原因了。
一般的命名方式为lib+库的名字+.a
比如C语言提供的标准静态库名字就是libc.a
。
静态库是指程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
而动态库则是指程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
动态库被加载在内存中,可以供多个使用库的程序共享映射到自己的虚拟地址空间使用,因此可以减少页面交换以及降低内存中代码冗余,并且因为与源程序模块分离,因此开发模式比较好。 而加载动态库的程序运行速度相对较慢,因为动态库运行时加载,映射到虚拟地址空间后需要重新根据映射起始地址计算函数/变量地址。 静态库则与之相反,其运行速度相对较快,但消耗资源较多。
我们为什么会制作库呢?一般是想让别人能够使用我们实现的功能,但又不暴露自己的源代码才会打包库。那么接下来我们来学习如何打包静态库。
由于生成静态库需要先生成目标文件(.o)再进行打包,故先编写相应的源文件再将其编译成目标文件:
[lyl@VM-4-3-centos 2022-3-14]$ gcc -c add.c -o add.o #生成目标文件
[lyl@VM-4-3-centos 2022-3-14]$ gcc -c sub.c -o sub.o #生成目标文件
此时的add.o和sub.o文件是已经编译好但还没有链接的两个文件,此时再用 ar命令将其打包成静态库:
[lyl@VM-4-3-centos 2022-3-14]$ ar -rc libmycal.a add.o sub.o #打包成静态库
这里的ar
是gnu归档工具,rc
表示(replace and create)。
[lyl@VM-4-3-centos 2022-3-14]$ ar -tv libmycal.a #查看静态库的目录列表
rw-rw-r-- 1001/1001 1240 Mar 14 11:11 2022 add.o
rw-rw-r-- 1001/1001 1240 Mar 14 11:11 2022 sub.o
[lyl@VM-4-3-centos 2022-3-14]$ cp ./*.h ./lib #将头文件复制到lib目录下(提供函数入口地址,使用接口的前提) [lyl@VM-4-3-centos 2022-3-14]$ cp ./libmycal.a ./lib # 将静态库复制到lib目录下
既然已经打包好了静态库,让我们包一下头文件来调用我们实现的接口:
#include <stdio.h>
#include "add.h"
#include "sub.h"
int main()
{
int a = 10;
int b = 20;
printf("a+b:%d\n", Add(a, b));
printf("a-b:%d\n", Sub(a, b));
return 0;
}
发现代码编译不过去,报错说函数Add及Sub未定义,我们明明已经实现了接口并打包成静态库了,这是为什么呢? 其实gcc编译时去链接库和头文件是去默认路径以及当前路径寻找,而我们将静态库打包到lib目录下,gcc编译时就找不到我们的库了,所以我们需要加一些选项来告知gcc去寻找指定路径的库及头文件。 gcc寻找的默认路径:
/usr/include
因此,正确链接的指令为:
gcc -o main main.c -I ./lib -L ./lib -lmycal -static
其中:
此时我们删除静态库libmycal.a,再运行main程序,发现程序照样可以运行。
学习完生成和使用静态库后,下面我们来生成一下动态库。
在这里,我们将生成动态库的依赖关系及方法写进自动化构建工具中:
需要注意的是:
$@
也可以此时make就制作好了动态库:
和静态库使用一样带上三个选项打包动态库:
这里我们在运行程序时可能会报错:
error while loading shared libraries
cannot open shared object file: No such file or directory
这是因为,此时的可执行程序已经与编译器无关了,这属于运行问题,运行时系统也需要找到我们所使用的库,但在默认路径下没有我们的库。
这里解决方法有多种,但我倾向于推荐下面这一种:
修改环境变量LD_LIBRARY_PATH
,将动态库所在路径添加到该环境变量中,这样程序在运行时系统就能够找到动态库,从而运行成功。
[lyl@VM-4-3-centos dynamic]$ echo $LD_LIBRARY_PATH
:/home/lyl/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
[lyl@VM-4-3-centos dynamic]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/lyl/2022-3-14/dynamic/lib
[lyl@VM-4-3-centos dynamic]$ echo $LD_LIBRARY_PATH
:/home/lyl/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/lyl/2022-3-14/dynamic/lib
当然,让系统找到我们的动态库还可以拷贝.so文件到系统共享库路径下, 一般指/usr/lib。但是这可能会污染系统原生的库,一般不推荐这样做。 其次,还有一种方法,在我们的系统下有/etc/ld.so.conf.d/这个路径:
[lyl@VM-4-3-centos dynamic]$ ls /etc/ld.so.conf.d/
bind-export-x86_64.conf dyninst-x86_64.conf hcoll.conf kernel-3.10.0-1160.11.1.el7.x86_64.conf mariadb-x86_64.conf mxm.conf sharp.conf
我们可以把库所在的路径写进这个路径当中,可以在这个路径下制造自己的.conf,然后再将库路径写进这个conf中:
echo "/home/lyl/2022-3-14/dynamic" > /etc/ld.so.conf.d/lyl.conf
当然这个方法也不推荐,毕竟可能污染库的头文件和库。
好了,动态库和静态库的全部内容至此介绍完毕。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。