【Linux 内核源码分析】虚拟内存地址空间

在现代操作系统中,每个进程被分配了独享的虚拟内存地址空间。这个地址空间可以视为一维线性空间,由多个连续的内存页组成。初始时,操作系统会将整个虚拟地址空间分成几个不同的区域,每个区域用于特定的目的。以下是一个常见的布局示例:

  • 代码段(Text Segment):也称为只读段,用于存储程序的可执行代码。
  • 数据段(Data Segment):用于存储全局变量和静态数据。
  • 堆(Heap):动态分配的内存区域,由程序员进行管理。堆的大小可以根据需要进行调整。
  • 栈(Stack):用于存储函数调用、局部变量和返回地址等信息。栈是自动管理的,并且具有固定大小。

在32位模式下,虚拟地址空间通常被限制在4GB范围内。而在64位模式下,则能够支持更大的地址空间范围。

虚拟内存地址空间布局

在这里插入图片描述

常见的虚拟内存地址空间布局如下:

  1. 0x00000000 - 0x08048000 (约128MB):保留区域

    • 这部分地址空间通常包含了一些系统保留的区域,比如 C 运行库的内容等,用户程序不能直接访问,否则会导致段错误(segmentation fault)。
  2. 0x08048000 - 0xC0000000:用户空间

    • 用户空间包含了进程的代码、数据以及堆和栈等,其中:
      • .text 段通常从 0x08048000 开始,存放程序的可执行指令。
      • 堆向高地址扩展,用于动态分配内存。
      • 栈向低地址增长,用于存放函数调用的参数、局部变量等。
  3. 0xC0000000 - 0xFFFFFFFF:内核空间

    • 这段地址空间是内核的逻辑地址,用户空间的程序不能直接访问,需要通过系统调用等方式切换到内核态才能访问这部分内核虚拟地址空间。

在这种布局下,每个进程都有自己独立的虚拟地址空间,其中用户空间和内核空间各自独立,保证了进程之间的隔离和安全性。

ASLR 机制

Linux 中的 ASLR(Address Space Layout Randomization)机制通过对栈、内存映射段、堆的起始地址加上随机偏移量来打乱虚拟地址空间布局,从而增加攻击者猜测系统资源地址的难度,提高了系统的安全性。

ASLR 机制会对以下三个部分进行随机化:

  • Random stack offset:Linux 会在进程启动时将栈顶地址随机化,从而防止攻击者通过栈溢出攻击获取程序控制权。

  • Random mmap offset:Linux 会对每个内存映射段的起始地址进行随机化,从而防止攻击者获取内存映射段的地址,进而执行代码注入等攻击。

  • Random brk offset:Linux 会对堆的起始地址进行随机化,从而防止攻击者通过堆溢出攻击获取程序控制权。

用户栈 Stack 地址段

在 Linux 系统中,每个进程都有一个用户栈(Stack)用于存储函数调用的参数、局部变量等信息。用户栈通常位于进程的虚拟地址空间,紧挨着内核空间的下方。

通过 prlimit 命令可以查看或修改进程的资源限制,包括用户栈的大小。默认情况下,用户栈的大小可能会被限制为 8MB。

这个用户栈的大小限制是为了防止进程无限制地使用栈空间而导致系统资源耗尽或者栈溢出等问题。当进程需要更大的栈空间时,可以通过修改资源限制或者使用特定的系统调用(如 setrlimit)来增加用户栈的大小。

Memory Mapping Segment

Memory Mapping Segment 是指内存映射段,它是用来分配内存区域的一部分地址空间。在 Linux 系统中,可以使用 mmap() 系统调用将文件映射到内存中,也可以通过 mmap() 直接申请一段内存空间来使用。

通过 mmap() 系统调用,可以将一个文件映射到进程的内存地址空间中的 Memory Mapping Segment。这样做的好处是可以直接在内存中对文件进行读写操作,而不需要频繁地进行磁盘 I/O 操作,从而提高了文件操作的效率。

此外,mmap() 也可以用于匿名内存映射,即直接申请一段内存空间来使用,而不与任何文件关联。在这种情况下,可以使用 mmap() 来在指定的内存地址空间申请内存,只要不与已有的虚拟地址冲突即可。需要注意的是,为了实现页对齐(通常是4KB或者1KB),地址需要以 000 结尾。

start_brk和brk

在 Linux 中,start_brk 和 brk 是用来标识堆的起始地址和结束地址的两个值,其中 brk 也被称为 program break。可以使用 brk() 和 sbrk() 这两个函数来改变 program break 的位置。

当在程序中调用 malloc() 函数来申请内存时,通常会在内部调用 sbrk() 函数来将 program break 的位置向上移动,从而扩展堆空间。

而当调用 free() 函数释放内存空间时,可以通过向 sbrk() 函数传递一个负值来将 program break 的位置向下移动,从而收缩堆空间。需要注意的是,brk() 和 sbrk() 所做的工作不仅仅是简单地移动 program break,还需要处理将虚拟内存映射到物理内存地址等相关操作。

在 glibc 中,当申请的内存空间大小不超过 MMAP_THRESHOLD(一个阈值)时,malloc() 函数会使用 brk()/sbrk() 来调整 program break 的位置,这样申请到的内存空间就位于 start_brk 和 brk 标识的范围之间。而当申请的空间大小超过了这个阈值时,malloc() 函数会改用 mmap() 来分配内存空间,这样申请到的内存空间就位于 Memory Mapping Segment 这一段内。

习惯上,整个 Heap 段和 Memory Mapping Segment 段被统称为“堆”。

静态存储区

静态存储区是进程在运行时分配给全局变量、静态变量和常量的内存空间。它们在程序的整个执行过程中都存在,并且在编译和链接阶段就确定了其大小和位置。

  • BSS Segment(未初始化数据段):BSS(Block Started by Symbol)段用于存储未初始化的全局变量和静态变量。在程序加载到内存时,操作系统会为 BSS 段分配一块内存,并将该内存区域初始化为零或空值。

  • Data Segment(已初始化数据段):Data 段用于存储已经初始化的全局变量和静态变量。在程序加载到内存时,操作系统会将 Data 段的内容直接从可执行文件中加载到相应的内存位置。

  • Text Segment(代码段):Text 段存储程序的可执行指令。这些指令在程序运行时是只读的,不能被修改。通常情况下,Text 段是共享的,多个进程可以共享同一个可执行文件的 Text 段,以减少内存占用。

堆和内存映射区共享同一地址空间

新的进程内存布局(默认进程内存布局)导致了栈空间的固定,而堆区域和 MMAP 区域共用一个空间,从而在很大程度上增大了堆区域的大小:

  • 栈空间的固定:在新的进程内存布局中,操作系统会为每个进程分配一定的虚拟地址空间用于栈空间。这个栈空间的大小是固定的,无法动态调整,因为栈空间通常用于存储函数调用和局部变量等,其大小需要在程序编译时就确定。

  • 堆区域和 MMAP 区域共用一个空间:在默认的进程内存布局中,堆区域和 MMAP 区域会被映射到同一个虚拟地址空间中,它们共享同一块虚拟地址范围。这意味着当堆区域需要扩展时,可以利用未使用的 MMAP 区域空间,从而增大了堆区域的可用空间。

  • 增大了堆区域的大小:由于堆区域和 MMAP 区域共用一个空间,并且 MMAP 区域在很大程度上增大了堆区域的可用空间,因此整体上堆区域的大小也得以增加。

64 位模式下虚拟地址空间布局

在这里插入图片描述
对于 x86_64 架构的 Linux 系统,每个用户空间进程通常可以访问两个独立的地址空间范围:

  • 从 0x0000000000000000 到 0x00007FFFFFFFFFFF,这是用户空间的正常地址范围,共计 128 TB。

  • 从 0xFFFF800000000000 到 0xFFFFFFFFFFFFFFFF,这是内核空间的地址范围,同样也是 128 TB。

对于 64 位的 x86_64 和 amd64 架构,在用户空间中,通常会有以下几个段:

  • text 段的起始地址通常为 0x0000000000400000,这是代码段,存储程序的可执行指令。

  • data 段和 bss 段紧随在 text 段后面,用于存储程序的静态数据和未初始化的全局变量。

  • 在 heap 段和 bss 段之间以及 stack 段和 0x00007FFFFFFFF000 之间,可能会存在由地址空间布局随机化(ASLR)引起的随机 brk offset。heap 段向上增长,用于动态分配内存;stack 段向下增长,用于存储函数调用和局部变量。

在经典布局下,Memory Mapping Segment(mmap 段)的起始地址会通过页对齐后从某一地址开始。在 amd64 架构下,页的大小可以是 4K、2M 或者 1G,而不像 x86 (_64) 架构下的统一页大小为 4K。因此,mmap 的起始范围会根据系统的页大小而有所不同。

在 x86_64 架构下,mmap 段的起始地址通常固定为 0x00002AAAAAAAB000(当然也可以通过设置随机的 mmap offset 来改变),并且该地址是向上增长的。这个地址是经典布局下 mmap 段的默认起始地址,用于映射文件或设备到内存中,提供了一种灵活的内存管理方式。

Linux 下控制虚拟地址空间布局

在 Linux 系统下,可以通过以下两个内核参数配置虚拟地址空间布局:

  • vm.legacy_va_layout:该参数用于控制内核是否启用经典布局。如果设置为 0,则启用现代布局;如果设置为 1,启用经典布局。

  • kernel.randomize_va_space:该参数用于控制 ASLR 的随机 brk offset 是否启用。如果设置为 0,则不启用随机 brk offset;如果设置为 1,则启用随机 brk offset。

这两个参数的默认值都是 1,即启用了经典布局和 ASLR 的随机 brk offset。

# 是否使用经典的进程内存布局
gary@...~$ cat /proc/sys/vm/legacy_va_layout
# 0: 使用新的进程内存布局
0
gary@...~$ sysctl -w vm.legacy_va_layout=1
 
# 是否开启 ASLR 地址空间布局随机化
# 当设置值为 1 时,地址空间会被随机化。
#   栈本身的位置
#   虚拟动态共享对象(VDSO)页面
#   共享内存区域
# 将选项设置为值为 2 将类似于 1,并添加数据段。
gary@...~$ cat /proc/sys/kernel/randomize_va_space
2
gary@...~$ sysctl -w kernel.randomize_va_space=0

VDSO

在 Linux 中,VDSO(Virtual Dynamic Shared Object)是一种特殊的内核映射文件,它位于用户空间,用于提供一些高效的系统调用接口。VDSO 的目的是减少用户空间应用程序对内核的频繁切换,从而提高系统的性能。

VDSO 在虚拟地址空间布局中占据了一个固定的位置,通常位于进程地址空间的最高地址部分。通过 VDSO,应用程序可以直接访问一些系统调用,而无需进行陷入内核的开销。

在控制虚拟地址空间布局中的 VDSO 方面,主要涉及到 kernel.randomize_va_space 参数。当该参数的值设置为 1 时,VDSO 页面的位置会被随机化,增加系统的安全性。这样,攻击者很难通过事先计算出的固定 VDSO 地址来进行针对性攻击。

Linux 系统默认采用了经典的虚拟地址空间布局,并启用了地址空间随机化(ASLR),包括对 VDSO 页面的位置进行随机化。
在这里插入图片描述
通常的虚拟地址空间布局包括以下部分:

  • Text Segment(代码段):存放程序的可执行指令,起始地址通常为0x0000000000400000或类似的值。

  • Data Segment(数据段):存放已经初始化的全局变量和静态变量等数据。

  • BSS Segment(未初始化数据段):存放未初始化的全局变量和静态变量等数据,通常会被初始化为0。

  • Heap 地址段:用于动态分配内存,包括使用malloc()等函数分配的内存。

  • Memory Mapping Segment(映射段):用于映射文件或设备到内存,例如使用mmap()函数进行内存映射。

  • Stack 地址段:用于存储函数调用的局部变量、函数参数和函数调用的返回地址等。

  • vvar(Virtual Variable):用于访问一些内核变量的特殊地址。

  • vdso(Virtual Dynamic Shared Object):提供一些高效的系统调用接口,避免频繁陷入内核。

  • vsyscall(Virtual System Call):提供一些常见系统调用的快速接口。

关于 vvar、vdso 和 vsyscall 的详细解释如下:

  • vsyscall:vsyscall 是一种将一些常用的系统调用函数直接映射到用户空间的机制。例如,对于读取时间的系统调用 gettimeofday(),内核会将其实现映射到 vsyscall 区域,这样用户空间程序可以直接调用这些系统调用而不需要陷入内核态,可以提高效率。然而,vsyscall 区域固定且较小,存在安全问题。

  • vdso:为了解决 vsyscall 固定区域和安全问题的限制,引入了 vdso 机制。vdso 将一些常用的系统函数的实现映射到一个独立的共享库文件中(通常是 linux-vd.so),用户空间程序可以通过调用这个共享库文件中的函数来获得相应的系统调用服务。vdso 的地址是随机的,并且可以包含更多的系统调用函数,因此更加灵活和安全。

  • vvar:vvar 区域用于存放一些特定的内核变量,用户空间程序可以通过调用 vdso 中的函数来访问这些变量,从而获取所需的信息。vvar 区域的地址是随机的,也增强了安全性。

在这里插入图片描述
通过 sysctl 设置成经典布局后,vdso 和 vvar 的位置会随着 Memory Mapping Segment 的变动而改变。Memory Mapping Segment 的起始地址固定为 0x00002AAAAAAAB000,并且向上增长。

在这种布局下,vdso 和 vvar 通常会被映射到 Memory Mapping Segment 中的某些区域,它们的具体位置会随着系统的内存映射情况而发生变动。这可以增加系统的安全性,并且使得内存布局更加灵活和可配置。

Linux 虚拟内存和物理内存

Linux 中的虚拟内存和物理内存之间存在着映射关系。每个进程有自己独立的虚拟内存空间,这种独立性使得链接器在链接可执行文件时可以设定内存地址而不必考虑实际的物理内存地址,从而简化了内存管理和提高了系统的灵活性。

当不同的进程使用相同的代码时,例如库文件中的代码,系统可以将这些共享的代码存储在物理内存中的一个位置,各个进程只需要将自己的虚拟内存映射到这一位置即可实现代码的共享,从而节省内存空间,减少重复存储,提高系统的效率。

在程序需要分配连续的内存空间时,虚拟内存空间可以分配连续的空间,而无需实际的物理内存空间也是连续的,这样就可以更好地利用内存碎片,减少内存浪费,优化内存的使用效率和系统性能。

参考:Linux内核源码分析(内存调优/文件系统/进程管理/设备驱动/网络协议栈)教程

Linux内核学习资料、学习群:739729163
Linux内核源码学习

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

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

相关文章

【Linux取经路】文件系统之重定向的实现原理

文章目录 一、再来理解重定向1.1 输出重定向效果演示1.2 重定向的原理1.3 dup21.4 输入重定向效果演示1.5 输入重定向代码实现 二、再来理解标准输出和标准错误2.1 同时对标准输出和标准错误进行重定向2.2 将标准输出和标准错误重定向到同一个文件 三、再看一切皆文件四、结语 …

Camtasia 2023 v23.4.2.51146 Win功能强大的屏幕录制和视频编辑软件

Camtasia 2023.3.4.2是一款适用于各类用户的屏幕录制和视频编辑软件,特别适合需要制作教育、培训和营销视频的专业人士。它结合了易用性和多样的功能,使视频制作变得更加高效和专业。 软件安装 适用于 Win10.win11系统 1.直接安装软件,安装…

redis scan命令导致cpu飙升

一.背景 今天下午Redis的cpu占用突然异常升高,一度占用达到了90%,触发了钉钉告警,之后又回到正常水平,跟DBA沟通,他说主要是下面这个语句的问题 SCAN 0 MATCH fastUser:6136* COUNT 10000这个语句的执行时长很短&…

nginx 日志改为json格式

nginx 日志改为json格式 场景描述效果变更旧样式新样式 场景描述 正常使用nginx时,使用默认的日志输出格式,对于后续日志接入其他第三方日志收集、清洗环节,因分隔符问题可能不是很友好。 xxxx - - [19/Feb/2024:11:16:48 0800] "GET …

并发编程线程安全之同步锁Synchronized

一、原子性定义 原子性的本质是互斥访问,同一时刻只有一个线程对它进行访问操作 二、原子性问题的简述 public class AutomicDemo {int count 0;public static void main(String[] args) throws InterruptedException {AutomicDemo automicDemo new AutomicDem…

Idea启动Gradle报错: Please, re-import the Gradle project and try again

Idea启动Gradle报错:Warning:Unable to make the module: reading, related gradle configuration was not found. Please, re-import the Gradle project and try again. 解决办法: 开启步骤:View -> Tool Windows -> Gradle 点击refe…

解锁创意灵感,探索FlutterExampleApps项目的奥秘

解锁创意灵感,探索FlutterExampleApps项目的奥秘 项目简介 FlutterExampleApps项目是一个包含各种示例应用链接的仓库,旨在演示Flutter应用开发中的各种功能、特性和集成。 项目包含了以下几个部分,每个部分都涵盖了不同的内容和主题&…

哈希(哈希散列数据结构)---底层原理

Day02: 1.哈希散列数据结构:底层实现就是:数组链表(红黑树) map的put方法和get方法。 2.数组方法和链表存取数据的区别 数组方法:法随机访问快 链表:增删改效率快。 3.哈希结合了链表和数组的特性。 …

在四维轻云中,如何实现地理空间数据云管理?

四维轻云是一款轻量化的地理空间数据网页管理平台,支持倾斜模型(.osgb)、激光点云(.las)、正射影像(dom)和数字高程模型(dem)等多种地理空间数据的在线管理、编辑及分享,其他类型地理空间数据也将陆续上线。 目前,平台具有项目管理、数据上传…

营销短信群发的注意事项?短信营销的优点?

营销短信的关键是什么?群发国际营销短信的好处? 营销短信作为一种高效、直接的推广方式,越来越受到企业的青睐。然而,要想让营销短信发挥最大的效果,就需要注意一些关键事项。接下来,蜂邮EDM将深入探讨营销…

【Java多线程】线程安全问题与解决方案

目录 1、线程安全问题 1.2、线程安全原因 2、线程加锁 2.1、synchronized 关键字 2.2、完善代码 2.3、对同一个线程的加锁操作 3、内容补充 3.1、内存可见性问题 3.2、指令重排序问题 3.3、解决方法 3.4、总结 volatile 关键字 1、线程安全问题 某个代码&#xff…

2023年全球前端大技术大会(GMTC北京站2023):核心内容与学习收获(附大会核心PPT下载)

此次峰会是一次内容丰富、有深度和广度的技术盛会。参会者不仅可以了解前端技术的最新发展和未来趋势,还可以与业界专家交流心得,提升自己的技能和能力。同时,此次大会也促进了全球前端社区的交流和合作,推动了前端技术的创新和发…

DP读书:《openEuler操作系统》(十)套接字 Socket 数据传输的基本模型

10min速通Socket 套接字简介数据传输基本模型1.TCP/IP模型2.UDP模型 套接字类型套接字(Socket)编程Socket 的连接1.连接概述(1)基本概念(2)连接状态(3)连接队列 2.建立连接3.关闭连接 socket 编程接口介绍数据的传输1. 阻塞与非阻塞2. I/O复用 数据的传输…

【数据分享】2014-2024年全国监测站点的逐时空气质量数据(15个指标\Excel\Shp格式)

空气质量的好坏反映了空气的污染程度,在各项涉及城市环境的研究中,空气质量都是一个十分重要的指标。空气质量是依据空气中污染物浓度的高低来判断的。 我们发现学者王晓磊在自己的主页里面分享了2014年5月以来的全国范围的到站点的逐时的空气质量数据&…

绝地求生:四大成长武器 满级效果好到爆 钱包真保不住了

刚才看了上手时机视频 说真的这次的四大成长皮肤 只能说是好看到爆啊 小伙伴们,这次过年的路费 可能就要被冲没有了 先来看看悟空的武器特写吧! 悟空的皮肤是M24成长皮肤 也是不错 炫酷的很哦 仔细看枪托的细节 是由紧箍咒的形状哦,这下…

Flink Catalog 解读与同步 Hudi 表元数据的最佳实践

博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,…

【C语言的小角落】逻辑与逻辑或混合计算

关注小庄 顿顿解馋(≧◡≦) 引言:本篇博客小庄带领小伙伴们解决一个比较角落有时头疼的问题—关于逻辑与和逻辑或结合运算的问题,请放心食用~ 我们先放代码说话 int main() {int x 1;int y 3;int z 4;if(x1 || y && z){;} printf("y …

自动驾驶中之定位总结

1 前言2 典型的单个定位方式2.1 基于通信的定位方法2.1.1 GNSS 全球卫星导航系统2.1.1.1 gnss的优点与缺点2.1.1.2 gnss定位技术2.1.1.2.1 RTK定位技术2.1.1.2.2 PPP定位技术 2.1.1.2 gnss定位技术总结 2.1.2 车联网定位 2.1 基于航位推算的定位方法2.1.1 惯性测试单元定位IMU2…

unity 使用VS Code 开发,VS Code配置注意事项

vscode 对应的插件(unity开发) 插件:.Net Install Tool,c#,c# Dev Kit,IntelliCode For C# Dev Kit,Unity,Unity Code Snippets 本人现在是用了这些插件 unity需要安装Visual Studio Editor 1、.Net Install Tool 设置 需要在设置里面配置…

美容小程序:让预约更简单,服务更贴心

在当今繁忙的生活节奏中,美容预约常常令人感到繁琐和疲惫。为了解决这个问题,许多美容院和SPA中心已经开始采用美容小程序来简化预约流程,并提供更加贴心的服务。在这篇文章中,我们将引导您了解如何制作一个美容小程序&#xff0c…