Linux——进程地址空间
进程地址空间
我们都知道一个程序都有很多很多的数据,不同的数据应该有不同的存放位置:
一个C,C++程序员,是这样看待存放数据的:
我们可以用代码来证明一下:
发现跟我们所说的理论是差不多的接近的。
我们还有一件事可以发现,就是如果我们按照上面这个图,堆是向上增长的(往高地址增长),栈是向下增长的(往低地址增长)。
我们也来验证一下:
在这之前我们还学了命令行参数和环境变量,其实这两个东西也是有存放的位置的:
是存放在栈区上面的,我们也可以来验证一下:
这时候有一个问题,这个东西是内存吗?
我们测试一段代码:
我们发现g_val被修改之后,子进程是200,父进程是100,两个g_val的值都不一样,但是&g_val的地址都是一样的。这是一个非常离谱的现象。
有没有一种可能,我们看到的地址不是真实的物理地址,这个我们看到的地址,是虚拟地址。这个虚拟地址又是基于进程地址空间来划分的。
我们上面画的那个图就是进程地址空间。
虚拟地址和物理地址的映射
那么到底是什么可以一个地址对应两个不同的值的呢?我们来看看:
我们首先有一个进程,每一个进程都会申请一个相应的进程地址空间。:
我们以上面的g_val为例,我们g_val会放在这个已初始化全区:
但这个时候g_val并没有被存放在虚拟地址,它一定会存放在物理地址中,这个时候我们得用一映射种方式将我们的数据放到我们的物理地址中存放起来。我们有一个叫页表的东西可以帮助我们完成这个关系的转化:
这个时候如果我们创建了一个子进程,它会赋值父进程的所有东西(包括页表):
这个时候我们把子进程的g_val的值改为200时,会在物理内存中重新找一块空间:
子进程的页表也会被修改:
我们来看一下进程地址空间的较为全面的解析:
进程地址空间是指为某个特定进程分配的虚拟内存区域。当进程在操作系统上运行时,它不会直接使用物理内存地址,而是使用该进程的虚拟地址。进程地址空间为每个进程提供了一个隔离的、看似连续的内存区域,使其能够独立地执行并保护其内存内容不受其他进程的干扰。
以下是进程地址空间的主要组成部分和特点:
代码段(Text Segment):这部分包含了进程要执行的机器代码。
数据段:包括初始化的数据(例如全局变量)和未初始化的数据。这部分内存存储了进程运行所需的变量和数据。
堆(Heap):是一个动态内存分配区域,进程在运行时可以从堆中分配或释放内存。例如,C 和 C++ 中的 malloc 和 free,以及Java中的new和delete,都是对堆进行操作的例子。
栈(Stack):这是一个后进先出(LIFO)的数据结构,用于存储局部变量、函数调用的返回地址和进行函数调用时保存的上下文。每当函数被调用时,一个新的栈帧就会被推入栈,每当函数返回时,当前的栈帧就会被弹出。
内存映射区域:用于文件映射和共享内存等操作。
隔离与保护:进程地址空间为每个进程提供了一个隔离的内存区域,确保进程不能访问其他进程的内存。这增强了系统的安全性和稳定性。
虚拟到物理的映射:虽然每个进程都有自己的虚拟地址空间,但这些地址实际上需要被映射到物理内存上。这一映射是由操作系统的内存管理单元(MMU)完成的。
总的来说,进程地址空间是为进程提供的虚拟内存布局,它包含了进程运行所需的代码、数据、堆和栈等信息。这种虚拟化技术使得多个进程可以独立且安全地在同一台机器上并发执行
地址空间
我们其实一来就讲进程地址空间,其实有点快了,我们先来理解一个概念:什么是地址空间?
地址空间通常用来表示一个程序或进程可以访问的内存范围
说白了,就是什么样的程序访问内存的范围,而我们这里谈论的是进程,就专门指的是进程的地址空间。
那么如何表示这个地址空间呢?我们会定义一个结构体,里面定义了这个地址空间的起始地址和结束地址:
虚拟地址空间
一句话来说:
虚拟地址空间是操作系统为每个运行的进程分配的抽象内存地址范围。这个虚拟地址空间使每个进程感觉它拥有整个计算机内存的同时,实际上受到操作系统的管理和限制。
就是说,操作系统给每个进程“画大饼”,让每个进程都觉得自己拥有操作系统的全部内存(相当于每个私生子都觉得自己会继承父亲的全部财产)。
这个有什么用呢?
- 隔离性:每个进程都有自己的虚拟地址空间,不同进程之间的虚拟地址空间互相隔离。这意味着一个进程不能直接访问另一个进程的内存,这提供了安全性和稳定性。
- 扩展性:虚拟地址空间通常比物理地址空间(实际可用的内存)要大得多。这允许操作系统有效地支持多个进程运行,即使物理内存有限。
- 内存保护:虚拟地址空间使操作系统能够实施内存保护。如果进程尝试访问其虚拟地址空间之外的内存,将触发异常,操作系统可以处理这些异常并防止进程访问未授权的内存。
- 虚拟内存:虚拟地址空间的一部分通常被映射到物理内存,但不是所有虚拟地址空间都需要实际的物理内存支持。这允许操作系统使用虚拟内存技术,将不常用的数据或代码存储在磁盘上,只在需要时将其加载到物理内存中,从而扩展可用的内存大小。
- 地址映射:虚拟地址空间和物理地址空间之间存在地址映射关系,由操作系统的内存管理单元(MMU)负责管理。MMU将进程的虚拟地址映射到物理地址,从而允许进程访问内存中的数据
地址空间的作用
地址空间的作用一般有这么几点:
- 可以统一化管理,有了页表这样转化技术,对物理内存的管理和数据管理就可以用较为统一化手段进行管理。
2.安全检查:页表可以对进程的操作进行安全检查,如果进程的操作违法,直接拒绝该操作。
这里要多说一下,页表除了虚拟地址到物理地址的转化,之后还有一列是权限检查:
相应的权限开放之后,才可以对相应的数据进行相应的操作,没有权限,直接拒绝。
- 保证进程的独立性:因为每一个进程都有一张页表进行虚拟地址到物理地址的转化,如果有一个进程挂掉了,也只会影响这个页表的转化关系,对其他的页表的转化关系没有影响。
自己划分局域进行映射
我们知道,栈区和堆区之间有相当大的一段留白,这段区域我们也是可以利用的,我们也可以在这段空白上划分自己的区域,来进行我们相应的映射关系。
而我们自己划分的区域,也有一个结构体来维护vm_area_struct: