两个不同的进程 虚拟地址相同_Linux的进程地址空间[二] - VMA_weixin_39832628的博客-程序员宅基地

技术标签: 两个不同的进程 虚拟地址相同  

f5a40ad8fef6af7e19048dbebbe55ebc.png

Linux的进程地址空间[一]

segments

一个进程通常由加载一个elf文件启动,而elf文件是由若干segments组成的,同样的,进程地址空间也由许多不同属性的segments组成,但这与硬件意义上的segmentation机制(参考这篇文章)不同,后者在某些体系结构(比如x86)中起重要作用,充当内存中物理地址连续的独立空间。Linux进程中的segment是虚拟地址空间中用于保存数据的区域,只在虚拟地址上连续。

97be58b5c1c1cf44607541a34b41cea2.png

text段包含了当前运行进程的二进制代码,其起始地址在IA32体系中中通常为0x08048000,在IA64体系中通常为0x0000000000400000(都是虚拟地址哈)。data段存储已初始化的全局变量,bss段存储未初始化的全局变量。从上图可以看出,这3个segments是紧挨者的,因为它们的大小是确定的,不会动态变化。

与之相对应的就是heap段和stack段。heap段存储动态分配的内存中的数据,stack段用于保存局部变量和实现函数/过程调用的上下文,它们的大小都是会在进程运行过程中发生变化的,因此中间留有空隙,heap向上增长,stack向下增长,因为不知道heap和stack哪个会用的多一些,这样设置可以最大限度的利用中间的空隙空间。

还有一个段比较特殊,是mmap()系统调用映射出来的。mmap映射的大小也是不确定的。3GB的虚拟地址空间已经很大了,但heap段, stack段,mmap段在动态增长的过程还是有重叠(碰撞)的可能。为了避免重叠发生,通常将mmap映射段的起始地址选在TASK_SIZE/3(也就是1GB)的位置。如果是64位系统,则虚拟地址空间更加巨大,几乎不可能发生重叠。

如果stack段和mmap段都采用固定的起始地址,这样实现起来简单,而且所有Linux系统都能保持统一,但是真实的世界不是那么简单纯洁的,正邪双方的较量一直存在。对于攻击者来说,如果他知道你的这些segments的起始地址,那么他构建恶意代码(比如通过缓冲区溢出获得栈内存区域的访问权,进而恶意操纵栈的内容)就变得容易了。

一个可以采用的反制措施就是不为这些segments的起点选择固定位置,而是在每次新进程启动时(通过设置PF_RANDOMIZE标志)随机改变这些值的设置。

a84a4a9f374be5df3e3618dfd17915cc.png

那这些segments的加载顺序是怎样的呢?以下图为例,首先通过execve()执行elf,则该可执行文件的text段,data段,stack段就建立了,在进程运行过程中,可能需要借助ld.so加载动态链接库,比如最常用的libc,则libc.so的text段,data段也建立了,而后可能通过mmap()的匿名映射来实现与其他进程的共享内存,还有可能通过brk()来扩大heap段的大小。

a27909885af19ad6708aac110e456443.png

vm_area_struct

在Linux中,每个segment用一个vm_area_struct(以下简称vma)结构体表示。vma是通过一个双向链表(早期的内核实现是单向链表)串起来的,现存的vma按起始地址以递增次序被归入链表中,每个vma是这个链表里的一个节点。

177a9236588150380c8c0d1433f27b1a.png

在用户空间可通过"/proc/PID/maps"接口来查看一个进程的所有vma在虚拟地址空间的分布情况,其内部实现靠的就是对这个链表的遍历。

38f6ee473ddf24233ce40eeeb6db7ad2.png

同时,vma又通过红黑树(red black tree)组织起来,每个vma又是这个红黑树里的一个节点。为什么要同时使用两种数据结构呢?使用链表管理固然简单方便,但是通过查找链表找到与特定地址关联的vma,其时间复杂度是O(N),而现实应用中,在进程地址空间中查找vma又是非常频繁的操作(比如发生page fault的时候)。

使用红黑树的话时间复杂度是O(

),尤其在vma数量很多的时候,可以显著减少查找所需的时间(数量翻倍,查找次数也仅多一次)。同时,红黑树是一种非平衡二叉树,可以简化重新平衡树的过程。

现在我们来看一下vm_area_struct结构体在Linux中是如何定义的(这里为了讲解的需要对结构体内元素的分布有所调整,事实上,结构体元素的分布是有讲究的,将相关的元素相邻放置并按cache line对齐,有利于它们在cache中处于同一条cache line上,提高效率):

struct vm_area_struct 
{
      
	unsigned long vm_start; 
	unsigned long vm_end;  
	struct vm_area_struct *vm_next, *vm_prev; 
	rb_node_t vm_rb; 
        unsigned long vm_flags;
        struct file * vm_file;
        unsigned long vm_pgoff;
	struct mm_struct * vm_mm; 
	...
}

其中,vm_start和vm_end分别是这个vma所指向区域的起始地址和结束地址,虽然vma是虚拟地址空间,但最终毕竟是要映射到物理内存上去的,所以也要求是4KB对齐的。

vm_next是指向链表的下一个vma,vm_rb是作为红黑树的一个节点。

a3bd3f372c46afae0b3d18ddc84190e7.png

vm_flags描述的是vma的属性,flag可以是VM_READ、VM_WRITE、VM_EXEC、VM_SHARED,分别指定vma的内容是否可以读、写、执行,或者由几个进程共享。前面介绍的页表PTE中也有类似的Read/Write权限限制位,那它和vma中的这些标志位是什么关系呢?

69483703f15ee76a6a526f7cc334f623.png

vma由许多的虚拟pages组成,每个虚拟page需要经过page table的转换才能找到对应的物理页面。PTE中的Read/Write位是由软件设置的,设置依据就是这个page所属的vma,因此一个vma设置的VM_READ/VM_WRITE属性会复制到这个vma所含pages的PTE中。

之后,硬件MMU就可以在地址翻译的过程中根据PTE的标志位来检测访问是否合法,这也是为什么PTE是一个软件实现的东西,但又必须按照处理器定义的格式去填充,这可以理解为软硬件之间的一种约定。那可以用软件去检测PTE么?当然可以,但肯定没有用专门的硬件单元来处理更快嘛。

可执行文件和动态链接库的text段和data段是基于elf文件的,mmap对文件的映射也是对应外部存储介质中这个被映射的文件的,这两种情况下,vm_file指向这个被映射的文件,进而可获得该文件的inode信息,而"vm_pgoff"是这个段在该文件内的偏移。

对于text段,一般偏移就是0。对于heap段,stack段以及mmap的匿名映射,没有与之相对应的文件实体,此时"vm_file"就为NULL,"vm_pgoff"的值没有意义。

那一个进程是怎么找到它的这些vma的呢?请看下文分解。

参考:

How The Kernel Manages Your Memory

原创文章,转载请注明出处。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_39832628/article/details/110994300

智能推荐

linux进程地址空间--vma的基本操作

由于虚拟内存的引入,每个进程都可拥有3GB的虚拟内存,并且用户进程之间的地址空间是互不可见、互不影响的,也就是说即使两个进程对同一个地址进行操作,也不会产生问题。在前面介绍的一些分配内存的途径中,无论是...

Linux进程地址空间-------vma基本操作

由于虚拟内存的引入,每个进程都可拥有3GB的虚拟内存,并且用户进程之间的地址空间是互不可见、互不影响的,也就是说即使两个进程对同一个地址进行操作,也不会产生问题。在前面介绍的一些分配内存的途径中,无论是...

Linux虚拟地址空间

Linux虚拟地址空间 注:本文来自多篇博客整理,具体博客链接在博客下方 在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中。这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的...

ROS-6.44.6-X64-L5-2G-PVE-VMA.zip

1、ROS-6.44.6-X64-L5-2G-PVE-VMA.zip解压 2、使用WinSCP打开路径/var/lib/vz/dump,上传解压文件vzdump-qemu-666-2019_12_06-20_43_59.vma 3、打开local -> 内容,选中刚才上传的备份文件vzdump-qemu-666-2019_12_...

Linux内存管理第四章 -- 进程地址空间(Process Address Space)

文章目录Linux内存管理 -- 进程地址空间(Process Address Space) Linux内存管理 – 进程地址空间(Process Address Space)

Linux进程地址空间的理解

对于Linux的虚拟内存的理解,这个例子算是一个很好的引导了,原文链接:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26683523&id=3201345 《Linux内核设计与实现》15章节给出的例子更详细些。 *******...

Linux进程启动过程分析do_execve(可执行程序的加载和运行)---Linux进程的管理与调度(十一)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-06 Linux-4.5 ...execve系统调用execve系统调用我们前面提到了, fork, vfork等复制出来的进程是父进程的一个副本, 那么如何我们想加载新的程序, 可

Linux虚拟地址空间布局以及进程栈和线程栈总结

本文转自多个博客,以及最后有我的总结。我们有单独从头到尾写一个总结的原因是别人已经写得很好了,我不花大量时间是...这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块。在L

Linux内核学习笔记(五)进程地址空间

Linux是一个虚拟内存操作系统,系统中的所有进程以虚拟方式共享内存。从一个进程的视角来看,它独占系统中所有的物理内存,并且一个进程的地址空间可以远远大于物理内存的大小。 进程的地址空间由可寻址的虚拟内存...

Linux进程描述符task_struct结构体详解--Linux进程的管理与调度(一)

日期 内核版本 架构 作者 GitHub CSDN 2016-05-12 Linux-4.5 X86 & arm gatieme LinuxDeviceDrivers Linux-进程管理与调度 ...注意,程序并不是进程,实际上两个或多个进程不仅有可能执行同一程序

源码之linux进程:vm_area_struct与虚拟内存的关系

在虚拟内存中,我提到了linux虚拟内存区域的结构,但具体其是如何在linux中表示与实现的呢? 我利用了linux2.6的源码进行了浅显的分析。 正文 task_struct 在linux中,进程控制块即PCB的结构为task_struct,我们以...

Linux mem 1.2 用户态进程空间布局 --- mmap()详解

mmap系统调用 Author: pwl Date: 2020-10-20 文章目录mmap系统调用1. 原理介绍1.1 映射1.2 vma...mmap_base(mmap基地址)2.2.2 get_unmapped_area()2.2.2.1 arch_get_unmapped_area()2.2.2.2 arch_get_unmapped_area_t

Linux可执行文件与进程的虚拟地址空间

作者简介:本文由西邮陈莉君教授研一学生贺东升编辑,梁金荣、张孝家校对建议结合之前的《linux的内存寻址方式》看。Linux可执行文件与进程的虚拟地址空间一个可执行文件被执行的同时也伴随...

【Linux】Linux虚拟内存空间描述

由于虚拟空间是程序员使用的空间,在程序员的头脑中并不需要页的概念,因此也不需要页的描述,在虚拟空间Linux描述的是分区。所谓分区,就是按照虚存的代码或数据的属性分成的段。描述虚拟分区的结构定义在文件...

进程虚拟地址空间中的虚拟内存区域(vma)---可执行程序与虚拟地址空间的映射关系

http://www.cnblogs.com/zszmhd/archive/2012/08/29/2661461.html   http://www.cnblogs.com/chengxuyuancc/archive/2013/04/17/3026920.html   ...

linux进程地址空间(3) 内存映射(2)malloc与do_brk

下面再看函数do_brk,事实上do_brk和do_mmap几乎一样,因为它们的本质都是一样的;这里多说一下它由mm/mmap.c文件的系统调用SYSCALL_DEFINE1调用,系统调用的问题如前几篇文章一样,后续会有专题讨论它,这里直接看...

Linux内存管理 —— 进程的虚拟地址空间和VSS

1. 进程虚拟地址空间 进程的虚拟地址空间记录在其task_struct结构的指针成员mm中(struct mm_struct),这是用户态进程才有的,里面包含pgd、代码段、堆、栈地址等信息。(kernel不需要mm,因为kernel不需要引用动态...

gnu_kappa_vma:Gnuplot 脚本的-源码

快速构建测量结果和 kappa-bridge MFK1 Kappabridges AGICO[1] 和振动磁力计 VMA-1[2]。 [1] [2] 自动高灵敏度振动线圈磁力计 / V. Maksimochkin、V. Trukhin、N. Garifullin、N. Khasanov // 仪器和实验技术。 ...

Linux面试之进程地址空间

当用户进程请求动态内存时,并没有获得请求的页框,而是获得了一个新的线性地址的使用权。这线性地址区域就成为进程地址空间的一部分。 *为什么要推迟分配? *进程怎样看待动态内存。 *进程地址空间的基本...

Linux进程描述符task_struct结构体详解--Linux进程的管理与调度(一)(转)

原 Linux进程描述符task_struct结构体详解--Linux进程的管理与调度(一) 2016年05月12日 13:12:40 JeanCheng 阅读数:24054 &amp...

进程地址空间VMA以及malloc

进程空间的VMA结构体中由两个成员,红黑树以及链表,表示其组织方式由两种: 红黑树:   链表类型: 有了上面的图例,下面简单讲一下malloc的分配原理: 首先malloc是通过系统调用brk来完成内存分配的: ...

linux中的PCB---task_struct

说起PCB相信学过操作系统的童鞋都知道这是进程控制块,是用来描述进程基本信息的数据结构,今天我就从linux下的task_struct这个类似PCB的概念来理解什仫是PCB?以及PCB存在什仫样的基本信息?  要了解PCB,首先需要...

Linux进程描述符task_struct结构体详解

注意,程序并不是进程,实际上两个或多个进程不仅有可能执行同一程序,而且还有可能共享地址空间等资源。 Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所

linux进程虚拟空间布局

首先看linux进程的虚拟空间内存布局,以i386 32位机器为例每个用户进程的虚拟地址空间为0x0—0xC0000000也就是3GB,其中0x0—0x08000000 128MB地址空间用于捕获空指针,用户空间分为代码段,堆,mmap区,栈。...

把握linux内核设计思想(十三):内存管理之进程地址空间

 进程地址空间由进程可寻址的虚拟内存组成,Linux 的虚拟地址空间为0~4G字节(注:本节讲述均以32为为例)。Linux内核将这 4G 字节的空间分为两部分。将最高的 1G 字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核...

Linux进程地址空间管理

目录 1. 重要数据结构说明 2 ... 进程地址空间概览 3 3. 地址区间操作 4 3.1 地址区间查找 4 3.2 地址区间合并 4 3.3 地址区间插入 6 3.4 地址区间创建 6 4. 映射的创建 8 4.1 mmap 8

linux 内核参数 max_map_count 进程可拥有的虚拟内存区域数量 简介

虚拟内存区域是一个连续的虚拟地址空间区域。 在进程的生命周期中,每当程序尝试在内存中映射文件,链接到共享内存段,或者分配堆空间的时候,这些区域将被创建。 调优这个值将限制进程可拥有VMA的数量。 限制一...

ARM32页表-虚拟地址到物理地址的转换

页表就是用于将虚拟地址转换为物理地址的转换关系表。访问虚拟地址时,计算机通过页表找到对应的实际物理地址访问。 我们在上一节介绍了内存管理模块概图, 怎么完成从pgd 到 page的转化呢? linux 内核code是...

随便推点

推荐文章

热门文章

相关标签