动静态库-动态库加载

news2024/11/20 18:26:59

动静态库

  • 前言
    • 引入
  • 一、静态库
    • 1. 创建静态库
      • ①原理
      • ②创建
    • 2. 使用静态库
      • ①借助编译选项
      • ②只需要带库名
    • 3. 小结
  • 二、动态库
    • 1. 创建动态库
    • 2. 使用动态库
  • 三、 动态库加载原理——进程地址空间
    • 1. 地址
      • ①程序没有被加载前的地址
      • ②程序加载后的地址
    • 2. 原理
      • ①动态库的地址
      • ②原理

前言

讲编译工具gcc时,讲到链接库,其中说静态链接和动态链接的相关知识,并且介绍了一些拓展命令,eg:od/file/ldd/readelf

接下来,目标:深入理解动静态库

引入

当我们想把我们写的代码给别人使用有两种方式:

  1. 直接把我们写的源文件和头文件给别人。
  2. 把源代码打包成库,加上头文件给别人。(库+.h)

优劣:

  • 第一种:相当于把底层的东西直接暴露给使用者。
  • 第二种:相当于只把使用说明(头文件)展示给使用者,使用者只知道如何调用方法,而不知道底层具体如何实现。

一、静态库

1. 创建静态库

①原理

生成库

  1. 静态库其实就是一堆.o文件打包形成
  2. 要先把源文件提前编译成目标文件
  3. 所以生成的库,人是读不出来的。

②创建

创建静态库

2. 使用静态库

开发者的库(xxx.a) + 使用者编写的代码(xxx.c) = 可执行程序(xxx.exe)

①借助编译选项

测试代码:
测试代码

  1. 直接编译一下试试:
    运行结果
    结果:运行失败
    结论:我们这里测试的头文件,不在系统指定的目录下(usr/include),所以gcc找不到这个头文件.
  1. 告诉编译器,头文件的位置。使用-I选项,后面跟头文件所在路径即可
    命令:gcc main.c -I ./lib/include
    运行结果
    结果:运行失败
    结论:这里不再是找不到头文件了,使用我们指定头文件路径这个是正确的,但是结果中的报错,是变量未定义报错,那很明显是链接报错,找不到静态库
  1. 告诉编译器,库的位置。使用-L选项,后面跟库文件所在路径即可
    命令:gcc main.c -I ./lib/include -L ./lib/mymathlib 运行结果
    结果:运行失败
    结论:指定库文件路径依旧不行,因为还要指定库文件名。为什么头文件就可以不指定文件名,那是因为在main.c中已经包含了头文件名
  1. 告诉编译器库的名称。使用-l选项,后紧跟库名。注:库名称是去掉lib和.a后缀。libmymath.a:mymath就是库名
    命令:gcc main.c -I ./lib/include -L ./lib/mymathlib -lmymath
    运行结果
    运行结果:
    运行结果

②只需要带库名

方法:

  1. 把头文件和库放到系统指定路径下,就可以只带-l编译选项。其实这个动作就是库的安装
  2. 头文件和库在系统指定的目录下,建立软链接,就可以只带-l编译选项(这个下面演示一下)

演示:给头文件和库在系统指定目录下建立软链接

  1. 对头文件建立软链接 对头文件建立软链接
    • 查看系统指定目录下的软链接:建立软链接成功 软链接
    • 因为是在/usr/include目录下建立了一个路径的软链接,所以在测试使用的要包含头文件的方式要改变
      头文件包含
  • 运行程序
    命令:gcc main.c -L ./lib/mymathlib -lmymath 注意:因为头文件的软链接被放到系统的指定目录下,所以就不需要使用-I选项
    运行结果:
    运行结果
  1. 对静态库建立软链接 对静态库建立软链接
  • 运行程序
    命令:gcc main.c -I ./lib/include -lmymath 注:因为库的软链接被放到系统的指定目录下,所以就不需要使用-L选项
    运行结果:
    运行结果

注:把头文件和库在系统指定的目录下,一起建立软链接,就可以只适用-l选项。上面的演示,我没有放在一起,所以要么带L选项,要么就要带I选项

3. 小结

使用总结:

  1. 大写i(I)指定头文件所在路径
  2. 大写l(L)指定库所在路径
  3. 小写l(l)指定库名称。注:库名称是去掉lib和.a后缀

静态库总结:

  1. 把头文件和库都移动到系统的指定目录下就可以不加选项I和L,但是只要使用第三方库必定要使用gcc -l[库名](第一二方,可以理解为系统和语言层次的库)
  2. 在系统指定目录下建立软链接也可以不加选项I和L
  3. 理解库中的全局变量
  4. 如果系统只提供静态库,则编译器就只能进行静态链接
  5. 可以链接多个库

二、动态库

1. 创建动态库

创建动态库的两个关键命令

  1. 生成目标文件:gcc -fPIC -c $^
  2. 生成动态库:gcc -shared -o $@ $^

生成动态库,并且分类:
创建动态库

注:发现生成的动态库具有可执行权限 生成的动态库
虽然具有可执行权限,但是并不能执行
可执行权限:以可执行程序的方式加载到内存

2. 使用动态库

测试代码:
测试代码

  1. 编译:根据对静态库使用的编译选项,指定路径测试动态库
    命令:gcc test.c -I ./mylib/include -L ./mylib/lib -lmymethod
    编译
    结果:编译成功,生成了可执行程序a.out
  2. 运行:
    运行结果
    结果:运行失败,没有发现共享库

注:ldd命令:显示可执行程序链接的动态库

  1. 原因:
    • 因为是动态库要加载到内存。编译器确实知道了动态库的位置,但是我系统不知道——加载器
  2. 解决:
    • 拷贝到系统默认的库路径/lib64 or /usr/lib64(最常用,我们以后使用的库,都是别人成熟的库,所以可以直接安装都系统指定的目录下)
    • 在系统默认的库路径(/lib64 or /usr/lib64)下建立软链接
    • 将自己的库所在的路径,条件到系统的环境变量LD_LIBRARY_PATH中
    • /etc/ld.so.conf.d建立自己的动态库路径的配置文件,然后加载(ldconfig)

这里测试一下后面两种解决办法,前面俩种拷贝和建立软链接就不测试了

  1. 环境变量:
    解决
    • 注:重启Xshell之后,环境变量就恢复了,所以如果想一直可以,就要在配置文件中进行添加
  2. 建立自己的动态库路径的配置文件
    解决
    • 注:
      1. 添加配置文件操作需要在root账号下
      2. 进入/etc/ld.so.conf.d目录下
      3. 创建一个以.conf为后缀的文件,名字任意
      4. 文件内填入需要使用的动态库路径
      5. ldconfig更新

如果还要添加别的路径下的动态库,需要再创建文件然后写入路径

注:外部库很多,eg:ncurses库——基于终端的图形界面库(可以上网搜下载一下玩玩)

三、 动态库加载原理——进程地址空间

  1. 动态库在进程运行的时候,是要被加载的
  2. 常见的动态库被所有的可执行程序使用(eg:c标准库就被Linux很多指令共享使用)。动态库 —— 共享库

所以动态库在系统加载后,会被所以进程共享

1. 地址

①程序没有被加载前的地址

问题:程序在编译好之后,没有运行前,内部有地址吗?

通过命令,可以把编译好的程序反汇编出来
命令:objdump -S a.out
在这里插入图片描述

所以程序编译好之后,内部就有地址了,而且现在的编译器大多会采用平坦模式。这个地址就是虚拟地址,更准确的说是逻辑地址,对于目前来说,二者并没有什么区别,所以还没加载到内存,这个虚拟地址已经出现了
注:
平坦模式:内存管理模式,它将整个内存地址空间视为一个连续的线性地址空间,从0递增,程序可以直接使用线性地址进行访问

②程序加载后的地址

把可执行程序加载到内存,必然要先形成好自己的PCB
图解:
图解

2. 原理

①动态库的地址

图解:
图解

所以也得出为什么采用fPIC(产生位置无关码),因为直接用偏移量对库中的函数进行编址。而静态库就不需要这样做,因为静态库是直接拷贝到可执行程序中去的,直接编址就可以

②原理

图解:
图解

  1. 结论:虚拟地址和物理地址建立映射,从此执行任何的代码,都是在我们的进程地址空间中执行的
  2. 事实:系统在运行的时候,一定存在很多个动态库。所以OS就得管理起来 —— 先描述再组织。所以系统中所有库的加载情况,OS都清楚

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

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

相关文章

实用干货:分享一个自动切换输入法的IDE插件

大家好,我是大澈! 本文约1100字,整篇阅读大约需要3分钟。 关注微信公众号:“程序员大澈”,免费加入问答群,一起交流技术难题与未来! 现在关注公众号,免费送你 ”前后端入行大礼包…

C++入门全集(5):内存管理

前言 一、内存区域划分 二、C的内存管理方式 2.1 对内置类型 2.2 对自定义类型 三、new和delete的底层实现 四、new和delete的原理 五、定位new 六、malloc/free和new/delete 前言 在C中,内存管理是不可避免的一门必修课。C对内存的自由度使其获得了更高的…

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:点击回弹效果)

设置组件点击时回弹效果。 说明: 从API Version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 clickEffect clickEffect(value: ClickEffect | null) 设置当前组件点击回弹效果。 系统能力: SystemCapabilit…

no declaration can be found for element ‘rabbit:connection-factory‘

spring-mvc 配置 rabbitmq 出现问题。 我的解决方案如下: 1 找到配置文件 spring-rabbitmq.xml 我的配置文件叫:spring-rabbitmq.xml,你们按照自己的查找。 2 定位如下URI 接着 Ctrl鼠标左键 3 确定spring-rabbit-x.x.xsd 按照步骤2 &…

回溯算法01-组合(Java)

1.组合 题目描述 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1: 输入:n 4, k 2 输出: [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4]]示例 2: 输入&#x…

巨控GRM561/562/563Y系列标准OPCUA客户端协议访问西门子1200PLC

驱动名称: 标准OPCUA客户端协议 描述: 本机作主机,读写其他外部设备数据 ◆寄存器类型: UA寄存器 地址范围: 可用数据类型:[BIT] [INT8] [INT16] [INT32] [INT64] [UINT8] [UINT16] [UINT32] [UINT64] [FLOAT32] [FLOAT64] ◆寄存器类型: 设备特殊寄存器 地址…

光影交织:汽车穿越隧道的视觉盛宴

在繁忙的城市中,隧道成为了连接两端的重要通道。而对于汽车来说,穿越隧道不仅是一次简单的空间转移,更是一场融合了视觉、技术与安全的独特体验。 当汽车缓缓驶入隧道,外界的光线逐渐减弱,隧道内部的光线开始发挥作用。…

Centos7 排查流量异常进程

首先可以根据漏洞捕获的相关信息的目的端口,查看下监听详情,如:netstat -lnp|grep 9015 执行命令输出如下所示内容: 最终可排查得知: 也可以通过以下方式进行详细排查: 1、基本知识 1.1、iftop介绍 一般…

java核心技术第十一章学习笔记

swing常用用户界面组件 Swing和模型-视图-控制器设计模式 模型:存储内容,必须实现改变内容和查找内容的方法,不可见 视图:显示内容 控制器:处理用户输入 一个模型可以有多个视图,每个视图可以显示全部…

稀碎从零算法笔记Day6-LeetCode:长度最小的子数组

前言:做JD的网安笔试题,结果查找子串(单词)这个操作不会。痛定思痛,决定学习滑动数组 题型:数组、双指针、滑动窗口 链接:209. 长度最小的子数组 - 力扣(LeetCode) 来…

使用awk和正则表达式过滤文本或字符串 - 详细指南和示例

当我们在 Linux 中运行某些命令来读取或编辑字符串或文件中的文本时,我们经常尝试将输出过滤到感兴趣的特定部分。这就是使用正则表达式派上用场的地方。 什么是正则表达式? 正则表达式可以定义为表示多个字符序列的字符串。关于正则表达式最重要的事情之…

网络编程的学习

思维导图 多路复用代码练习 select完成TCP并发服务器 #include<myhead.h> #define SER_IP "192.168.125.73" //服务器IP #define SER_PORT 8888 //服务器端口号int main(int argc, const char *argv[]) {//1、创建用于监听的套接字int sfd -1;s…

文件操作与IO(3) 文件内容的读写——数据流

目录 一、流的概念 二、字节流代码演示 1、InputStream read方法 第一个没有参数的版本&#xff1a; 第二个带有byte数组的版本&#xff1a; 第三个版本 搭配Scanner的使用 2、OutputStream write方法 第一个版本&#xff1a; 第二个写入整个数组版本&#xff1a; …

小米澎湃和华为原生鸿蒙,那个更有发展前景?

小米的澎湃系统暂时不了解&#xff0c;但华为的鸿蒙系统值得一说。 就目前鸿蒙而言&#xff1b;24年初鸿蒙星河版面向开发者开放申请。其底座全线自研&#xff0c;去掉了传统的 Linux 内核以及 AOSP 安卓开放源代码项目等代码&#xff0c;仅支持鸿蒙内核和鸿蒙系统的应用。星河…

【UE 材质 Niagara】爆炸效果

目录 效果 步骤 一、材质部分 二、Niagara部分 效果 步骤 一、材质部分 1. 创建一个材质&#xff0c;这里命名为“M_Burst” 打开“M_Burst”&#xff0c;设置混合模式为半透明&#xff0c;设置着色模型为无光照&#xff0c;勾选双面显示 在材质图表中首先创建扰动效果 其…

网络编程作业day4

广播模型&#xff1a; 发送端&#xff1a; #include <myhead.h> int main(int argc, const char *argv[]) {//创建套接字int sfdsocket(AF_INET,SOCK_DGRAM,0);if(sfd-1){perror("socket error");return -1;}//设置套接字允许广播属性int broadcast1;if(sets…

蓝桥杯备赛 day2 | 4. 付账问题 5. 数字三角形

付账问题&#xff0c;关键是要了解整型的范围&#xff0c;确定获取输入数据的变量类型 需要注意的是int的十进制范围-32768 ~ 32767&#xff0c;那么我们可以知道&#xff0c;人数n是可以用int来装的&#xff0c;需付款数S应该是long long&#xff0c;获取的每个人初始钱数也应…

自动驾驶预测与决策规划(nuplan数据集)

欢迎大家关注我的B站&#xff1a; 偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com) 目录 1.概述 2 数据采集 3.开环与闭环仿真 4.数据注释 5.场景 6.规划框架 6.1Train 6.2Simulation 6.3Metric 6.4Visualization 7.下载…

JasperStudio中TextField文本框组件渲染之后,出现行间距不一致的问题

目录 1.1、问题描述 1.2、解决方案 1.1、问题描述 最近在处理线上遇到的一个问题,是有关JasperReports报表相关的问题,问题背景大概是这样的:我们的项目中使用了JasperReports来渲染报表,其中使用到了Text Field文本框组件,但是问题是渲染出来的数据直接会出现一些间距…

JAVA如何利用接口实现多继承问题

hello&#xff0c;上文带大家学习了java中类的继承&#xff0c;我们可以创建一个父类&#xff0c;将类中的共性抽取出来&#xff0c;通过子类继承的方式来实现代码的复用。今天带大家学习不同类之间的另外几种关系&#xff0c;即多态抽象类和接口。 多态的概念 多态&#xff0c…