本章重点:

动静态库的制作,使用和查找

可执行程序ELF格式

可执行程序的加载过程

虚拟地址空间和动态库加载的过程


动静态库的制作,使用和查找

  1.在了解库的制作之前,我们首先需要知道什么是库。库是写好的现有的,成熟的,可以反复复用的代码。有了库我们只需要有头文件和知道对应的调用方法就可以使用库中的函数,不需要每次都造轮子手撕,我们平时使用的c/c++标准库都是这样封装的库,然后将头文件暴露出来给用户调用。

  2.本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库分为静态库 .a[Linux]、.lib[windows],和动态库.so[Linux]、.dll[windows]。

  3.静态库:静态库(.a):程序在编译链接的时候把库的代码链接到可执⾏⽂件中,程序运⾏的时候将不再需要静态库,静态库可以理解为多个.o文件的集合。⼀个可执⾏程序可能⽤到许多的库,这些库运⾏有的是静态库,有的是动态库,⽽我们的编译默认为动态链接库,只有在该库下找不到动态.so的时候才会采⽤同名静态库。我们也可以使⽤ gcc的 -static 强转设置链接静态库。

  这里使用之前在基础IO文章中写的mylib.c和mylib.h来封装一个静态库。

  需要先构造一个makefile文件帮助我们快速构造静态库,这里先用 ar 工具将 mylib.o 添加到libmylib.a 中,ar是gnu的一个归档工具,用于创建、修改、提取 .a 静态库文件。rc表示replace和create,表示如果这个库存在就替换,不存在就创建它。

  此时我们并没有.o文件,所以还需要将.c文件编译生成.o文件,%.o:%.c,这是一个通配规则,意思是:让所有的 .c文件可以对应生成 .o 文件,此处只有一个.c文件,但是静态库可以由多个.o文件链接形成。

  然后编写一个清理指令即可。

  执行make指令我们就得到了一个静态链接库

  那么如何使用静态链接库呢?我们只需要在需要调用静态库函数的文件包含对应的头文件,然后编译的时候带对应的库即可,这里又有三种方法链接库:

  (1)库文件和头文件都安装在系统默认路径,比如:头文件在 /usr/include/,库文件在 /usr/lib//usr/lib64/

  (2)库文件和源文件在同一目录下,直接使用指令g++ -o main main.c. -L. -lmylib,-L告诉编译器在当前目录下寻找库,-l链接名为libmylib.a的静态库

  (3)头文件和库文件分别在独立目录,此时mylib.h文件在Include文件中,libmylib.a在lib文件中,使用指令g++ -o main -main.c -Iinclude -Llib -lmylib,需要使用-I指定头文件路径,-L指定库的路径,-l指定连接哪个库

  注意形成执行程序以后即使删除libmylib.a也可以正常运行可执行程序,因为静态库已经连接到可执行程序的内部了,所以我们可以看到的是此时的可执行程序会变得很大。

  这里我们还可以在makefile内部使用指令将生成的静态库进行打包压缩然后发送给别人使用,此时只需要解压然后使用第三种方法也就是头文件和库文件分别在不同目录就可以链接库了。

  所以我们完整的发布流程就是:先将.o文件链接成静态库,然后打包压缩头文件和静态库

  4.动态库:程序在运⾏的时候才去链接动态库的代码,多个程序共享使⽤库的代码。 ⼀个与动态库链接的可执⾏⽂件仅仅包含它⽤到的函数⼊⼝地址的⼀个表,⽽不是外部函数所在⽬标⽂件的整个机器码。在可执⾏⽂件开始运⾏以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking) 。动态库可以在多个程序间共享,所以动态链接使得可执⾏⽂件更⼩,节省了磁盘空间。操作系统采⽤虚拟内存机制允许物理内存中的⼀份动态库被要⽤到该库的所有进程共⽤,节省了内存和磁盘空间

  动态库的生成过程与静态库类似,不一样的是生成库的指令以及生成.o文件的指令,shared: 表⽰⽣成共享库格式,fPIC:产⽣位置⽆关码(position independent code) ,库名规则:libxxx.s。

g++ -fPIC -c mylib.cpp -o mylib.o   # 先编译为位置无关的目标文件
g++ -shared -o libmylib.so mylib.o  # 再链接为共享库

  动态库的使用方法也有三种:

  (1)头⽂件和库⽂件安装到系统路径下,使用命令g++ main.c -lmylib即可链接动态库

(2)头文件与库文件与源文件同目录,使用命令g++ main.c -L. -lmylib

  (3)头文件与库文件分别有独立的路径,使用命令g++ main.c -Iinclude -Llib -lmylib,注意当前lib路径下如果有同名动态库和静态库优先会链接动态库。

  可以使用ldd 来查看链接了哪些库,此时发现确实是链接了我们的动态库

  但是运行的时候发现报错了,这是因为程序在运行时找不到共享库 libmylib.so,因为Linux 默认只在系统路径(如 /lib, /usr/lib)下查找共享库,不会自动查找 -L 指定的路径!

  这里有三种方法可以解决,(1)拷贝.so文件到系统共享库的路径下 (2)向系统共享库路径下建立同名的软连接 (3)更改环境变量 这里使用方法三来演示:

  这样就可以增加系统查找共享库的路径了。需要注意的是前面两种方法操作以后都需要使用sudo ldconfig来更新系统共享库的索引。

可执行程序ELF格式

  在Linux之中有四种文件都是ELF格式的文件,有xxx.o文件,xxx.so文件,xxx可执行文件以及内核转储文件。一个ELF格式的文件由四个部分组成:

  (1)ELF头:描述文件的主要特性,其文件的开始位置,它的最主要目的是定位文件的其他部分。

  (2)程序表头:  列举了所有有效的段(segments)和他们的属性。表⾥记着每个段的开始的位置和位移(offset)、⻓度,毕竟这些段,都是紧密的放在⼆进制⽂件中,需要段表的描述信息,才能把他们每个段分割开。

  (3)节头表(Section header table) :包含对节(sections)的描述。

  (4)节(Section ):ELF⽂件中的基本组成单位,包含了特定类型的数据。ELF⽂件的各种信息和 数据都存储在不同的节中,如代码节存储了可执⾏代码,数据节存储了全局变量和静态数据等。

可执行程序的加载过程

  ELF形成可执行程序分为以下两部:(1)将多份c/c++源代码翻译成为.o文件 (2)将多份.o文件的section进行合并。

  所以链接其实就像将一个个的相同属性的section进行合并。

虚拟地址空间和动态库加载的过程

  一个可执行程序在加载到内存之前有没有地址?进程mm_struct、vm_area_struct在进程刚刚创建的时候,初始化数据从哪⾥来的⼀个ELF程序,在没有被加载到内存的时候,本来就有地址,当代计算机⼯作的时候,都采⽤"平坦模式"进⾏⼯作。所以也要求ELF对⾃⼰的代码和数据进⾏统⼀编址,下⾯是 objdump -S 反汇编之后的代码

  左侧的就是ELF的虚拟地址,其实,严格意义上应该叫做逻辑地址(起始地址+偏移量), 但是我们认为起始地址是0.也就是说,其实虚拟地址在我们的程序还没有加载到内存的时候,就已经把可执⾏程序进⾏统⼀编址了.

  进程mm_struct、vm_area_struct在进程刚刚创建的时候,初始化数据从哪⾥来的?从ELF各个 segment来,每个segment有⾃⼰的起始地址和⾃⼰的⻓度,⽤来初始化内核结构中的[start, end]等范围数据,另外在⽤详细地址,填充⻚表。

  所以虚拟地址空间是cpu/编译器/OS协调操作下的共同产物。

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

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

相关文章

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | IncrementingCounter(递增计数器)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— IncrementingCounter组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ 使用 Vue 3 的 Composition API 和 <script setup&g…

简约求职简历竞聘工作求职PPT模版共享

简历竞聘&#xff0c;自我介绍&#xff0c;个人简历&#xff0c;工作求职PPT模版&#xff0c;岗位竞聘求职简历PPT模版&#xff0c;低调绿自我介绍PPT模版&#xff0c;简约求职简历PPT模版&#xff0c;个人介绍PPT模版&#xff0c;我的简历PPT模版&#xff0c;个人求职简介PPT模…

Java大厂面试攻略:Spring Boot与微服务架构深度剖析

问题一&#xff1a;Spring Boot 的自动配置原理是什么&#xff1f; 简洁面试回答&#xff1a; Spring Boot 的自动配置基于条件化配置&#xff0c;通过 Conditional 注解实现&#xff0c;根据项目中依赖和环境自动装配 Bean。 详细解析&#xff1a; Spring Boot 自动配置的核…

Windows核心端口攻防全解析:135、139、445端口的技术内幕与安全实践

Windows核心端口攻防全解析&#xff1a;135、139、445端口的技术内幕与安全实践 引言&#xff1a;Windows网络通信的命脉 在Windows网络生态系统中&#xff0c;135、139和445端口犹如网络通信的"大动脉"&#xff0c;承载着关键的系统服务和网络功能。这些端口不仅是…

从生活场景学透 JavaScript 原型与原型链

一、构造函数&#xff1a;以 “人” 为例的对象工厂 1. 生活场景下的构造函数定义 我们以 “人” 为场景创建构造函数&#xff0c;每个人都有姓名、年龄等个性化属性&#xff0c;也有人类共有的特征&#xff1a; // 人类构造函数 function Person(name, age) {this.name na…

学c++ cpp 可以投递哪些岗位

此次描述知识针对应届生来说哈&#xff0c;如果是社招&#xff0c;更多是对于你目前从事的方向&#xff0c;技术栈进行招聘就好了。 此次编写是按照boss上岗位筛选的方式进行编写的&#xff0c;其实投简历一般也是用boss&#xff0c;后面也会出一篇文章给大家介绍一般找工作都用…

【Docker基础】Docker镜像管理:docker rmi、prune详解

目录 引言 1 Docker镜像管理概述 1.1 为什么需要镜像清理&#xff1f; 1.2 镜像生命周期管理 2 docker rmi命令详解 2.1 基本语法 2.2 常用选项 2.3 删除单个镜像 2.4 删除多个镜像 2.5 强制删除镜像 2.6 删除所有镜像 3 docker rmi工作原理 3.1 镜像删除流程 3.…

57-Oracle SQL Profile(23ai)实操

在上一期中说到了SQL Tuning Advisor其中一个影响对象就是SQL Profile&#xff0c;同样在管理和应用开发中,SQL性能优化是个任重道远的工作&#xff0c;低效的SQL语句让应用响应缓慢,用户整体体验下降,拖垮搞蹦整个系统都有可能。Oracle数据库提供了多种组合工具&#xff0c;有…

man的使用

man的使用 文章目录 man的使用基本用法&#xff1a;常见 man 命令操作&#xff1a;man 命令的章节&#xff1a;示例&#xff1a; man 是 Linux 和 macOS 系统中的命令&#xff0c;用于查看命令和程序的手册页&#xff08;manual pages&#xff09;。手册页包含了关于命令、函…

【蓝牙】手机连接Linux系统蓝牙配对,Linux Qt5分享PDF到手机

要实现手机连接 A40i Linux 系统并通过蓝牙接收 PDF 文件&#xff0c;可以按照以下步骤操作&#xff1a; 1. 配置 Linux 蓝牙功能 确保开发板上的蓝牙模块已正确驱动并支持蓝牙协议栈。 安装蓝牙工具&#xff1a; bash sudo apt install bluetooth bluez bluez-tools 启动蓝…

1432. 改变一个整数能得到的最大差值

1432. 改变一个整数能得到的最大差值 题目链接&#xff1a;1432. 改变一个整数能得到的最大差值 代码如下&#xff1a; class Solution { public:int maxDiff(int num) {string s to_string(num);function<int(char, char)> replace_stoi [&](char old_char, cha…

解密 Spring MVC:从 Tomcat 到 Controller 的一次完整请求之旅

今天&#xff0c;想和你聊一个我们每天都在打交道&#xff0c;但可能不曾深入思考的话题&#xff1a;当一个 HTTP 请求从浏览器发出&#xff0c;到最终被我们的 Spring Controller 处理&#xff0c;它到底经历了一场怎样的旅程&#xff1f; 理解这个流程&#xff0c;不仅仅是为…

在 Java 中操作 Map时,高效遍历和安全删除数据

在 Java 中操作 Map 时&#xff0c;高效遍历和安全删除数据可以通过以下方式实现&#xff1a; 一、遍历 Map 的 4 种高效方式 1. 传统迭代器&#xff08;Iterator&#xff09; Map<String, Integer> map new HashMap<>(); map.put("key1", 5); map.pu…

力扣-136.只出现一次的数字

题目描述 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 class Solution {public i…

Go 网络编程:HTTP服务与客户端开发

Go 在标准库中内置了功能强大的 net/http 包&#xff0c;可快速构建高并发、高性能的 HTTP 服务&#xff0c;广泛应用于微服务、Web后端、API中间层等场景。 一、快速创建一个HTTP服务 示例&#xff1a;最简Hello服务 package mainimport ("fmt""net/http&quo…

【Prism】 实现注入的几个标准化步骤(相机举例)

📸 Prism 架构中如何优雅地注册和注入相机服务 在开发基于 Prism + WPF 的应用时,合理使用依赖注入(DI)可以大大提高系统的可维护性和扩展性。本文以一个多相机平台管理系统为例,展示如何通过接口、枚举、容器注册等方式,实现相机服务的灵活配置与使用。 🧩 一、定义…

vue3组件式开发示例

1&#xff0c;定义组件&#xff08;根据实际调整提交分析结果方法&#xff09; <template><!-- 分析结果上传对话框组件 --><el-dialogv-model"uploadResultDialog":title"title":width"width":before-close"handleBeforeC…

基于arm linux的bluealsa开启蓝牙A2DP和SCO录音功能

bluealsa的软件架构 #mermaid-svg-ohITacCRHItwRR1t {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ohITacCRHItwRR1t .error-icon{fill:#552222;}#mermaid-svg-ohITacCRHItwRR1t .error-text{fill:#552222;stroke:…

网页后端开发(基础3--Springboot框架)

web的服务器资源&#xff1a; 静态资源&#xff1a;服务器上存储的不会改变的数据&#xff0c;通常不会根据用户的请求而变化。比如&#xff1a;HTML、CSS、JS、图片、视频等&#xff08;负责页面展示&#xff09; 动态资源&#xff1a;服务器端根据用户请求和其他数据…

ROS通过urdf_to_graphiz对urdf和xacro文件进行结构可视化

对机器人的urdf文件进行结构可视化&#xff1a; 举例命令如下&#xff1a; urdf_to_graphiz go2_description.urdf 输出 .gv 和 .pdf文件&#xff0c;打开 pdf文件如图&#xff1a;