『 Linux 』 进程地址空间与动态库地址

文章目录

    • 逻辑地址
    • 进程地址空间
    • 指令的执行
      • 惰性加载(Lazy Loading)/延迟加载
    • 动态库的地址


逻辑地址

请添加图片描述

在程序加载进内存之前,即编译之后就已经形成了地址,在编译之后的地址被称为 逻辑地址;

  • 逻辑地址

    逻辑地址是程序在编译时产生的地址;

    在编译阶段,编译器会为程序中的每个变量函数等元素生成一个相对的地址即逻辑地址;

    这些地址是相对于程序开始的位置而言,他们不是物理内存地址;

假设一个源文件(main.c)中内容为:

#include <stdio.h>

int main()
{
  printf("hello world\n");
  int x = 10;
  int y = 20;
  int z = x + y;
  printf("z = x + y = %d\n", z);
  return 0;
}

使用gcc将源文件进行编译链接形成可执行程序a.out;

gcc main.c

利用objdump工具选择-S选项可以观察可执行程序的反汇编情况,在此处即objdump -S a.out;

$ objdump -S a.out 

a.out:     file format elf64-x86-64


Disassembly of section .init:

0000000000400418 <_init>:
  400418:   48 83 ec 08             sub    $0x8,%rsp
  40041c:   48 8b 05 d5 0b 20 00    mov    0x200bd5(%rip),%rax        # 600ff8 <__gmon_start__>
  400423:   48 85 c0                test   %rax,%rax
  400426:   74 05                   je     40042d <_init+0x15>
  400428:   e8 53 00 00 00          callq  400480 <__gmon_start__@plt>
  40042d:   48 83 c4 08             add    $0x8,%rsp
  400431:   c3                      retq   

# ......
# 略....
# ......


0000000000400620 <__libc_csu_fini>:
  400620:   f3 c3                   repz retq 

Disassembly of section .fini:

0000000000400624 <_fini>:
  400624:   48 83 ec 08             sub    $0x8,%rsp
  400628:   48 83 c4 08             add    $0x8,%rsp
  40062c:   c3                      retq   

其中代码中40041840062c即为编译后所生成的逻辑地址;

源文件进行编译后生成可执行程序后将存在逻辑地址;

逻辑地址一般按照段进行分布,意思就是当编译过后最终的指令地址将以不同的段落进行分组;

每个分组被称为段描述,主要的段包括:

  • .text

    包含编译后的机器指令,即程序代码;

  • .data

    包含已初始化的全局变量和静态变量;

  • .rodata\.rdonly

    包含只读数据,例如字符串常量等;

  • .bss

    用于未初始化的全局变量和静态变量;

    一般情况下该段不占用实际的文件空间,但是会被标记在程序运行时需要分配的空间;

  • .symtab段(符号表),.strtab段(字符串表),.debug

    包含用于调试和符号解析的信息;

在使用objdump -S时主要为用户展示主要为.text段与源代码(如果可用)中的内容;

若是需要查看整体的逻辑地址段描述的细节可以使用-h选项:

$ objdump -h a.out 

a.out:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000400238  0000000000400238  00000238  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  0000000000400254  0000000000400254  00000254  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000400274  0000000000400274  00000274  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .gnu.hash     0000001c  0000000000400298  0000000000400298  00000298  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 #...
 
 19 .dynamic      000001d0  0000000000600e28  0000000000600e28  00000e28  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .got          00000008  0000000000600ff8  0000000000600ff8  00000ff8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .got.plt      00000038  0000000000601000  0000000000601000  00001000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .data         00000004  0000000000601038  0000000000601038  00001038  2**0
                  CONTENTS, ALLOC, LOAD, DATA
 23 .bss          00000004  000000000060103c  000000000060103c  0000103c  2**0
                  ALLOC
 24 .comment      00000059  0000000000000000  0000000000000000  0000103c  2**0
                  CONTENTS, READONLY

由于编译器并不知道程序将被加载到内存的具体的哪个位置,因此实际上是一种相对地址;

这些地址基于程序的起始点或某个特定段的起始点;


进程地址空间

请添加图片描述

程序在加载进内存后会成为进程,操作系统会为进程维护一个进程地址空间(虚拟内存)从而保持物理内存的安全性;

进程地址空间一般是由逻辑地址直接加载进内存的;

当逻辑地址被加载进内存时将会进行一个至进程地址空间的一个转换;

最终通过内存管理单元MMU与页表的映射机制映射至物理内存;


指令的执行

请添加图片描述

被编译生成的可执行程序在执行时将会加载进内存;

实际上加载进内存的即为可执行程序的代码和数据;

代码即为编译生成的各个指令集以及其虚拟地址的映射;

所以实际上指令集中的每个指令都将存在其对应的物理地址;

CPU中存在一个寄存器为EIPpc指针(指令寄存器);

当可执行程序被加载进内存成为进程时,操作系统将会给这个寄存器中存放这个进程代码的初始位置(虚拟地址 ),例如程序的入口点;

CPU将会根据EIP中指向的地址从进程的虚拟地址空间通过页表映射读取指令,并让EIP读取下一条指令的地址;

实际上EIP所读取到的指令地址都是虚拟地址;

在整个内存管理系统中,真正代表虚拟地址到物理地址映射的只能通过内存管理单元MMU与页表之间的映射;

以该图为例为以下几点:

  • 加载可执行程序

    可执行程序(如1.exe)从磁盘中加载进物理内存;

    加载过程中虚拟地址被分配给程序的各个部分(如代码段,数据段等);

  • 虚拟地址与物理地址的映射

    程序的虚拟地址包括代码段,已初始化数据段,堆,共享区等;

    页表负责将这些虚拟地址映射到物理地址;

  • 指令执行过程

    当进程被调度执行时,CPU中的EIP(指令寄存器)会指向进程代码的入口地址(虚拟地址);

    EIP寄存器中的地址通过页表映射到物理地址从而读取指令;

    CPU根据EIP指向的地址从进程的虚拟地址空间中读取指令并执行指令;

  • 内存管理单元(MMU)

    MMU在这个过程中起到关键作用,负责将虚拟地址转化为物理地址;

    MMU通过查询页表来完成地之间的转换;


惰性加载(Lazy Loading)/延迟加载

请添加图片描述

一般情况下一个可执行程序执行成为进程后操作系统将会优先为其维护对应的task_struct,进程地址空间,页表等结构体,但不一定会第一时间将所有的代码数据都加载进物理内存当中;

这是一种按需加载的策略;

当存运行过程中遇到了未被加载进物理内存的代码和数据时内存管理单元MMU将会触发缺页中断异常;

当这个异常被操作系统检测到时将会从磁盘中找到对应的代码数据将其加载进内存当中;

通过这种方式,操作系统可以有效管理有限的物理内存资源从而确保那些确实需要被立即访问的数据代码有限被加载进物理内存而不必一开始就分配所有资源从而提高系统的整体性能和响应速度;


动态库的地址

请添加图片描述

动态库是一个当一个可执行程序依赖时需要外部链接的库;

与静态库不同,在进行链接时静态库的代码数据将直接以拷贝的形式使得其能够与可执行程序融为一体;

而动态库则是当加载器将其加载到物理内存中就只会独一份并且可以被其他依赖该动态库的进程共享;

所以动态库在加载的过程中是不能确定其具体加载的位置的;

在使用gcc/g++生成动态库时需要使用选项gcc -fPIC,这个选项是生成一个与位置无关码;

  • 位置无关码(PIC)

    这是一种特殊类型的机器码,他可以在内存中的任何位置执行而不需要修改;

    这是通过确保代码执行时不依赖它的绝对地址来实现的,即代码对于数据的禁用是基于它当前的运行地址来计算的;

动态库被设计为可以被多个进程共享;

为了使不同的进程能够同时使用同一份物理内存的库副本,库中的代码必须能够运行在任何地址上即它们必须是与位置无关的;

  • 内存效率

    通过使用位置无关码,操作系统可以更有效的管理内存;

    如,它可以避免为每个使用特定库的进程单独加载库的副本从而节省内存资源;

  • 动态加载

    位置无关码允许动态库在运行时被加载到任意位置使得在链接时提供灵活性;

使用-fPIC选项所生成的.o目标文件最终被生成的动态库其中的地址将可以加载到物理内存中的任意位置,并随机为其分配进程地址空间;

假设存在一个动态库函数,编译为位置无关码:

// example.c
int add(int a, int b) {
    return a + b;
}

编译这个库为位置无关码:

gcc -shared -fPIC -o libexample.so example.c

编译完成后使用objdump查看动态库的反汇编:

objdump -d libexample.so

# 简化

00000000000006a0 <add>:
 6a0:   55                      push   %rbp
 6a1:   48 89 e5                mov    %rsp,%rbp
 6a4:   89 7d fc                mov    %edi,-0x4(%rbp)
 6a7:   89 75 f8                mov    %esi,-0x8(%rbp)
 6aa:   8b 55 fc                mov    -0x4(%rbp),%edx
 6ad:   8b 45 f8                mov    -0x8(%rbp),%eax
 6b0:   01 d0                   add    %edx,%eax
 6b2:   5d                      pop    %rbp
 6b3:   c3                      retq

在该例子中显示的为函数add的机器码;

其中使用的是相对寄存器的操作而并没有绝对地址的引用;

动态库中的代码执行时将一句当前的指令指针(EIP/RIP)计算相对地址;

即使代码被加载到内存的不同为止也可以使用相对于当前指令的偏移量来访问数据和其他指令;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电商领域利器来了!港大阿里联合提出MimicBrush,对参考图模仿进行零样本图像编辑,万物皆可编辑。

阿里和港大提出的MimicBrush可以通过对参考图模仿进行零样本图像编辑。将一张图片的某一部分融合到领一张图片上去。用在电商商品展示上或者单纯的图片编辑和内容迁移很有用。从官方演示来看效果也很好。 MimicBrush&#xff0c;它会随机选择视频剪辑中的两帧&#xff0c;遮盖其…

足底筋膜炎怎么治疗才能彻底除根

现代快节奏的生活中&#xff0c;足底筋膜炎作为一种常见的足部疾病&#xff0c;困扰着越来越多的人。长时间的站立、行走&#xff0c;以及不正确的运动姿势&#xff0c;都可能成为足底筋膜炎的诱因。足底筋膜炎带来的疼痛和不适&#xff0c;严重影响了人们的生活质量和日常工作…

AI Stable diffusion 报错:稳定扩散模型加载失败,退出

可能是内存不够&#xff0c;看看你最近是加了新的大的模型&#xff0c;可以把你的stable-diffusion-webui\models\Stable-diffusion目录下的某个ckpt删除掉&#xff0c;可能ckpt太大&#xff0c;无法加载成功&#xff1b; Stable diffusion model failed to load, exiting 如图…

SpringBoot+Vue实现Excel文档导入和导出

1.准备工作 1.1.前端程序 在前端首先加上批量导出的按钮&#xff0c;如下 <el-button size"small" type"warning" plain click"exportData"> 批量导出 </el-button> 在添加了点击事件之后&#xff0c;在methods中要与之对应的添加上…

高考志愿填报,选专业应该考虑哪些因素?

这是一个复杂的社会&#xff0c;各种影响就业的因素层出不穷&#xff0c;也从未断绝。对于高考生而言&#xff0c;高考刚结束&#xff0c;短暂的放松后&#xff0c;就必须考虑自身以后应该就读什么专业&#xff0c;如果不对就读专业进行评估&#xff0c;仔细挑选&#xff0c;毕…

【设计模式深度剖析】【8】【行为型】【备忘录模式】| 以后悔药为例加深理解

&#x1f448;️上一篇:观察者模式 设计模式-专栏&#x1f448;️ 文章目录 备忘录模式定义英文原话直译如何理解呢&#xff1f; 3个角色1. Memento&#xff08;备忘录&#xff09;2. Originator&#xff08;原发器&#xff09;3. Caretaker&#xff08;负责人&#xff09;类…

每日一题——Python实现PAT乙级1028 人口普查 Keyboard(举一反三+思想解读+逐步优化)六千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 题目链接​编辑我的写法 专业点评 时间复杂度分析 空间复杂度分析 总结 我要更强…

Star-CCM+自动网格执行方法与设置技巧

在Star中进行一个仿真项目时,有时会创建多个自动网格。网格创建结束后需要执行。在Star中,网格执行可以分为三种。分别是:单独执行操作;多个执行操作;全部执行操作。接下来将三种执行操作的方法与步骤进行介绍。 其次,如果不习惯用自定义控制网格,有时在一个项目中就会…

认识一些分布函数-Gumbel分布

1. Gumbel分布 Gumbel分布(也称为古贝尔型)是一种常用的非对称极值分布( Extreme Value Distribution,EVD),用于建模极大值和极小值,也就是所谓的EVD Type I分布。例如,EVD Type I 被用来预测地震、洪水和其他自然灾害,以及在风险管理中建模操作风险和那些在一定年龄…

关于阿里云效流水线自动部署项目教程

1、登录阿里云效:阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 2、点击左侧流水线&#xff1a; 3、在流水线界面&#xff0c;新建流水线 4、我的是php代码&#xff0c;因此选择php模版 5、创建之后添加流程线源&#xff0c;如下图 6、选择相应的源头。比…

eclipse中报出Invaild project path

一、问题&#xff1a; 二、提示分析&#xff1a; 资源路径位置类型无效的项目路径&#xff1a;找到重复的路径条目 项目路径中有一个名为"/eclipse_rtt_demo"的条目被多次定义&#xff0c;这在Eclipse的构建路径设置中是不允许的。这个重复的条目既被标记为系统包含…

Studio One软件最新版下载及详细安装教程

Studio One 6是一款功能丰富、专业级的音乐制作软件&#xff0c;它具备灵活的工作流程和高效的团队协作能力&#xff0c;能帮助用户实现高质量的音乐创作和制作。 智能模板更快的启动&#xff0c;全新的智能模板为你手头的任务提供了必要的工具集&#xff0c;包括基本录制、混音…

【个人云盘连接助手】解决多个用户账号无法同时映射到本地磁盘问题

问题 家里买了个联想个人云盘&#xff0c;但是使用SAMBA映射到windows本地磁盘时&#xff0c;每次只能连接到一个账户上&#xff0c;于是想着写个小工具&#xff0c;方便切换多个用户账户&#xff0c;自动映射到本地磁盘 开发简介 开发语言&#xff1a;python 主要实现功能&…

训练营第三十六天| 337.打家劫舍 III121. 买卖股票的最佳时机

337.打家劫舍 III 力扣题目链接(opens new window) 在上次打劫完一条街道之后和一圈房屋后&#xff0c;小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为“根”。 除了“根”之外&#xff0c;每栋房子有且只有一个“父“房子与之相连。一番侦察…

工业屏:千万不要以为电脑显示啥样,工业屏就啥样,注意色差。

重要的事情说三遍&#xff1a;一定要放到实际场景下调色&#xff0c;定要放到实际场景下调色&#xff0c;定要放到实际场景下调色。 工业控制屏的色域通常比普通电脑显示器要小。工业控制屏主要用于工业环境中&#xff0c;需要满足特定行业的需求和标准。由于工业控制屏的设计…

深度剖析淘宝扭蛋机源码:打造趣味性电商活动的秘诀

在当今电商市场中&#xff0c;如何吸引用户的注意力、提升用户的参与度成为了各大电商平台竞相追求的目标。淘宝扭蛋机作为一种新型的电商活动形式&#xff0c;以其趣味性和互动性深受用户喜爱。本文将深度剖析淘宝扭蛋机源码&#xff0c;探讨其如何打造趣味性与互动性并存的电…

可视化图表走起来(1):桑基图,一目了然数据流向。

从事可视化设计&#xff0c;什么时候选用什么样的图表非常重要&#xff0c;今天来介绍一下桑基图的定义、场景、数据项等等&#xff0c;贝格前端工场愿意与各位老铁一道成长。 一、桑基图的定义 桑基图&#xff08;Sankey diagram&#xff09;是一种特殊类型的可视化图表&…

深入解析 Spring Cloud Sentinel:分布式系统流量控制与熔断降级的全面指南

&#x1f4e2;&#x1f4e2;&#x1f4e2; 深入解析 Spring Cloud Sentinel&#xff1a;分布式系统流量控制与熔断降级的全面指南 Spring Cloud Sentinel 是阿里巴巴开源的一款强大的分布式系统流量防卫组件&#xff0c;专为微服务架构设计&#xff0c;提供流量控制、熔断降级…

【Spring6】13-19章 JdbcTemplate+代理模式+AOP+Spring事务+Spring集成MyBatis

十三、JdbcTemplate JdbcTemplate是Spring提供的一个JDBC模板类&#xff0c;是对JDBC的封装&#xff0c;简化JDBC代码。 当然&#xff0c;你也可以不用&#xff0c;可以让Spring集成其它的ORM框架&#xff0c;例如&#xff1a;MyBatis、Hibernate等。 接下来我们简单来学习一下…

【LVGL】Guider 界面分析

文章目录 前言架构创建 UI切换界面空间释放分析创建页面空间变化 前言 分析Gui Guider-1.7.2-GA 生成的 LVGL 界面切换&#xff0c;资源管理等处理 架构 所有控件存放于同一个结构体 lv_ui 内&#xff0c;每个页面都至少包含 screen_xxx 和 screen_xxx_del 两个成员 typede…