进程地址空间的奥秘:让你理解程序如何在计算机中生存
- 一. 程序地址空间
- 1.1 基本概念
- 1.2 虚拟内存管理
- 1.3 为什么存在虚拟地址空间
- 1.3.1 意义
- 2. 最后
本文将介绍进程地址空间的基本概念与结构,帮助读者理解操作系统如何管理和分配内存。进程地址空间指的是操作系统为每个运行的进程分配的内存区域,包括代码段、数据段、堆区、栈区等。这些区域各自有特定的功能与管理方式。代码段用于存放程序执行指令,数据段用于存放全局变量,堆区用于动态分配内存,而栈区则用于存储函数调用时的局部变量和返回地址。通过了解这些内存区域的作用与分配机制,读者能更好地理解操作系统如何保障多进程运行时的内存安全与隔离性。
💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!
👍 点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀 分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对Linux OS感兴趣的朋友,让我们一起进步!
一. 程序地址空间
1.1 基本概念
虚拟地址空间是指操作系统为每个进程提供的一个独立的内存空间,它与实际物理内存分离。每个进程都拥有自己的虚拟地址空间,操作系统通过内存管理单元(MMU)将虚拟地址映射到物理内存地址,实现了进程间内存的隔离和保护。虚拟地址空间使得每个进程似乎拥有从零开始的连续内存,避免了直接访问物理内存的复杂性,同时提供了更高的灵活性和安全性。
下面来段代码,来验证确实有虚拟地址的存在!
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 0;
}
else if(id == 0){ //child,⼦进程肯定先跑完,也就是⼦进程先修改,完成之后,⽗进程再
读取
g_val=100;
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
}else{ //parent
sleep(3);
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
sleep(1);
return 0;
}
输出结果:
child[3046]: 100 : 0x80497e8
parent[3045]: 0 : 0x80497e8
通过现象可以看出,全局变量g_val在父子进程中值不相同,地址却相同。这里的地址就是虚拟地址,为什么值不一样,因为每个进程都会有独立的虚拟地址空间和一套页表(该页表作用:建立虚拟地址与物理地址的映射关系),OS会将虚拟地址转化为物理地址。
图来理解该过程更形象,如下图:
结论:上⾯的图就⾜矣说明问题,同⼀个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址!
OS将每个区域进行区域划分。用mm_struct结构体保存相关属性。
上述已经提到了每个进程都有地址空间,那么多的进程地址空间,OS需不需要管理呢???需要的,如何管理:先描述,再组织。
1.2 虚拟内存管理
linux下进程的地址空间的所有的信息的结构体是 mm_struct (内存描述符)。每个进程只有⼀个mm_struct结构,在每个进程的task_struct结构中,有⼀个指向该进程的结构。
- mm_struct保存的部分属性信息如下:
struct mm_struct
{
/…/
struct vm_area_struct mmap; / 指向虚拟区间(VMA)链表 /
struct rb_root mm_rb; / red_black树 /
unsigned long task_size; /具有该结构体的进程的虚拟地址空间的⼤⼩/
/…/
// 代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
/…*/
}
mm_struct结构是对整个⽤⼾空间的描述。每⼀个进程都会有⾃⼰独⽴的mm_struct,这样每⼀个进程都会有⾃⼰独⽴的地址空间才能互不⼲扰。先来看看由task_struct到mm_struct,进程的地址空间的分布情况:
因为上述已经说过进程地址空间存在不同的区域,为了达到快速访问的目的,使用vm_area_struct来;连接各个虚拟内存区域(VMA)。
linux内核使⽤ vm_area_struct 结构来表⽰⼀个独⽴的虚拟内存区域(VMA),由于每个不同质的虚拟内存区域功能和内部机制都不同,因此⼀个进程使⽤多个vm_area_struct结构来分别表⽰不同类型的虚拟内存区域。上⾯提到的两种组织⽅式使⽤的就是vm_area_struct结构来连接各个VMA,⽅便进程快速访问。
- vm_area_struct内核结构如下:
struct vm_area_struct {
unsigned long vm_start; //虚存区起始
unsigned long vm_end; //虚存区结束
struct vm_area_struct *vm_next, *vm_prev; //前后指针
struct rb_node vm_rb; //红⿊树中的位置
unsigned long rb_subtree_gap;
struct mm_struct *vm_mm; //所属的 mm_struct
pgprot_t vm_page_prot;
unsigned long vm_flags; //标志位
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
struct list_head anon_vma_chain;
struct anon_vma *anon_vma;
const struct vm_operations_struct *vm_ops; //vma对应的实际操作
unsigned long vm_pgoff; //⽂件映射偏移量
struct file * vm_file; //映射的⽂件
void * vm_private_data; //私有数据
atomic_long_t swap_readahead_info;
#ifndef CONFIG_MMU
struct vm_region vm_region; / NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
struct mempolicy vm_policy; / NUMA policy for the VMA */
#endif
struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;
将上述结构进行量化,如图:
1.3 为什么存在虚拟地址空间
- 内存隔离与安全性:
虚拟地址空间为每个进程提供独立的内存视图,避免了进程间直接访问彼此内存的风险。这样,即使一个进程出现问题,它的错误也不会影响到其他进程的运行,确保系统的稳定性与安全性。 - 简化内存管理:
操作系统通过虚拟地址空间为每个进程提供统一的内存模型,进程无需关心物理内存的具体布局。操作系统可以将虚拟地址映射到不同的物理内存位置,优化内存资源的使用,提高内存管理的灵活性。 - 支持虚拟内存机制:
虚拟地址空间使得操作系统能够实现虚拟内存机制,将物理内存的使用扩展到磁盘存储。通过分页和分段等技术,操作系统可以将大于物理内存容量的数据载入内存,只在需要时加载部分内容,从而实现程序运行时的内存“扩展”。 - 进程迁移与共享:
在多任务操作系统中,虚拟地址空间使得进程迁移和共享变得容易。虚拟地址映射机制允许进程在不同的物理机器上运行,或者让多个进程共享某一部分内存(如共享库)。
1.3.1 意义
- 地址空间隔离与安全性
虚拟地址空间为每个进程提供独立的地址空间,使得不同进程之间的内存互不干扰。这种隔离机制有效防止了进程之间相互访问内存的风险,从而提高了系统的安全性。例如,一个进程不能直接修改另一个进程的内存,避免了内存泄漏或数据破坏的情况。 - 简化程序开发与管理
对程序员而言,虚拟地址空间让他们无需关心物理内存的分配与管理。程序可以假定自己拥有完整的内存空间,而操作系统通过地址映射将虚拟地址转换为物理地址。这样,开发者可以集中精力在逻辑和功能开发上,减少了复杂的内存管理问题。 - 内存共享与优化
虚拟地址空间还支持进程间的内存共享。例如,当多个进程需要访问相同的共享库时,操作系统可以通过映射同一块物理内存到各自的虚拟地址空间中,从而避免重复加载相同的内容,节省内存资源。此外,虚拟内存还允许操作系统通过分页技术将内存按需分配,优化内存使用。 - 支持虚拟内存机制
虚拟地址空间使得虚拟内存的概念成为可能。通过虚拟内存,操作系统能够将物理内存和磁盘空间结合使用,允许程序运行超出实际物理内存大小的应用程序。当物理内存不足时,操作系统会将一些数据暂时移至磁盘(交换区),保证进程继续运行,提升了系统的灵活性和吞吐量。
综上,虚拟地址空间不仅保障了内存的安全、管理简便,还提升了内存利用率和系统性能,是现代操作系统不可或缺的基础设施。
2. 最后
本文主要介绍了进程地址空间的基本概念及其管理方式。进程地址空间是操作系统为每个进程提供的独立内存区域,包括代码段、数据段、堆区和栈区等。通过虚拟内存管理,操作系统实现了进程间内存隔离和保护,确保了系统的安全性与稳定性。文章还阐述了虚拟地址空间的意义,包括内存隔离、简化内存管理、支持虚拟内存机制以及内存共享与优化等。通过这些机制,操作系统能够高效管理内存,提升系统性能和资源利用率,是现代操作系统不可或缺的基础。
路虽远,行则将至;事虽难,做则必成
亲爱的读者们,下一篇文章再会!!! \color{Red}亲爱的读者们,下一篇文章再会!!! 亲爱的读者们,下一篇文章再会!!!