【Linux:动态库与静态库】

news2024/12/24 8:17:30

1 动态库与静态库的概念

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码 。
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

 我们可以简单的

 看看我们常用的c/c++库:

 那库的名字是啥呢?

我们拿上面图中最下面的两个来说明:

libstdc++.so.6  libstdc++.so.6.0.19

我们的规则是:去掉开头lib,去掉.so或者.a后的内容

所以我们这里得到的库名字都是:stdc++ 

一般来说云服务器只会默认存在动态库,静态库需要我们自己安装。

C/C++静态库安装命令:

sudo yum install -y glibc -static
sudo yum install -y libstdc++ -static

2 为什么要用库

有了库,程序员开发时就能够少去不少麻烦,比如我们如果要自己写一个cout打印函数来帮助打印的话会浪费很多不必要的时间,所以我们一般写C/C++代码时喜欢包含头文件,头文件中就包含了一些常用的接口的声明,那么定义呢?

其实接口的定义我们使用的编译器已经帮助我们将函数定义打好包安装到了默认的搜索路径下,当我们只要包含了头文件,在链接的时候就会去默认路径下找到接口定义的目标文件,然后链接。

我们经常使用的VS2019叫做集成开发环境,其中包含了编辑器,头文件和库,这也是为什么在我们包含了头文件时写头文件中的接口时会有语法提醒,本质上其实就是在头文件中找到该接口。


3 制作静态库

我们首先创建4个文件,将四个文件中.c文件编译生成了.o文件后 ,将.o文件与.h文件交给otherUsr目录中:

 再将main.c拷过去:

 现在我们在otherUsr中运行:

 很显然此时能够运行成功。但是我们一般是把.o文件打包成一个库,打包命令为:

ar -rc libXXX.a *.o

 我们可以来试试:

 这时我们为了规范性将libmymath.a放在一个名叫dir的目录下,将*.o的文件放在include的目录下:

 这时为了能够找到*.h文件和打包的库文件我们要加上3个选项来帮助编译器来找寻:

-I *.h的文件路径
-L 打包库的路径    //L后面空格可加可不加
-l 打包库名    //l后面空格可加可不加,注意库名不包括lib前缀和.a后缀

 这样就能够成功运行了:

 但是为啥我们用C/C++的库就不用这么麻烦呢?原因C/C++的库是安装到指定路径下的,所以我们不用指定路径,如果我们想要将我们自己的库和头文件添加到系统默认配置里面可以用下面方法:

 

 其中系统默认的头文件安装路径是:/usr/include

默认库安装路径是:/lib64

此时我们运行:

 我们发现仍然是会报错的,但是已经不是找不到头文件了,而是链接错误,为什么呢?

因为这个是我们自己配置的第三方库,所以我们必须的指定库名称,否则将会找不到库,当我们指定库名称时来试试:

 很显然已经成功运行了。

其实这就是第三方库,非语言层面非操作系统层面给我们提供的库,我们自己下载的库一般都会下载到系统编译器默认的搜索路径下方便使用。

总结:第三方库的使用

  • 要指定头文件和库文件。
  • 如果没有安装到系统gcc/g++默认的搜索路径下,用户必须指定选项来告知编译器:a:头文件在哪里b:库文件在哪里c:库文件名称
  • 将我们下载到的头文件和库文件拷贝考系统的默认路径下,需要带上库文件的名称来找到库文件。
  • 一般来说,普通用户将下载好的库安装到系统默认路径下都是需要sudo来提权的。

 4 制作动态库

首先来说,制作动态库时生成目标文件要加上 -fPIC 选项,表示的是与位置无关码

(position ignore code)至于为啥是这样文章末尾会给出解释。

[grm@VM-8-12-centos owner]$ gcc -fPIC -c *.c
[grm@VM-8-12-centos owner]$ ll
total 24
-rw-rw-r-- 1 grm grm   61 Apr  1 22:32 my_add.c
-rw-rw-r-- 1 grm grm   40 Apr  1 22:32 my_add.h
-rw-rw-r-- 1 grm grm 1240 Apr  2 10:53 my_add.o
-rw-rw-r-- 1 grm grm   61 Apr  1 22:33 my_sub.c
-rw-rw-r-- 1 grm grm   39 Apr  1 22:33 my_sub.h
-rw-rw-r-- 1 grm grm 1240 Apr  2 10:53 my_sub.o

 这时我们打包就不用ar命令了,直接用gcc打包,只是要带上选项 -shared

[grm@VM-8-12-centos owner]$ gcc -shared -o libmymath.so *.o
[grm@VM-8-12-centos owner]$ ll
total 32
-rwxrwxr-x 1 grm grm 7952 Apr  2 10:56 libmymath.so
-rw-rw-r-- 1 grm grm   61 Apr  1 22:32 my_add.c
-rw-rw-r-- 1 grm grm   40 Apr  1 22:32 my_add.h
-rw-rw-r-- 1 grm grm 1240 Apr  2 10:53 my_add.o
-rw-rw-r-- 1 grm grm   61 Apr  1 22:33 my_sub.c
-rw-rw-r-- 1 grm grm   39 Apr  1 22:33 my_sub.h
-rw-rw-r-- 1 grm grm 1240 Apr  2 10:53 my_sub.o

这也很好的解释了为啥云服务器默认都是动态库。

为了规范性我们将*.h的文件放进了include目录下,然后将打包生成的动态库放进lib目录下,然后再打包:

 将包拷贝给otherUsr并且解压:

 我们按照之前实现静态库的方式来编译链接:

[grm@VM-8-12-centos otherUsr]$ gcc -o mytest main.c -I include -L lib -l mymath
[grm@VM-8-12-centos otherUsr]$ ll
total 28
drwxrwxr-x 2 grm grm 4096 Apr  2 10:59 include
drwxrwxr-x 2 grm grm 4096 Apr  2 11:02 lib
-rw-rw-r-- 1 grm grm  187 Apr  1 22:48 main.c
-rwxrwxr-x 1 grm grm 8432 Apr  2 11:14 mytest
-rw-rw-r-- 1 grm grm 2359 Apr  2 11:08 owner.tgz
[grm@VM-8-12-centos otherUsr]$ ./mytest
./mytest: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory

我们发现生成了可执行文件,但是运行时却发现找不到文件,这是为啥呀?我们不是已经指定了库的名称和路径了吗?为啥还是找不到呢?

我们可以反过来思考一下:我们使用-L-l选项时是将库的路径和名称告诉了谁?

是操作系统吗?显然不是,我们只是告诉了编译器,但是没有告诉操作系统,也就是操作系统找不到库在哪里了。那为啥静态库就可以呢?

回想一下静态库的原理,静态库直接将用户导的库的二进制代码拷贝到可执行程序中,所以操作系统能够直接找到,但是动态库却不会。

我们可以通过ldd命令查看一下:

那么操作系统是如何查找动态库的呢?主要有以下3种方式:

  • 1 通过环境变量 LD_LIBRARY_PATH

我们可以先查看该环境变量里面有什么?

[grm@VM-8-12-centos otherUsr]$ echo $LD_LIBRARY_PATH
:/home/grm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64

然后我们将库路径导入到该环境变量中:

[grm@VM-8-12-centos otherUsr]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/grm/lesson14/otherUsr/lib
[grm@VM-8-12-centos otherUsr]$ echo $LD_LIBRARY_PATH
:/home/grm/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/grm/lesson14/otherUsr/lib

不知道大家注意到没,我们导入环境变量时并没有具体到库的名称,而只是到了存放库的目录路径下,这是为了方便后面我们将其他库也导入该目录中也能够正确使用。

此时我们通过ldd查看:

 我们运行起来试试:

 很显然能够成功。但是这样做有一个很大的问题:那就是当我们退出的时候配置的环境变量会自动销毁,所以这种方式只是一种临时方案。

  • 2 软链接方案

前面我们介绍了软链接,这里通过软链接是一种比较好的方案:

[grm@VM-8-12-centos otherUsr]$ sudo ln -s /home/grm/lesson14/otherUsr/lib/libmymath.so  /lib64/libmymath.so

这种方式也能够让操作系统找到库:

 而且这种方式当我们没有删除软链接的时候是永久保存的,不想要了直接删除软链接即可。

  • 3 配置文件方案

首先我们使用unlink命令解除软链接关系,然后ldd查看:

这时已经没有了软链接,然后我们使用配置文件方案:

先查看一下 /etc/ld.so.conf.d 目录下的内容:

 然后我们提权创建一个自己的文件,并向文件中写入我们库所在的路径:

[grm@VM-8-12-centos otherUsr]$ sudo touch /etc/ld.so.conf.d/mystudy.conf
[grm@VM-8-12-centos otherUsr]$ sudo vim /etc/ld.so.conf.d/mystudy.conf 
[grm@VM-8-12-centos otherUsr]$ cat /etc/ld.so.conf.d/mystudy.conf 
/home/grm/lesson14/otherUsr/lib

用vim打开文件时一定要提权,否则可能无法保存。

这时为了让配置的文件立即生效需要用 ldconfig 命令:

[grm@VM-8-12-centos otherUsr]$ sudo ldconfig
[grm@VM-8-12-centos otherUsr]$ ldd mytest
	linux-vdso.so.1 =>  (0x00007ffed5dfb000)
	libmymath.so => /home/grm/lesson14/otherUsr/lib/libmymath.so (0x00007f853583b000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f853546d000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f8535a3d000)
[grm@VM-8-12-centos otherUsr]$ ./mytest
10 + 20 = 30
10 + 20 = -10

这时便能够成功运行了。


5 动静态库的理解

5.1 静态库的理解

我们知道,静态库是直接将二进制代码拷贝到可执行文件中的,那么当我们若干个进程使用相同的静态库时势必会重复拷贝多份相同的二进制代码,那么这样文件的大小肯定会变大,我们下载时会消耗更多的资源。(所以一般情况下我们不采取静态库)

但是当我们生成了可执行文件后就算我们把静态库删除了也无所谓,因为我们已经将静态库的二进制代码拷贝到了可执行文件中,执行可执行文件依旧能够正常运行。

5.2 动态库的理解

动态库是不会将库的二进制代码直接拷贝到可执行文件的,而是链接时再去寻找,那么我们只需要load一份库的代码到内存中,通过页表映射,当我们执行时再到对应的进程地址空间寻找即可。这份库的代码在进程地址空间中对应着哪一个区呢?答案是共享区。在共享区中存放着库的二进制代码,但是这样会面临着一个问题:不同进程,运行程度是不同的,需要使用的第三方库是不同的,那么注定了每一个进程的共享空间中空闲位置是不确定的,如何找到不同进程对应的库呢?

这时我们使用绝对编址的方法就已经行不通了,那么我们就要采取相对编址的方法,记录偏移量,通过偏移量来找到库的虚拟地址。这就说明此时加载库在共享区的时候随便你怎么加载,我们是通过偏移量来寻找的。

制作动态库时生成目标文件要加上 -fPIC 选项,表示的是与位置无关码

(position ignore code)就是这个原因。

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

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

相关文章

企业微信4.1.6 版本新功能介绍

一、效率工具与基础体验优化 文档 文档增加了丰富的模板,包含项目管理、日报周报、信息收集等多种场景,帮助了解更多文档功能,助力日常工作。 权限管理新增了「成员加入确认」开关,开启后需要管理员确认才能添加成员&#xff0…

相爱相杀的在线帮助文档语雀、Baklib、石墨文档,到底有何区别?

在线帮助文档是现代企业不可或缺的一部分,它提供了针对特定产品或服务的详细说明和指南,以帮助用户更好地理解产品或服务并解决问题。目前市面上有许多在线帮助文档工具,其中语雀、Baklib和石墨文档是比较受欢迎的三种,本文将对它…

卸载旧版本Keil,安装新版本的注意事项以及安装完成以后的一些问题

1. 资料 这里使用的是MDK536安装包和Keil.STM32F4xx_DFP.2.16.0安装包(因为板子是正点原子stm32F407的); 安装包放到下面(安装包也是在网上找的,里面还是比较全的,有C51,以及注册机等&#xf…

只做笔记有必要买apple pencil吗?好写的电容笔排行榜

随着科技的发展,出现了许多新的电子器件和数码器件。比如智能手机,比如ipad,比如电容笔等等。但实际上,想要让ipad发挥出最大的作用,就必须要有一支好的电笔。就像是我们在ipad上写字,总是要手写&#xff0…

硬件工程师-电路设计1-概念

学习电路设计及分析需要掌握的三大定律详解 1、遵循 源 回路 阻抗 分析回路上的节点,分析节点的内阻,电压,功率电流 2、分析电路上的波形:电路设计的过程就是波形整形的过程 波形整形:幅值的整形 波的…

CNNs:ZFNet之基于AlexNet特征可视化实验分析

CNNs:ZFNet之基于AlexNet特征可视化实验分析 导言基于AlexNet网络的实验分析实验一:不同卷积层特征提取分析实验二:不同卷积层提取特征收敛分析 ZFNet网络介绍基于ZFNet网络的实验分析实验三:针对AlexNet特征提取改善可视化实验四:特征不变性…

HW之轻量级内网资产探测漏洞扫描工具

简介 RGPScan是一款支持弱口令爆破的内网资产探测漏洞扫描工具,集成了Xray与Nuclei的Poc 工具定位 内网资产探测、通用漏洞扫描、弱口令爆破、端口转发、内网穿透、SOCK5 主机[IP&域名]存活检测,支持PING/ICMP模式 端口[IP&域名]服务扫描 网…

腾讯云图形验证码申请流程

目录 一、官方指引二、操作步骤1.步骤1:新建验证,获取验证码密钥2.步骤2:客户端接入验证码,展示验证页面3.步骤3:服务端接入验证码,调用票据校验 API 进行二次校验 一、官方指引 https://cloud.tencent.co…

为什么mac插入了u盘没反应 苹果mac插上usb后怎么找到

U盘使用简单,便于携带,几乎每个mac用户都有一个u盘。使用新的u盘的时候,你有没有遇到过mac插入了u盘没反应的情况呢?如果你是初次接触Mac电脑,使用u盘可能会手足无措,因为Mac系统和Windows存在差异&#xf…

苹果 App Store 出现山寨ChatGPT;Anthropic宣布获得4.5亿美元C轮融资

🚀 中国互联网协会提醒公众警惕“AI换脸”的新骗局 中国互联网协会提醒公众警惕“AI换脸”的新骗局,不法分子利用AI技术通过声音合成、伪造面部表情等实施诈骗。 公众应加强个人信息安全与防范措施,如加强个人信息保护、防止信息泄露、安装…

BLE连接通信

// BLE连接有关的技术分析 前言 本文的论述流程: 将传统的连接通信与广播进行对比,指出其不足说明BLE是如何制定规范解决这些问题写出完整的连接通信流程,并对能影响BLE连接通信的参数进行分析 1 连接通信的不足 保持连接是一个相当消耗…

代码随想录算法训练营15期 Day 2 | 977.有序数组的平方 、209.长度最小的子数组 、59.螺旋矩阵II 、总结

977.有序数组的平方 题目建议&#xff1a; 本题关键在于理解双指针思想 题目链接&#xff1a;力扣 思路一&#xff1a;暴力解算&#xff0c;直接将所有元素变成一个平方&#xff0c;然后进行排序。 class Solution { public:vector<int> sortedSquares(vector<int&g…

maven常用插件详解

官网讲解&#xff1a;https://maven.apache.org/plugins/ Maven 实际上是一个依赖插件执行的框架&#xff0c;它执行的每个任务实际上都由插件完成的。Maven 的核心发布包&#xff08;jar&#xff09;中并不包含任何 Maven 插件&#xff0c;它们以独立构件的形式存在&#xff…

数据翻译的代码辅助插件,一个注解搞定,减少30%SQL代码量

一、开源项目简介 Easy Trans是一款用于做数据翻译的代码辅助插件&#xff0c;利用MyBatis Plus/JPA/BeetlSQL 等ORM框架的能力自动查表&#xff0c;让开发者可以快速的把ID/字典码 翻译为前端需要展示的数据。 二、开源协议 使用Apache-2.0开源协议 三、界面展示 四、功能概…

CTF入门指南

何为CTF &#xff1f; CTF&#xff08;Capture The Flag&#xff09;夺旗比赛&#xff0c;在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会&#xff0c;以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。…

文档关键信息提取形成知识图谱:基于NLP算法提取文本内容的关键信息生成信息图谱教程及码源(含pyltp安装使用教程)

文档关键信息提取形成知识图谱&#xff1a;基于NLP算法提取文本内容的关键信息生成信息图谱&#xff08;含pyltp安装使用教程&#xff09; 1. 项目介绍 目标&#xff1a;输入一篇文档&#xff0c;将文档进行关键信息提取&#xff0c;进行结构化&#xff0c;并最终组织成图谱组…

Cisco® Catalyst® 8000V 边缘软件 (Catalyst 8000V) 17.11.1a 发布 - 虚拟路由器

Cisco Catalyst 8000v Edge Software, IOS XE Release Dublin-17.11.1a ED 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-catalyst-8000v/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Cisco Catalyst 8000V 边…

Spark集群的运行基本流程是怎样的?

Spark集群的运行架构 Spark是基于内存计算的大数据并行计算框架&#xff0c;比MapReduce计算框架具有更高的实时性&#xff0c;同时具有高效容错性和可伸缩性&#xff0c;在学习Spark操作之前&#xff0c;首先介绍Spark运行架构&#xff0c;如图所示。 在上图中&#xff0c;Sp…

asp网站Repuest获取Http网络读卡器刷卡数据Response回应驱动读卡器显示响声

RFID网络WIFI无线TCP/UDP/HTTP可编程二次开发读卡器POE供电语音-淘宝网 (taobao.com) 中文液晶显示http协议网络读卡器是一款能利用现有的计算机网络&#xff0c;不需要独立布线就可以组成一个高性能低成本实时联网ID卡管理系统的端终设备&#xff0c;刷卡后即时向远程计算机传…

快递业的最新发展趋势:2023年市场预测

快递业是随着电子商务崛起而迅速发展的行业之一。自从互联网取代了线下商业模式&#xff0c;电子商务的发展成为了现代零售业的主要趋势&#xff0c;而快递业则变得越来越重要和不可或缺。未来的快递业需要应对许多挑战和机遇。 在2023年&#xff0c;快递业将进一步走向数字化、…