1.一些与本章标题无关的补充内容
当我们修改文件内容的时候,有可能会修改文件的属性。比如:可能会更改文件的大小属性。
我们在通过指令查看文件的时间相关属性时会往往观察到这样的现象:Access(访问时间,之前有具体讲过)没有及时修改。
在较新的linux中,Access时间往往不会立刻更新,经过一定的时间间隔OS才会更新。
当然既然提到了这个时间属性,这里便在补充一点:当我们使用makefile的时候其,你所要make
的文件能否被编译成可执行文件就是看modify时间有没有发生改变。
2.动静态库的基本概念
2.1linux下动静态库的基础认知
静态库是一种包含多个目标文件(Object Files)的归档文件,这些目标文件在链接阶段被合并到最终的可执行文件中。静态库的文件扩展名通常为.a
,例如libexample.a
。
动态库是一种在程序运行时动态加载的库,它们在程序启动时或运行时按需加载。动态库的文件扩展名通常为.so
(例如libexample.so
),或者带有版本号(例如libexample.so.1.0
)。
2.2静态编译和动态编译
静态编译是指将所有需要的库都打包进可执行文件中,这样在运行时就不需要依赖任何外部库。在静态编译的情况下,可执行文件的体积会比较大,但是可以保证程序的可移植性和稳定性。此外,静态编译还可以避免由于共享库版本不一致或缺失而导致的兼容性问题。
动态编译是指在运行程序时,程序需要依赖一些共享库才能正常工作,这些共享库通常是系统已经安装好的动态链接库(.so文件)。在动态编译的情况下,程序只需要编译链接自己的代码,而不需要将所有依赖的共享库都打包进可执行文件中。这样可以节省空间,同时也方便了共享库的更新和维护。
在我们之前学过的makefile中加入-static可以静态编译,不过一般的服务器可能没有内置语言的静态库,而只有动态库。
2.3linux下库的命名方式
在 Linux 下,库有三种常见的名称(上图为完整名称):
- 完整名称(Full name):以 lib 开头,以 .so 结尾,比如 libc.so、libstdc++.so 等。它通常包含库的版本信息和其他细节,这是库文件的实际名称。
- 简称(Short name)或 soname:去掉 lib 前缀和 .so 后缀(版本信息也去掉)的名称,比如 c、stdc++ 等。这通常用于编译和链接时指定库。
- 链接名称(Link name):是指库的软链接或硬链接的名称。比如 libc.so 的一个链接可能叫做 libc-2.28.so。
3.库的制作
3.1一些小思考
库的本质就是一堆二进制函数,可我们如何得知一个库给我们提供了什么方法?
首先一套完整的库包括以下几个成分:
1.库文件本身
2.头文件.h(会说明库中暴露出来方法的基本使用)
3.说明文档
在我们以前的项目中我们都习惯把函数的实现和声明分开写,这样可以方便我们制作库,并且保证私密性。
3.2makefile中实现静态库
如图:
在linux同一目录下创建5个文档(红色字为文档名)
当使用gcc编译mytest.c时,我们需要输入以下指令(gcc只看得到声明,看不到实现):
若我们想将自己的代码给别人使用,按照上面的方法即可(给予原文件以及头文件)
如果我不想这么做可以将其打包成静态库(原理就是提供.o文件打包成库)
下面提供两种写法:
第一种(不推荐):
-
obj = mytest.o add.o sub.o
:这一行定义了一个变量 obj,它包含了需要编译的对象文件的名称。 -
mytest: $(obj)
:这一行指定了可执行文件 mytest 依赖于变量 obj 中列出的所有对象文件。如果 obj 中任何一个对象文件发生更改,那么就需要重新编译 mytest。 -
gcc -o $@ $^
:这一行是构建可执行文件 mytest 的命令。它使用 gcc 编译器,将所有依赖项(在这里是 obj 中列出的对象文件)链接在一起,并将输出文件命名为 $@(在这里是 mytest)。 -
%.o: %.c
:%代表匹配任意长度的字符串。所以这个规则的意思是:任意名称的目标文件(以.o结尾),依赖于同名的源文件(以.c结尾)。 -
gcc -c $<
:这一行是构建对象文件的命令。使用gcc编译器编译依赖项(%.c文件),生成目标文件(%.o文件),但不进行链接。make之后的实际效果图:
第二种:
libmymath.a:sub.o add.o //库名为libmymath.a
ar -rc $@ $^ //使用 ar 工具,执行 -rc 选项来创建或更新库文件,并将所有依赖项(在这里是 sub.o 和 add.o)添加到库中。
%.o:%.c
gcc -c $<
(ar -tv 静态库名 可查看静态库)
完整形态:
libmymath.a:sub.o add.o
ar -rc $@ $^
%.o:%.c
gcc -c $<
.PHONY:clean
clean:
rm -rf *.o output libmymath.a
.PHONY:output
output:
mkdir output
cp -rf *.h output
cp -rf libmymath.a output
.PHONY:install //安装到某处
install:
cp *.h /user/include
cp libmymath.a/lib64
将output文件创建好后,重新命名成lib(此时的lib和mytest.c都在lesson2文件下)
输入如图指令:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXv7Py52-1685974570404)(C:\Users\山野村居\Documents\Tencent Files\1641229318\FileRecv\MobileFile\3DDEB61892F295752B6DC9A63B8676EB.png)]
这里为什么需要指明这些选项,我们正常写代码为什么编译时不需要指明这些选项?
之前的库,在系统的默认路径下:/lib64,/user/lib,/user/include等编译器是能识别这些路径的。
换句话说如果我不想带这些选项,可以将这些东西拷贝到默认路径下,不过一般不推荐这么做
(上面的这种过程其实就是一般软件的安装过程 )
3.3makefile中形成动态库
#形成一个动态链接的共享库
libmytham.so:add.o sub.o
gcc -shared -o $@ $^
#产生.o目标文件,程序内部的地址方案是:与位置无关,库文件可以在内存的任何位置加载,而且不影响和其他程序的关联性
%.o:%.c
gcc -fPIC -c $<
.PHONY:clean
clean:
rm -f libmymath.so *.o
.PHONY:lib
lib:
mkdir lib
cp *.h lib
cp libmymath.so lib
使用刚才上面演示的gcc的方法编译mytest.c后虽然能编译成功,但是却会报错,为什么呢?
程序运行的时候需要动态库,但是当我上面gcc编译完时我所提供的寻找的位置已经消失了,此时加载器需要在运行的时候,告知系统,我们的库在哪里。而刚刚上面的静态库却不需要,因为已经直接拷贝进去了。
如何解决呢?
在库所在的文件导入一个环境变量LD_LIBRARY_PATH(比如路径lib下里面有add.h sub.h libmymath.so , 将环境变量放入这个lib里面就行),代码为:$ export LD_LIBRARY_PATH=绝对路径
当然这么搞下次退出环境变量就消失了,之前这点有讲过。
或者可以 ldconfifig 配置/etc/ld.so.conf.d/,ldconfifig更新
[root@localhost linux]# cat /etc/ld.so.conf.d/bit.conf
/root/tools/linux
[root@localhost linux]# ldconfig
3.4总结
如果只提供静态库,我们只能将我们的库,静态链接到我们的程序当中。
如果只提供动态库,我们只能将我们的库采用动态链接。
如果此时只有动态库,是无法在makefile中加入 -static进行静态链接的。
如果提供了两种库,当我们使用gcc和g++时候会优先选择动态库。