【Linux笔记】动静态库的封装和加载

一、静态库的封装

我们在学习C语言阶段其实就已经知道一个可执行程序的形成过程分为预处理、编译、汇编、链接这四个阶段,而且也知道我们程序中使用的各种库其实是在链接的阶段加载的。

可我们那时候并不知道库是怎么被加载的,或者库是怎么形成的,所以今天我们就要好好的来聊一下,库的形成。

1.1、库封装的本质

我们知道链接阶段其实是将形成可执行程序的各种.o目标文件连接起来形成可执行程序,但是对于一个库来说,可是如果一个可执行程序使用的很多函数分散在不同的.o文件中,那么这样一个一个的链接就显得很麻烦。

就例如我们现在有这样的一个场景:

如上图,我们有这样一堆头文件和对应的方法实现,我们想要模拟编译器的行为先形成.o文件,再将它们连接形成可执行程序。我们当然可以一个一个的将它们编译形成.o文件,然后再连接:

但这样实在太麻烦,特别是后面链接形成可执行的操作,如果今天我们的可执行程序依赖了100或200个.o文件,那这样不累死人?

所以为了方便,我们可以将一些有关联的.o文件再次打包,形成一个库文件,那这样我们在连接的时候就只需要找到这个库文件就行了。

所以库的封装其实就是将一批.o文件打包。

1.2、静态库的封装方法

我们打包静态库使用的指令是ar -rc指令,例如我们想要将上面的这些方法打包成一个名为mymath的静态库,对应的指令是这样的:

注意:上面所写的库名称为libmymath.a,但是该库的真实名称并不是libmymath,而是mymath,这是因为库文件必须要一个前缀lib,而.a是静态库文件的后缀名。

但是这样使用起来会有点麻烦,这是因为gcc在编译形成可执行程序的时候默认值知道系统默认的库的名称和路径,我们现在这个是我们自己新建的库,所以gcc不认识。所以我们在编译的时候还需要特别指定库名称和库路径。

指定库名称使用的是-l选项,执行库路径使用的是-L选项:

这样我们才能成功的生成可执行程序:

但我们今天还是想要做得更规范一些,一般我们的库文件和头文件是被放到一个目录结构中的,这样就更像一个整体。

我们编写一个makefile来完成再次打包的工作:

这样执行了makefile之后,我们就得到了一个更规范的库文件了:

然后使用的时候就需要注意了,因为我们上面的演示中头文件是和test.c处在同一个路径下的,所以编译的时候不需要指定头文件的路径编译器也能直接找到,而我们今天已经形成了一个库文件,也就是所我们以后其他的源代码想要用这个库中的方法,就只需要有这个库文件即可,而其他的源代码的路径下就不会有对应的头文件了,所以我们在使用的时候还需要多加一个-I的选项表示指定头文件的路径:

二、动态库的封装

2.1、动态库的封装方法

动态库的封装就和静态库有点不一样了,首先动态库的封装是直接使用gcc的,这说明gcc是可以直接形成动态库的。这个后面我也会聊一些,这是因为动态库比起静态库来说更重要,优先级也更高。

第二个不同的地方也比较特殊了,我们在形成对应的.o文件的时候还需要额外的加上一个“与位置无关码”fPIC的选项,这个后面我们简单的聊一些,其实我也不是很懂,记住就行了。

此外还需要加上一个-shared的指令,表示生成的是“共享库”,这个也是到后面聊:

其他的地方就没什么不同了,因为库的本质动态库和静态库是一样的。

所以我们就只需要修改一下makefile即可,也就是像上面生成静态库一样,将二次打包也做好:

执行makfile之后我们就形成了动态库了:

然后使用动态库生成可执行程序的时候也是个静态库一样:

但当我们直接运行的时候就会出现问题了:

如果直接运行的话,会提示找不到动态库,这时候大家可能就会有疑问了,我在形成可执行程序的时候不是已经告诉了编译器库的路径了吗?怎么还会找不到呢?

这是因为静态库和动态库的加载是不一样的。

严格来说,静态库是不需要加载的,因为静态库是在链接的时候将对应的方法一代码的形式“拷贝”到我们可执行程序的代码部分,所以自然的,运行的时候就不要再找静态库了。

而动态库不同,动态库并不是以代码的形式将拷贝到可执行程序的代码部分,它是在运行的时候才加载到“内存”,并不是加载到可执行程序中,然后由可执行程序在内存中去寻找。

所以我们也就知道了,我们编译形成可执行程序的时候只是将库的路径告诉了编译器,但运行是操作系统的事,操作系统当然不知道我们的库在哪里了。

如果想要解决动态库运行时候找不到库的问题,可以有一下四种解决方案。

2.2、解决动态库运行时找不到的三种方法

方法1:直接将动态库安装到系统中默认路径下

我们以前在编译可执行程序的时候,之所以不需要指定库路径和头文件的路径,是因为我们以前使用的都是系统默认支持的库,而这些库的头文件都会存在于系统中的两个默认的路径:/lib64/usr/include中。

所以我们今天就可以直接将我们的头文件和库拷贝到这两个路径中:

这样我们再次运行就可以正常运行了:

并且如果我们今天将可执行程序删掉在重新编译形成,也不再需要指定头文件路径和库路径了,只需要指定库名称即可:

因为我们的头文件和库已经存在于系统的默认路径下了,所以在编译的时候就像我们以前使用系统库一样再默认路径下寻找了,而之所以还需要指定库名称是因为我们实现的mymath本质上还是属于第三方库,而第三方库gcc是不认识的。

而且我们在使用ldd指令来查询可执行程序所依赖的库的时候也是可以查到的,并且显示的路径就是/lib64:

而如果我们将刚才加入的头文件和库都删掉,在用ldd查询的时候就会显示找不到对应的库了:

同时将库直接安装到系统的默认路径下也是我们以后使用第三方库最推荐的一种做法,没有之一。

方法2:在当前路径下创建连接到库的软连接:

其实这也是软连接的一个应用场景,当然啦我们也可以将软连接创建到/lib64目录下,我当前就不做了。

方法3:更改环境变量

在我们的系统中有许许多多的环境变量,而其中有一个是和我们今天动态库加载有关的环境变量——LD_LIBRARY_PATH,这个环境变量就是用来存储我们需要加载的动态库的绝对路径。

但是有的朋友可能这个环境变量是空的或者不存在,这也是很正常的,因为有可能你的系统比较新或者你从来没有设置过这个环境变量。

设置了之后我们就可以,正常的运行我们的可执行程序了:

那我们最后再来做一个实验,就是如果同一套方法,我们机提供了静态库也提供了动态库,编译器会选择哪种库呢?

我们可以先将两个库拷贝到同一个目录下:

然后我们在重新生成可执行程序,再来查看一下它所依赖的库:

从结果中我们可以看到,默认依赖的是动态库,虽然这里找不到,但是也能显示出来我们所依赖的是哪一个库(找不到是因为我们现在库的路径变了,而且我们也把原来的动态库给删除了)。

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

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

相关文章

《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述(12)

接前一篇文章:《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述(11) 4.2 PCIe体系结构的组成部件 PCIe总线作为处理器系统的局部总线,其作用与PCI总线类似,主要目的是为了连接处理器系统中的外部设备…

CNN应用Keras Tuner寻找最佳Hidden Layers层数和神经元数量

介绍: Keras Tuner是一种用于优化Keras模型超参数的开源Python库。它允许您通过自动化搜索算法来寻找最佳的超参数组合,以提高模型的性能。Keras Tuner提供了一系列内置的超参数搜索算法,如随机搜索、网格搜索、贝叶斯优化等。它还支持自定义…

.NET高级面试指南专题六【线程安全】5种方法解决线程安全问题

前言 多线程编程相对于单线程会出现一个特有的问题,就是线程安全的问题。所谓的线程安全,就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且…

备战蓝桥杯---动态规划(基础1)

先看几道比较简单的题&#xff1a; 直接f[i][j]f[i-1][j]f[i][j-1]即可&#xff08;注意有马的地方赋值为0&#xff09; 下面是递推循环方式实现的AC代码&#xff1a; #include<bits/stdc.h> using namespace std; #define int long long int a[30][30]; int n,m,x,y; …

机器学习---学习与推断,近似推断、话题模型

1. 学习与推断 基于概率图模型定义的分布&#xff0c;能对目标变量的边际分布&#xff08;marginal distribution&#xff09;或某些可观测变量 为条件的条件分布进行推断。对概率图模型&#xff0c;还需确定具体分布的参数&#xff0c;称为参数估计或学习问 题&#xff0c;…

AcWing 1224 交换瓶子(简单图论)

[题目概述] 有 N 个瓶子&#xff0c;编号 1∼N&#xff0c;放在架子上。 比如有 5 个瓶子&#xff1a; 2 1 3 5 4 要求每次拿起 2 个瓶子&#xff0c;交换它们的位置。 经过若干次后&#xff0c;使得瓶子的序号为&#xff1a; 1 2 3 4 5 对于这么简单的情况&#xff0c;显然&a…

第5章 数据库操作

学习目标 了解数据库&#xff0c;能够说出数据库的概念、特点和分类 熟悉Flask-SQLAlchemy的安装&#xff0c;能够在Flask程序中独立安装扩展包Flask-SQLAlchemy 掌握数据库的连接方式&#xff0c;能够通过设置配置项SQLALCHEMY_DATABASE_URI的方式连接数据库 掌握模型的定义…

Kafka集群安装与部署

集群规划 准备工作 安装 安装包下载&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1BtSiaf1ptLKdJiA36CyxJg?pwd6666 Kafka安装与配置 1、上传并解压安装包 tar -zxvf kafka_2.12-3.3.1.tgz -C /opt/moudle/2、修改解压后的文件名称 mv kafka_2.12-3.3.1/ kafka…

【C++】引用与内联

个人主页 &#xff1a; zxctsclrjjjcph 文章封面来自&#xff1a;艺术家–贤海林 如有转载请先通知 文章目录 1. 前言2. 引用2.1 引用概念2.2 引用使用场景2.3 引用特性2.4 引用和指针的区别2.5 传值、传引用效率比较2.5.1 值和引用的作为返回值类型的性能比较 3. 内联函数3.1 …

Ansible copy模块 复制文件使用 主服务器 给副服务器 复制文件使用 指定文件权限 覆盖备份等

目录 参数复制文件然后进行同时复制操作 给定内容生成文件&#xff0c;并制定权限验证 关于覆盖先查看当前内容覆盖并备份查看文件权限 还有有没有备份查看文件内容 参数 这个模块用于将文件复制到远程主机&#xff0c;同时支持给定内容生成文件和修改权限等。   其相关选项…

【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性

如何使用关联对象给分类实现一个weak的属性 通过关联对象objc_setAssociatedObject中的策略policy可知&#xff0c;并不支持使用weak修饰对象属性&#xff1a; typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {OBJC_ASSOCIATION_ASSIGN 0, //assignOBJC_ASSOCIATION…

Android:Volley框架使用

3.15 Volley框架使用 Volley框架主要作为网络请求,图片加载工具。当应用数据量小、网络请求频繁,可以使用Volley框架。 框架Github地址:https://github.com/google/volley Volley框架的简单使用,创建项目Pro_VolleyDemo。将Github上下载Volley框架源代码,volley-master.zi…

Linux中ps/kill/execl的使用

ps命令&#xff1a; ps -aus或者ps -ajx或者 ps -ef可以查看有哪些进程。加上 | grep "xxx" 可以查看名为”xxx"的进程。 ps -aus | grep "xxx" kill命令&#xff1a; kill -9 pid 杀死某个进程 kill -l 查看系统有哪些信号 execl函数&#…

JavaScript滚动事件

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 滚动是网页交互不可或缺的一部分。监听页面和元素的滚动事件,可以帮助…

跟着cherno手搓游戏引擎【22】CameraController、Resize

前置&#xff1a; YOTO.h: #pragma once//用于YOTO APP#include "YOTO/Application.h" #include"YOTO/Layer.h" #include "YOTO/Log.h"#include"YOTO/Core/Timestep.h"#include"YOTO/Input.h" #include"YOTO/KeyCod…

力扣刷题之旅:进阶篇(五)—— 动态规划(DP)的妙用

力扣&#xff08;LeetCode&#xff09;是一个在线编程平台&#xff0c;主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目&#xff0c;以及它们的解题代码。 --点击进入刷题地址 引言&#xff1a; 在算法的世界中&#xff0c;动态规划&#xff…

[HTTP协议]应用层的HTTP 协议介绍

目录 1.前言 2.使用fiddler抓包来观察HTTP协议格式 3.HTTP协议的基本格式 2.1请求 2,1.1首行 2.1.2请求头 2.1.3空行 2.2响应 2.2.1首行 2.2.2响应头 键值对 ​编辑2.2.3空行 2.2.4载荷(响应正文) 3.认识URL 3.1关于URL encode 1.前言 我们在前面的博客中,简单的…

力扣231. 2 的幂(数学,二分查找,位运算)

Problem: 231. 2 的幂 文章目录 题目描述思路即解法复杂度Code 题目描述 思路即解法 思路1&#xff1a;位运算 1.易验证2的幂为正数&#xff1b; 2.易得2的幂用二进制表示只能有一个位为数字1 3.即将其转换为二进制统计其二进制1的个数 思路2&#xff1a;数学 当给定数n大于1时…

DNS 域名系统——应用层

目录 1 域名系统 DNS 1.1 域名系统 1.2 互联网的域名结构 1.2.1 顶级域名 TLD(Top Level Domain) (1) 国家顶级域名 nTLD (2) 通用顶级域名 gTLD (3) 基础结构域名 (infrastructure domain) 1.3 域名服务器 1.3.1 域名服务器的四种类型 &#xff08;1…

Springboot拦截器中跨域失效的问题、同一个接口传入参数不同,一个成功,一个有跨域问题、拦截器和@CrossOrigin和@Controller

Springboot拦截器中跨域失效的问题 一、概述 1、具体场景 起因&#xff1a; 同一个接口&#xff0c;传入不同参数进行值的修改时&#xff0c;一个成功&#xff0c;另一个竟然失败&#xff0c;而且是跨域问题拦截器内的request参数调用getHeader方法时&#xff0c;获取不到前端…