【Linux】进程地址空间与虚拟地址空间
🔥个人主页🔥:孤寂大仙V
🌈收录专栏🌈:Linux
🌹往期回顾🌹:【Linux】环境变量
🔖流水不争,争的是滔滔不
- 一、概念范畴
- 二、进程地址空间
- 三、为什么要有虚拟地址空间
- 四、使用页表的一些优点
一、概念范畴
进程地址空间:是从进程角度出发,描述一个进程在运行时能够访问和使用的内存地址范围,是与具体进程紧密绑定的概念,强调的是进程对内存资源的占有和使用,每个进程都有一套独立的进程地址空间,用于存放该进程运行所需的各种资源,如代码、数据、堆栈等。
虚拟地址空间:是一种系统层面的内存管理抽象概念,是对整个系统中内存地址的一种虚拟划分方式,它为每个进程提供了一个看似独立、连续且独占的地址空间,让进程在这个虚拟的地址环境中运行,而不必关心实际物理内存的布局和其他进程的内存使用情况。
涵盖范围
进程地址空间:仅针对单个进程而言,只包含了一个特定进程运行时所涉及的代码、数据、堆栈等所在的地址范围,是虚拟地址空间的一部分,其大小通常由进程的需求和系统的限制共同决定,不同进程的进程地址空间是相互独立且隔离的。
虚拟地址空间:是系统为所有进程提供的一个统一的虚拟地址架构,它涵盖了系统中所有进程可能使用到的虚拟地址范围,是一个更为宏观和整体的概念,通常会占据一个非常大的地址范围,比如在 32 位系统中一般是 4GB 的地址空间,所有进程的进程地址空间都包含在这个大的虚拟地址空间内。
作用对象
进程地址空间:主要作用是为单个进程提供独立的内存运行环境,确保进程的代码、数据等在运行过程中相互隔离和保护,防止不同进程之间的干扰和数据破坏,使得每个进程都能独立、稳定地运行,就像每个进程都有自己独立的 “小天地” 来进行各种操作。
虚拟地址空间:主要作用是为操作系统提供一种高效的内存管理机制,通过虚拟地址与物理地址的映射,实现内存的动态分配、回收、保护和共享等功能,方便操作系统对整个系统的内存资源进行统一管理和调度,提高内存的利用率和系统的整体性能,它更像是一个为操作系统管理内存而构建的 “大框架”。
实现方式
进程地址空间:进程地址空间的实现依赖于虚拟地址空间技术,通过操作系统为每个进程创建独立的页表等数据结构来实现进程地址空间的隔离和管理,页表记录了进程虚拟地址与物理内存页面的映射关系,确保进程只能访问自己地址空间内的内存。
虚拟地址空间:虚拟地址空间的实现需要硬件和软件的协同工作,硬件方面主要由内存管理单元(MMU)来完成虚拟地址到物理地址的转换,软件方面则由操作系统负责维护页表、管理物理内存分配、处理缺页中断等操作,共同构建出一个虚拟的、对进程透明的地址空间环境。
虚拟地址空间是宏观上为所有进程构建的整体概念,并非局限于单个进程地址空间的具体布局。
二、进程地址空间
一个进程一个虚拟地址空间。一个进程一个页表。
页表是操作系统里管内存的关键数据结构。它像本 “地址映射字典”,记录着虚拟地址和物理地址的对应关系。
进程用的是虚拟地址,当要访问内存时,内存管理单元依据页表把虚拟地址转成物理地址。页表项里有物理页框号等信息,还能标记地址是否有效、访问权限等,帮系统管理内存,保障访问安全。
进程地址空间是pcb构建的结构体,进程地址空间中划分为了几个区域被pcb统一管理起来。每个进程存放的是虚拟地址,某个进程的虚拟地址通过页表映射为物理地址,进而访问内存中的指定变量。这些操作都是由操作系统来完成的。
创建父进程和进程的时候虚拟地址和物理地址的变化
创建父子进程的时候,父子各有一份虚拟地址。父子进程共享代码和数据,此时虚拟地址和物理地址是一样的,父子进程的页表虚拟地址与物理地址的映射关系也相同。当此时对子进程进行修改,子进程发生写时拷贝。子进程的物理地址发生变化,子进程的虚拟地址不会发生变化。只是子进程页表中虚拟地址和物理的地址的映射关系发生了变化。
描述linux下进程的地址空间的所有的信息的结构体是 mm_struct (内存描述符)。每个进程只有一个mm_struct结构,在每个进程的task_struct结构中,有⼀个指向该进程的结构。 可以说,mm_struct结构是对整个用户空间的描述。每⼀个进程都会有自己独立的mm_struct,这样每⼀个进程都会有自己独立的地址空间才能互不干扰。
那既然每⼀个进程都会有自己独立的mm_struct,操作系统肯定是要将这么多进程的mm_struct组织起来的!虚拟空间的组织方式有两种:
- 当虚拟区较少时采取单链表,由mmap指针指向这个链表;
- 当虚拟区间多时采取红黑树进行管理,由mm_rb指向这棵树。
linux内核使用 vm_area_struct 结构来表示⼀个独立的虚拟内存区域(VMA),由于每个不同质的虚拟内存区域功能和内部机制都不同,因此⼀个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域。上面提到的两种组织方式使用的就是vm_area_struct结构来连接各个VMA,放便进程快速访问。
上面我们说的操作系统利用虚拟地址映射为物理地址在物理内存中寻址。下面我们来聊聊磁盘中的代码和数据如何存放到内存。
当程序启动时,操作系统为其分配虚拟地址空间,代码和数据在虚拟内存中拥有固定的虚拟地址。操作系统将代码和数据从磁盘加载到物理内存中,此时物理地址尚未确定。操作系统通过页表将虚拟地址映射到物理地址,完成虚拟地址到物理地址的转换。程序运行时,CPU使用虚拟地址访问内存,内存管理单元(MMU)通过页表将其转换为物理地址。虚拟地址在程序加载前就已经分配,物理地址在程序加载后通过映射确定。
三、为什么要有虚拟地址空间
安全
每个进程都可以访问任意的内存空间,这也就意味着任意⼀个进程都能够去读写系统相关内存区域,如果是⼀个木马病毒,那么他就能随意的修改内存空间,让设备直接瘫痪。
地址问题
众所周知,编译完成后的程序是存放在硬盘上的,当运行的时候,需要将程序搬到内存当中去运行,如果直接使用物理地址的话,我们无法确定内存现在使用到哪里了,也就是说拷贝的实际内存地址每⼀次运行都是不确定的,比如:第⼀次执行a.out时候,内存当中⼀个进程都没有运行,所以搬移到内存地址是0x00000000,但是第⼆次的时候,内存已经有10个进程在运行了,那执行a.out的时候,内存地址就不⼀定了 。
效率
如果直接使用物理内存,进程作为一个整体(内存块)进行操作,当物理内存不足时,通常会将不常用的进程拷贝到磁盘的交换分区中以腾出内存。然而,使用物理地址时,需要将整个进程一起拷贝,这会导致内存和磁盘之间的拷贝时间过长,效率较低。
四、使用页表的一些优点
地址空间和页表是OS创建并维护的!是不是也就意味着,凡是想使用地址空间和页表进行映射,也一定要在OS的监管下来进行访问!!也顺便,包括各个进程以及内核的相关有效数据! 保护了物理内存中的所有的合法数据。
• 因为有地址空间的存在和页表的映射的存在,我们的物理内存中可以对未来的数据进行任意位置的加载!物理内存的分配和进程的管理就可以做到没有关系。进程管理模块和内存管理模块就完成了解耦合。
◦ 因为有地址空间的存在,所以我们在C、C++语言上new, malloc空间的时候,其实是在地址空间上申请的,物理内存甚至可以一个字节都不给你。而当你真正进行对物理地址空间访问的时候,才执行内存的相关管理算法,帮你申请内存,构建页表映射关系(延迟分配),这是由操作系统自动完成,用户包括进程完全0感知!!
• 因为页表的映射的存在,程序在物理内存中理论上就可以任意位置加载。它可以将地址空间上的虚拟地址和物理地址进行映射,在进程视角所有的内存分布都可以是有序的。
一些小补充
之前文章的操作系统的挂起状态是怎么进行的,在这里通过这篇文章的知识重提一下。当内存资源不足的时候操作系统就会发生挂起状态,通过把物理内存的物理地址的内容和页表中的物理地址清空放到磁盘中swap分区中。