网站ip pv,婚纱网站php,网站为什么维护,seo外推软件#x1f31f;hello#xff0c;各位读者大大们你们好呀#x1f31f; #x1f36d;#x1f36d;系列专栏#xff1a;【Linux初阶】 ✒️✒️本篇内容#xff1a;动静态库初识#xff0c;库的含义#xff0c;静态库的生成与链接#xff0c;gcc/g默认链接方式#xff0c… hello各位读者大大们你们好呀 系列专栏【Linux初阶】 ✒️✒️本篇内容动静态库初识库的含义静态库的生成与链接gcc/g默认链接方式动态库的生成与动态链接查看动态链接的方法动静态库的加载原理 作者简介计算机海洋的新进船长一枚请多多指教( •̀֊•́ ) ̖́- 文章目录 一、动静态库初识1.静态库和动态库2.动态库的理解 二、理解库的含义三、静态库和静态链接1.生成静态库2.查看静态库中的目录列表3.静态库的下载和安装4.静态库的链接5.gcc/g的默认链接方式 四、动态库和动态链接1.生成动态库2.动态链接1共享路径法2环境变量法3更改配置文件法4软连接法【推荐】5查看动态库是否链接成功 五、动静态库的加载1.静态库不存在加载2.动态库加载1动态库的编址方式2动态库加载和访问的逻辑重点 结语 一、动静态库初识
1.静态库和动态库
静态库.a程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。动态库.so程序在运行的时候才去链接动态库的代码多个程序共享使用库的代码。
2.动态库的理解
一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表而不是外部函数所在目标文件的整个机器码。在可执行文件开始运行以前外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中这个过程称为动态链接dynamic linking。动态库可以在多个程序间共享所以动态链接使得可执行文件更小节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用节省了内存和磁盘空间。 二、理解库的含义
我们可以创建一个测试程序
测试程序
/add.h/
#pragma once
#include stdio.h
extern int Add(int a, int b);/add.c/
#include my_add.h
int Add(int a, int b)
{printf(enter Add func, %d %d ?\n, a, b);return a b;
}/sub.h/
#pragma once
#include stdio.h
extern int Sub(int a, int b);/add.c/
#include add.h
int sub(int a, int b)
{return a - b;
}///main.c
#include my_add.h
#include my_sub.hint main()
{int a 10;int b 20;int res Sub(a, b);printf(result: %d\n, res);res Add(a, b);printf(result: %d\n, res);return 0;
}程序的编译分为四个阶段预处理、编译、汇编、链接。输入下面三条指令分别会形成 3个汇编之后的二进制文件这种二进制文件无法运行因为它缺了链接的步骤。
gcc -c main.c - main.o
gcc -c my_add.c - my_add.o
gcc -c my_sub.c - my_sub.o这种 .o文件我们称它为可重定位目标二进制文件。通过对 .o文件的链接可以形成一个统一的可执行文件。 我们在上面说过.o文件是一个二进制文件无法阅读。如果我们不想给对方我们的源码可以给对方提供 .o可重定位目标二进制文件让别人用你的代码进行链接即可。
也就是说如果要在没有源码的情况下形成可执行文件我们需要给对方提供 .o方法的实现.h都有什么方法还要有自己调用方法的文件main.o。
在上面的基础上我们可以试着给所有的 .o文件打一个包最后只给对方提供一个库文件即可。
库文件.o文件 - 一个文件 - 库 - 根据打包工具和方式不同静态库和动态库 总结库文件就是 .o文件的集合。 三、静态库和静态链接
1.生成静态库
为了方便理解我们使用 Makefile工具
libmymath.a:my_add.o my_sub.o
ar - rc $ $^ #ar是gnu归档工具rc表示(replace and create)
my_add.o:my_add.c
gcc - c my_add.c - o my_add.o #形成.o文件
my_sub.o : my_sub.c
gcc - c my_sub.c - o my_sub.o.PHONY : output #发布
output :
mkdir - p mylib / include #-p建立多级目录
mkdir - p mylib / lib
cp - f *.a mylib / lib #将生成的libmymath.a和头文件拷贝到特定的目录中
cp - f *.h mylib / include.PHONY:clean
clean :
rm - rf *.o libmymath.a mylib #删除所有.o文件和对应的库至此我们生成了对应的目录文件 mylib而这个mylib就是我们的静态库。 总结交付库 库文件.a/.so 匹配的头文件。 ———— 我是一条知识分割线 ————
2.查看静态库中的目录列表
方法如下
查看静态库中的目录列表
[rootlocalhost linux]# ar - tv libmymath.a
rw - r--r-- 0 / 0 1240 Sep 15 16:53 2017 add.o
rw - r--r-- 0 / 0 1240 Sep 15 16 : 53 2017 sub.o
t : 列出静态库中的文件
v : verbose 详细信息———— 我是一条知识分割线 ————
3.静态库的下载和安装
我们可以使用 tar命令对静态库进行压缩。然后将这个文件放在某一个网站中让用户下载。 用户下载后可进行解压和安装。 安装实际上就是将库文件和对应的头文件放到系统的特定路径下。 总结安装的本质就是拷贝。库的安装就是将库文件和头文件分别拷贝到对应的默认路径中。库的卸载就是将库文件和头文件从默认路径下删除。 ———— 我是一条知识分割线 ————
4.静态库的链接
首先我们一起来看下 mylib的结构。 此时如果我们直接链接或者只添加头文件路径都会会出现报错。 因为gcc/g需要详细的头文件、库文件路径、还有库文件名称。 这里-I大写i后跟头文件路径-L 大写l后跟库文件路径-l小写l后跟库文件名。 补充上述字母后面空格可带可不带。gcc/g只能在当前路径下搜索因此需要具体的头文件、库文件路径、还有库文件名称。
如果已经将库文件和头文件安装到默认路径下只需要指明库文件名称即可。 总结链接第三方库时必须指明头文件路径、库文件路径、库文件名称。 注意我们平时编译代码不用提供路径是因为库文件和头文件在系统的默认路径下。gcc/g默认能识别C/C的库。 ———— 我是一条知识分割线 ————
5.gcc/g的默认链接方式
gcc、g是默认使用动态链接的。如果软件有动态库和静态库在链接过程中软件会将静态库的代码拷贝过来但是最后使用的是动态链接。如果软件只使用了静态库没有使用任何一个动态库最终才会以静态方式进行链接。 总结gcc、g是默认使用动态链接的 四、动态库和动态链接
1.生成动态库
在学习动态库生成之前我们需要了解几个名词
shared: 表示生成共享库格式fPIC产生位置无关码(position independent code)库名规则libxxx.so
示例 #生成共享库格式的 .o文件[rootlocalhost linux]# gcc -fPIC -c sub.c add.c #生成对应的动态库文件[rootlocalhost linux]# gcc -shared -o libmymath.so *.o 接下来我们要生成一个目录存放库文件和头文件 至此我们创建好了我们的动态库。 ———— 我是一条知识分割线 ————
2.动态链接
创建好目录之后我们尝试链接发现会存在报错err找不到文件或目录因此动态链接并没有我们想象中那么简单。
我们不是已经告诉 gcc/g头文件、库文件路径还有库文件名称了吗为什么还是不能链接呢答案是gcc/g只完成了编译的步骤我们在运行的时候还需要操作系统通过动态链接调用我们的代码也就是说OS也需要知道我们库的位置但是库不在系统路径下无法找到。
下面介绍动态链接的四种方法
1共享路径法
拷贝.so文件到系统共享库路径下, 一般指/usr/lib。
2环境变量法
更改环境变量 LD_LIBRARY_PATH系统除了会在默认路径下搜索库还会在这个变量下搜索。 [rootlocalhost linux]# export LD_LIBRARY_PATH. 后跟库的具体路径[rootlocalhost linux]# gcc main.c -lmymath[rootlocalhost linux]# ./a.out3更改配置文件法
我们通过更改自己的配置文件也可以让操作系统找到。
ldconfig 配置/etc/ld.so.conf.d/ldconfig更新进入cd系统存放配置文件的路径创建一个配置文件将所需要链接的库文件的具体地址填入到自定义的配置文件中输入指令 ldconfig进行配置文件更新永久有效除非删除删除先删除配置文件再输入指令 ldconfig 注意配置文件操作大多需要 root权限普通用户可以通过 sudo提权实现配置文件的创建和更新。 4软连接法【推荐】
操作系统可以查找当前路径下的库文件所以我们可以在当前路径下创建一个软连接文件链接库文件实现OS的访问。
在当前路径下创建软连接文件。 断开链接
或者我们可以将软连接文件放到系统默认路径下。 ———— 我是一条知识分割线 ————
5查看动态库是否链接成功
ldd 文件名 #指令五、动静态库的加载
1.静态库不存在加载
静态库不存在加载因为程序在编译完成之后就会将静态库的内容拷贝到我们的可执行程序中。当可程序运行起来之后再整体加载到内存。 注意程序加载 ! 静态库加载静态库加载不存在。 当其他可执行程序再次调用静态库时就需要再次将静态库内容拷贝到可执行程序中。静态库的使用可能会导致代码的冗余。
代码在编译的过程中就已经以虚拟地址空间的方式将我们的代码编译好了因此静态库拷贝的本质将静态库的代码展开将代码拷贝到可执行程序的代码区中。 总结代码在编译好后会按照虚拟地址空间的排布规则对代码进行排布静态库在编译过程中会将展开的代码拷贝到代码区中。未来这部分代码必须通过相对确定的地址位置进行访问。 ———— 我是一条知识分割线 ————
2.动态库加载
1动态库的编址方式
动态链接并没有将代码拷贝到我们的可执行程序中它是将动态库中指定函数的地址写进了可执行程序中。
举个例子假设我们有一个 my.exe可执行文件、libc.so动态库在可执行程序中有一个 printf函数函数在可执行文件中具有对应的地址完成动态库加载之后它会将 printf的地址写进可执行程序中。当程序需要的时候可以通过地址找到对应的方法。
在动态库的生成中我们讲解了一个名词 fPIC产生位置无关码(position independent code)。它具体的含义是什么呢与位置无关用特定的参照系来定位某个人或物体所处位置的相对定位的方式。
也就是说我们是用这个方法实现动态库的连接和加载的。动态库加载会将函数的地址写入到可执行程序中这个地址是偏移地址记录了函数在 .so中的偏移量。 总结静态库拷贝依据确定起始点进行拷贝方法的位置确定这种编址方式称为绝对编址动态库加载会将函数的偏移地址写入到可执行程序中根据不同的起始地址查找方法这种编址方式为相对编址。 2动态库加载和访问的逻辑重点
动态库加载和访问的逻辑详解
还是以 printf函数为例当我们的计算机需要调用代码的 printf函数通过页表读取之后发现 printf的实现代码在可执行程序中并不存在这是编译时就标识好的同时还会发现代码区中的这个地址是一个外部地址。
此时操作系统就知道要访问这个库了接下来操作系统不会继续执行 printf的代码它会先将磁盘中的动态库加载到内存。
再将内存中库的内容通过页表映射到虚拟地址空间的共享区中映射完成后库天然就拥有了起始地址。不同的程序可能会加载不同的动态库因此在动态库没有完成加载映射之前它的起始地址是不确定的。
在虚拟地址空间的代码区中因为我们的代码存有库方法的偏移地址所以在库完成动态加载映射之后当我们想调用 printf函数跳转动态库的执行方法我们就可以在确定了库的起始地址的前提下根据代码中保存的偏移地址直接跳转到共享区的库方法之中。
至此我们就可以在有需要调用库函数的时候直接在上下文中跳转。最终实现动态库加载和访问。 总结调用库方法前需要对动态库进行加载映射使动态库具有起始地址然后代码区中的代码就可以通过偏移量在上下文中进行跳转最终找到共享区的库方法。 补充动态库加载时操作系统会根据一定的策略加载库方法而不会将所有方法一次性加载。 补充操作系统会在有需要的时候对库进行加载链接当100个进程都用了同一个库内存中这个库的代码也就只有一份。因此我们可以通过使用动态链接的方式实现节省内存的目的。 结语 基础IO - 动静态库 的知识大概就讲到这里啦博主后续会继续更新更多C 和 Linux 的相关知识干货满满如果觉得博主写的还不错的话希望各位小伙伴不要吝啬手中的三连哦你们的支持是博主坚持创作的动力