Linux之动静态库

news2024/11/25 7:05:23

动静态库

  • 动静态库的基本原理
  • 动静态库的认识
  • 动静态库特征
  • 静态库的打包与使用
  • 动态库的打包与使用

动静态库的基本原理

我们知道,生成一个可执行程序会经历以下四个步骤:

  1. 预处理:头文件展开,宏替换,去注释,条件编译,最终会生成.i文件;
  2. 编译:用于词法分析,语法分析,语义分析,符号汇总,检查无误后将代码翻译成汇编代码,会生成.s文件;
  3. 汇编:将汇编指令转化为二进制指令,会生成.s文件;
  4. 链接:生成各个.o文件,链接起来,生成可执行程序;

比如下列程序:Add.cSub.cmain.c需要形成可执行程序,需要先生成Add.oSub.omain.o文件,然后链接起来,生成可执行程序;
在这里插入图片描述
同样,我们在另外一个程序中也要用到这Add.cSub.c这两个文件,经过同样的操作就可以。
在这里插入图片描述
实际上,对于可能频繁用到的源文件,例如上述的Add.cSub.c,我们可以将它们的目标文件Add.oSub.o进行打包,之后需要用到这些目标文件时就可以之间链接这个包当中的目标文件了,而这个包实际上就可以称之为一个库。

我们可以理解为库本质都是一堆目标文件(xxx.o)的集合。

动静态库的认识

在Linux下创建test.c文件,编写如下代码:

  1 #include <stdio.h>
  2 
  3 int main()
  4 {
  5     printf("hello world\n");                                                                                                                                        
  6     return 0;
  7 }

在这份代码当中我们可以通过调用printf输出hello world,主要原因是gcc编译器在生成可执行程序时,将C标准库也链接进来了。

我们使用 ldd 命令可以查看一个可执行程序依赖的库文件;
在这里插入图片描述
这其中的libc.so.6就是该可执行程序所依赖的库文件,我们通过ls命令可以发现libc.so.6实际上只是一个软链接;
在这里插入图片描述
实际上该软链接的源文件libc-2.17.solibc.so.6在同一个目录下,为了进一步了解,我们可以通过file 文件名命令来查看libc-2.17.so的文件类型。
在这里插入图片描述
我们需要了解,在Linux下:以.so为后缀的是动态库,以.a为后缀的是静态库。在windos下以.dll为后缀的是动态库,以.lib为后缀的是静态库。

上面的libc-2.17.so实际上就是一个动态库,这里可执行程序所依赖的libc.so.6实际上就是C动态库,当我们去掉一个动静态库的前缀lib,再去掉后缀.so或者.a及其后面的版本号,剩下的就是这个库的名字。

gcc/g++编译器默认都是动态链接的,若想进行静态链接,可以携带一个-static选项。
在这里插入图片描述
此时生成的可执行程序就是静态链接的了,可以明显发现静态链接生成的可执行程序的文件大小,比动态链接生成的可执行程序的文件大小要大得多。

静态链接生成的可执行程序并不依赖其他库文件,此时当我们使用 ldd 文件名命令查看该可执行程序所依赖的库文件时就会看到以下信息。
在这里插入图片描述
在这里插入图片描述

动静态库特征

静态库

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

优点:

  • 使用静态库生成可执行程序后,该可执行程序就可以独自运行,不再需要库了。

缺点:

  • 使用静态库生成可执行程序会占用大量空间,特别是当有多个静态程序同时加载而这些静态程序使用的都是相同的库,这时在内存当中就会存在大量的重复代码。

动态库

动态库是程序在运行的时候才去链接相应的动态库代码的,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。

在可执行文件开始运行前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接。动态库在多个程序间共享,节省了磁盘空间,操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
在这里插入图片描述
优点:

  • 节省磁盘空间,且多个用到相同动态库的程序同时运行时,库文件会通过进程地址空间进行共享,内存当中不会存在重复代码。
    缺点:
  • 必须依赖动态库,否则无法运行。

静态库的打包与使用

我们以两个源文件add.csub.c,两个头文件add.hsub.h来实现:
add.h文件内容:

  1 #pragma once 
  2 
  3 extern int Add(int a, int b);   

add.c文件内容:

  1 #include "add.h"
  2 
  3 int Add(int a, int b)
  4 {
  5     return a + b;                                                                                                                                                   
  6 }

sub.h文件内容:

  1 #pragma once 
  2 
  3 int Sub(int a, int b);  

sub.c文件内容:

  1 #include "sub.h"  
  2   
  3 int Sub(int a, int b)  
  4 {  
  5     return a - b;                                                                                                                                                   
  6 } 

第一步:让所有.c文件生成.o目标文件;

在这里插入图片描述

第二步:使用ar命令将所有目标文件打包为静态库;

ar命令是gnu的归档工具,常用于将目标文件打包为静态库,下面我们使用ar命令的-r选项和-c选项进行打包。

  • -r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件。
  • -c(create):建立静态库文件。

在这里插入图片描述
此外,我们可以用ar命令的-t选项和-v选项查看静态库当中的文件。

  • -t:列出静态库中的文件。
  • -v(verbose):显示详细的信息。
    在这里插入图片描述

第三步:将头文件和生成的静态库组织起来;

当我们把自己的库给别人用的时候,实际上需要给别人两个文件夹,一个文件夹下面放的是一堆头文件的集合,另一个文件夹下面放的是所有的库文件。

因此,在这里我们可以将add.hsub.h这两个头文件放到一个名为include的目录下,将生成的静态库文件libcal.a放到一个名为lib的目录下,然后将这两个目录都放到mathlib下,此时就可以将mathlib给别人使用了。
在这里插入图片描述

创建makefile

在这里插入图片描述
创建makefile以后使我们的操作更加便捷:
在这里插入图片描述
接下来我们创建一个main.c文件:

1 #include <stdio.h>
2 #include <add.h>                                                                
3 
4 int main()
5 {
6     int a = 10;
7     int b = 20;
8     int ret = Add(a, b);
9     printf("%d + %d = %d\n", a, b, ret);
10     return 0;
11 }

现在该目录下就只有main.c和我们刚才打包好的静态库。
在这里插入图片描述

方法一:使用选项

此时使用gcc编译main.c生成可执行程序时需要携带三个选项:

  • -I:指定头文件搜索路径。
  • -L:指定库文件搜索路径。
  • -l:指明需要链接库文件路径下的哪一个库。
    在这里插入图片描述
    说明一下:
  • 因为编译器不知道你所包含的头文件add.h在哪里,所以需要指定头文件的搜索路径。
  • 因为头文件add.h当中只有Add函数的声明,并没有该函数的定义,所以还需要指定所要链接库文件的搜索路径。
  • 实际中,在库文件的lib目录下可能会有大量的库文件,因此我们需要指明需要链接库文件路径下的哪一个库。库文件名去掉前缀lib,再去掉后缀.so或者.a及其后面的版本号,剩下的就是这个库的名字。
  • -I-L-l这三个选项后面可以加空格,也可以不加空格。

方法二:把头文件和库文件拷贝到系统路径下

既然编译器找不到我们的头文件和库文件,那么我们直接将头文件和库文件拷贝到系统路径下不就行了。
在这里插入图片描述
虽然已经将头文件和库文件拷贝到系统路径下,但当我们使用gcc编译main.c生成可执行程序时,还是需要指明需要链接库文件路径下的哪一个库。

我们使用gcc编译的是C语言,而gcc就是用来编译C程序的,所以gcc编译的时候默认就找的是C库,但此时我们要链接的是哪一个库编译器是不知道的,因此我们还是需要使用-l选项,指明需要链接库文件路径下的哪一个库。

扩展:
实际上我们拷贝头文件和库文件到系统路径下的过程,就是安装库的过程。但并不推荐将自己写的头文件和库文件拷贝到系统路径下,这样做会对系统文件造成污染。

动态库的打包与使用

动态库的打包相对于静态库来说有一点点差别,但大致相同:

第一步:让所有源文件生成对应的目标文件

此时用源文件生成目标文件时需要携带-fPIC选项:

-fPIC(position independent code):产生位置无关码。

在这里插入图片描述

  • -fPIC作用于编译阶段,告诉编译器产生与位置无关的代码,此时产生的代码中没有绝对地址,全部都使用相对地址,从而代码可以被加载器加载到内存的任意位置都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
  • 如果不加-fPIC选项,则加载.so文件的代码段时,代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的拷贝,并且每个拷贝都不一样,取决于这个.so文件代码段和数据段内存映射的位置。
  • 不加-fPIC编译出来的.so是要在加载时根据加载到的位置再次重定位的,因为它里面的代码BBS位置无关代码。如果该.so文件被多个应用程序共同使用,那么它们必须每个程序维护一份.so的代码副本(因为.so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)。
  • 我们总是用-fPIC来生成.so,但从来不用-fPIC来生成.a。但是.so一样可以不用-fPIC选项进行编译,只是这样的.so必须要在加载到用户程序的地址空间时重定向所有表目。

第二步:使用-shared选项将所有目标文件打包为动态库

与生成静态库不同的是,生成动态库时我们不必使用ar命令,我们只需使用gcc的-shared选项即可。
在这里插入图片描述

第三步:将头文件和生成的动态库组织起来

与生成静态库时一样,为了方便别人使用,在这里我们可以将add.h和sub.h这两个头文件放到一个名为include的目录下,将生成的动态库文件libcal.so放到一个名为lib的目录下,然后将这两个目录都放到mlib下,此时就可以将mlib给别人使用了。
在这里插入图片描述

创建makefile

在这里插入图片描述
在这里插入图片描述
我们依然使用main.c来进行动态库的演示:

使用该动态库的方法与刚才我们使用静态库的方法一样,我们既可以使用 -I,-L,-l这三个选项来生成可执行程序,也可以先将头文件和库文件拷贝到系统目录下,然后仅使用-l选项指明需要链接的库名字来生成可执行程序;

此时使用gcc编译main.c生成可执行程序时,需要用-I选项指定头文件搜索路径,用-L选项指定库文件搜索路径,最后用-l选项指明需要链接库文件路径下的哪一个库。
在这里插入图片描述
此时我们会发现,生成的可执行程序a.out无法执行,我们要注意的是:我们使用-I-L-l这三个选项都是在编译期间告诉编译器我们使用的头文件和库文件在哪里以及是谁,但是当生成的可执行程序生成后就与编译器没有关系了,此后该可执行程序运行起来后,操作系统找不到该可执行程序所依赖的动态库,我们可以使用ldd命令进行查看。
在这里插入图片描述
我们如何去解决这个问题呢?

方法一:拷贝.so文件到系统共享库路径下
在这里插入图片描述
方法二:更改LD_LIBRARY_PATH

LD_LIBRARY_PATH是程序运行动态查找库时所要搜索的路径,我们只需将动态库所在的目录路径添加到LD_LIBRARY_PATH环境变量当中即可。
在这里插入图片描述

方法三:配置/etc/ld.so.conf.d/

我们可以通过配置/etc/ld.so.conf.d/的方式解决该问题,/etc/ld.so.conf.d/路径下存放的全部都是以.conf为后缀的配置文件,而这些配置文件当中存放的都是路径,系统会自动在/etc/ld.so.conf.d/路径下找所有配置文件里面的路径,之后就会在每个路径下查找你所需要的库。我们若是将自己库文件的路径也放到该路径下,那么当可执行程序运行时,系统就能够找到我们的库文件了。
在这里插入图片描述
此时我们发现系统还是没有找到该可执行程序所依赖的动态库,我们需要使用ldconfig命令将配置文件更新一下,更新之后系统就可以找到该可执行程序所依赖的动态库了。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1024193.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iOS16新特性:实时活动-在锁屏界面实时更新APP消息 | 京东云技术团队

简介 之前在 《iOS16新特性:灵动岛适配开发与到家业务场景结合的探索实践》 里介绍了iOS16新的特性&#xff1a;实时更新&#xff08;Live Activity&#xff09;中灵动岛的适配流程&#xff0c;但其实除了灵动岛的展示样式&#xff0c;Live Activity还有一种非常实用的应用场景…

【Verilog教程】2.3 Verilog 数据类型

Verilog 最常用的 2 种数据类型就是线网&#xff08;wire&#xff09;与寄存器&#xff08;reg&#xff09;&#xff0c;其余类型可以理解为这两种数据类型的扩展或辅助。 线网&#xff08;wire&#xff09; wire 类型表示硬件单元之间的物理连线&#xff0c;由其连接的器件输…

脑网络图谱

前言 研究人脑面临的一个挑战是其多尺度组织和系统复杂性。我们对大脑组织的认识主要来源于离体组织学检查&#xff0c;如细胞结构映射。通过研究全脑微观结构特征的变化&#xff0c;可以划分为不同的脑区。然而&#xff0c;这种研究大脑组织的“局部”方法非常耗时、耗资源&a…

Python 图形化界面基础篇:创建工具栏

Python 图形化界面基础篇&#xff1a;创建工具栏 引言 Tkinter 库简介步骤1&#xff1a;导入 Tkinter 模块步骤2&#xff1a;创建 Tkinter 窗口步骤3&#xff1a;创建工具栏步骤4&#xff1a;向工具栏添加工具按钮步骤5&#xff1a;处理工具按钮的点击事件步骤6&#xff1a;启动…

window10下安装docker教程

要在Windows 10上安装Docker&#xff0c;您可以按照以下步骤进行操作&#xff1a; 在您的Web浏览器中&#xff0c;访问Docker官方网站&#xff1a;https://www.docker.com/get-started。然后&#xff0c;点击"Download Docker Desktop"按钮。 在下载页面上&#xff…

ruoyi-vue-pro 项目安装使用过程中的问题解决

ruoyi-vue-pro是功能比较多的一款前后端平台,因为刚接触ruoyi平台不久&#xff0c;为了更好了解ruoyi平台的相关功能&#xff0c;就本地部署了&#xff0c;作者主动屏蔽了部分功能&#xff0c;部分功能会提示功能能未启用&#xff0c;同时启用dev环境的时候是演示版本&#xff…

使用香橙派学习Linux udev的rules 并实现U盘的自动挂载

在之前编程首先语音刷抖音的博文里提到过udev&#xff0c;现在回顾一下&#xff1a; 什么是udev&#xff1f; udev是一个设备管理工具&#xff0c;udev以守护进程的形式运行&#xff0c;通过侦听内核发出来的uevent来管理/dev目录下的设备文件。udev在用户空间运行&#xff0c;…

SQL Server关于AlwaysOn的理解-读写分离的误区(一)

前言 很多人认为AlwaysOn在同步提交模式下数据是实时同步的&#xff0c;也就是说在主副本写入数据后可以在辅助副本立即查询到。因此期望实现一个彻底的读写分离策略&#xff0c;即所有的写语句在主副本上&#xff0c;所有的只读语句分离到辅助副本上。这是一个认知误区&#x…

jmeter基础压力教程

Jmeter基础压力测试教程 一、安装Jmeter&#xff1b; 安装需求&#xff1a;1. JDK 8.0.91安装包&#xff08;最新即可&#xff0c;配置环境变量&#xff09; 2. Badboy2.25脚本录制工具&#xff08;注&#xff1a;Jmeter3.0与badboy2.0不兼容&#xff09; Jmerter安装包…

玩玩“小藤”开发者套件 Atlas 200I DK A2 之环境准备

玩玩“小藤”开发者套件 Atlas 200I DK A2 之环境准备 0. 背景1. 烧录镜像2. 安装依赖3. 安装 Ascend-cann-toolkit 软件包4. 安装PyTorch5. 安装 PyTorch 插件 torch_npu6. 安装APEX混合精度模块 0. 背景 总所周知&#xff0c;英伟达的GPU供不应求&#xff0c;还各种限制。华…

Ae 效果:CC Bubbles

模拟/CC Bubbles Simulation/CC Bubbles CC Bubbles&#xff08;CC 气泡&#xff09;主要用于生成气泡以及模拟其运动的效果。 既可将效果应用到一个纯色图层&#xff0c;也可将其应用到一个图层的副本上或者调整图层上。CC Bubbles 效果生成的气泡在运动过程会与图层内容即时发…

现货白银的价格如何变动

只要是在正常的交易日&#xff0c;现货白银价格的走势&#xff0c;几乎全天24小时都处于波动之中&#xff0c;其行情走势会因为各种政治、经济因素的影响而发生改变&#xff0c;有时价格波动在一天内可以高达两三美元&#xff0c;有时却可以连续几周都处于窄幅波动&#xff0c;…

详解Nacos和Eureka的区别

文章目录 Eureka是什么Nacos是什么Nacos的实现原理 Nacos和Eureka的区别CAP理论连接方式服务异常剔除操作实例方式自我保护机制 Eureka是什么 Eureka 是Spring Cloud 微服务框架默认的也是推荐的服务注册中心, 由Netflix公司与2012将其开源出来,Eureka基于REST服务开发,主要用…

爬虫项目(四):抓取网页所有图片

文章目录 一、书籍推荐二、完整代码三、运行结果 一、书籍推荐 推荐本人书籍《Python网络爬虫入门到实战》 &#xff0c;详细介绍见&#x1f449;&#xff1a; 《Python网络爬虫入门到实战》 书籍介绍 二、完整代码 原理&#xff1a;抓取该链接中所有的图片格式。基于seleni…

分布式数据库(笔记)

课程链接&#xff1a;015_数据库系统的用户接口以及SQL语言&#xff08;一&#xff09;_哔哩哔哩_bilibili 1、分类 DDB&#xff1a;物理上分散&#xff0c;逻辑上集中 特点&#xff1a; 有统一的全局模式靠分布式数据库管理系统实现 DDBMS分布在网络的不同节点上 优点&am…

Responder

环境准备 操作系统:Kali Linux工具:responder,john,evil-winrm PS:输入以下命令解决靶场环境无法打开问题 #echo "<靶机IP> unika.htb">>/etc/hostsresponder工具 [Kali 官网] 手册地址:https://www.kali.org/tools/responder/ 摘要: This package c…

ubuntu 里根文件系统的扩容,/dev/ubuntu-vg/ubuntu-lv 文件系统扩充到整个分区

笔者安装了ubuntu服务器版软件&#xff0c;由于系统安装的时候没有划分好磁盘分区&#xff0c;只采用了1000G固态硬盘的 200G来安装系统&#xff0c;安装完毕后&#xff0c;用df -h 命令查看如下&#xff1a; 根文件系统仅占用了 196G&#xff0c;而本身硬盘的尺寸为1000G&…

C++笔记之std::forward

C笔记之std::forward 文章目录 C笔记之std::forward例一例二 std::forward的作用是在C中帮助实现完美转发&#xff08;perfect forwarding&#xff09;&#xff0c;它将传递给它的参数以原始类型和引用的方式传递给下一个函数&#xff0c;保持参数的值类别&#xff08;lvalue或…

爬虫 — Js 逆向

目录 一、概念1、爬虫2、反爬虫3、加密解密4、加密5、步骤 二、常用加密方式1、加密方式2、常见加密算法3、JS 中常见的算法4、MD5 算法4.1、MD5 加密网站4.2、特点 5、DES/AES 算法6、RSA 算法7、base64 算法 三、环境配置1、node.js 环境配置2、PyCharm 环境配置 一、概念 1…

【AI视野·今日NLP 自然语言处理论文速览 第三十六期】Tue, 19 Sep 2023

AI视野今日CS.NLP 自然语言处理论文速览 Tue, 19 Sep 2023 (showing first 100 of 106 entries) Totally 106 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Speaker attribution in German parliamentary debates with QLoRA-ada…