唐山百度网站建设,东莞网络推广网站,建设银行成都市第九支行 网站,企业网络维护本片博客将重点回答三个问题 什么是地址空间#xff1f; 地址空间是如何设计的#xff1f; 为什么要有地址空间#xff1f; 程序地址空间排布图 在32位下#xff0c;一个进程的地址空间#xff0c;取值范围是0x0000 0000~ 0xFFFF FFFF 回答三个问题之前我们先来证明地址空…本片博客将重点回答三个问题 什么是地址空间 地址空间是如何设计的 为什么要有地址空间 程序地址空间排布图 在32位下一个进程的地址空间取值范围是0x0000 0000~ 0xFFFF FFFF 回答三个问题之前我们先来证明地址空间排布是按如图所布局的 各个区空间地址验证代码
#include unistd.h
#include stdio.h
#include stdlib.hint g_unval; // 未初始化数据
int g_val 100; // 初始化数据一般指全局初始化数据
int main(int argc, char* argv[], char* env[]) // 命令行参数环境变量
{// 代码地址打印 printf(code addr: %p\n, main); printf(init global addr: %p\n, g_val);printf(uninit global addr: %p\n, g_unval);// 堆区指针变量本质是变量也要开辟空间不过放的内容是地址char *heap_mem (char*)malloc(10);printf(heap addr: %p\n, heap_mem);// 栈区函数内定义的变量都是在栈上开辟空间printf(stack addr: %p\n, heap_mem); int i 0;for (i 0; i argc; i){ // 命令行参数地址 printf(argv[%d]: %p\n, i, argv[i]);}int j 0;for (j 0; env[j]; j){printf(env[%d]: %p\n, j, env[j]);}return 0;}运行结果 堆、栈之间的两个箭头表示 栈向地址减小的方向增长 堆向地址增大的方向增长 证明方法也很简单 运行结果也证明确实是这样 我们会发现堆区之间差了20字节 我们平时申请空间系统会多给你一些空间 多出的空间用来记录你堆的属性信息 所以平时我们free空间只要传起始地址 剩下的系统知道要free多长的空间
我们在系统部分要记的两个口诀 1、先描述在组织 2、堆、栈相对而生 static 修饰局部变量本质就是将该变量开辟在全局区域 所有的字面常量将来都是要映编码进代码的 在正文代码上其实有一小段是字符常量区
什么是地址空间以及是如何设计的
我们平时打印各种地址其实就是进程打印程序运行之后打印的
在解释什么是地址空间之前我们先来讲一个故事 有一个富豪他有5亿元家产 他有3个私生子彼此并不知道对方存在 3个私生子分别叫张三、李四、王五 富豪为了鼓励3个儿子 对张三说你好好念书将来5亿就是你的了 对另外两个儿子也说了同样的话 因为不知道彼此存在 对于这三个儿子他们都认为是5亿继承人 富豪给他们每一个儿子画了一个大饼 有一天张三对他爸说要1千买学习资料 李四说我成年了想买一辆两百万的跑车 王五说我创业需要50万 富豪都给了他们需要的钱 只要他们要钱富豪都会给 有一天张三说要1亿富豪说要这么多干嘛 拒绝了张三即使被拒绝了张三依旧认为自己是5亿的继承人 我们站在上帝视角知道即使富豪过世了 这三个儿子不可能都拥有5亿 他们每个儿子可以断断续续的要钱 但永远要不到5亿却依然坚信自己以后能拥有这5亿 对应关系 富豪 ---- 操作系统 儿子 ---- 进程 富豪画的饼 ---- 地址空间
在内存中的地址空间本质是一种数据结构 将来要和一个特定的进程关联起来
以前直接访问物理内存如果有野指针的问题 可能直接访问到其他进程 内存本身是随时可以被读写 所以在老式的程序里面野指针是会直接改了其他进程的东西 结论直接使用物理内存不安全
现代计算机的解决方式 每个进程有自己的PCB 操作系统给每个进程一个虚拟的地址空间 通过映射机制映射到物理内存 我们可能会有疑问最终还是会访问物理内存 万一虚拟地址是一个非法地址呢 其实映射机制有一个检查机制万一是非法地址 可以不让你映射 虚拟地址空间究竟是什么?
每个进程都要有地址空间 就好比操作系统要给每个进程画个饼 操作系统要给每个饼做管理 在内存中的地址空间本质是一种内核数据结构 它里面至少有各个区域的划分 我们把如图结构称为地址空间
区域空间并不是死的会有一定的变化 所谓的范围变化本质是对start 或end 标记值 - 特定的范围即可 所以一个地址为什么有两个值 到这里就可以回答这个问题了 刚开始创建时只有父进程 然后创建子进程子进程会继承父进程的属性 所以子进程的页表、地址空间和父进程一样 当子进程尝试修改变量值时 因为要保证进程的独立性 操作系统会重新为子进程开辟一份物理内存 并修改子进程页表的映射关系 但是虚拟地址并不受影响还是一样的地址 但映射到物理内存的不同区域 看到的值便不一样 这种策略就叫作写时拷贝 为什么要存在地址空间
保护物理内存 凡是非法的访问或者映射 os都会识别到并且终止你这个进程 因为地址空间和页表是os创建并维护的 也就意味着凡是想使用地址空间和页表 进行映射也一定要在OS的监管下进行访问OS耦合度更低 因为有地址空间的存在 因为有页表映射的存在 我们的物理内存就可以 对未来的数据进行任意位置的加载 物理内存的分配就可以和 进程的管理互不关联 从而使内存管理模块和进程管理模块 完成解耦合
我们在C、C语言上new、malloc空间时 本质是在虚拟地址空间申请的 因为有地址空间的存在所以上层申请空间 物理内存可以甚至一个字节都不给你 当你真正访问物理地址时才执行 内存相关算法帮你申请内存构建 页表映射关系这样空间使用率为100% 以此提高整机效率
保证进程的独立性 因为有地址空间的存在每一个进程 都认为自己拥有4GB的空间(32) 并且各个区域是有序的进而 可以通过页表映射到不同的区域 来实现进程的独立性每一个进程 不知道也不需要知道其他进程的存在
重新理解什么是挂起 加载的本质就是创建进程但并不是 非得把所有程序的代码和数据加载到 内存中并创建内核数据结构建立映射关系 在极端情况下只有内核结构被创建 此时就叫新建状态 理论上可以实现对程序的分批加载 既然可以分批加载自然可以分批换出 一个进程短时间不会被执行比如阻塞 而使进程的数据和代码被换出就叫挂起 页表不仅仅映射物理内存 磁盘位置也可以映射 所以当代码挂起时不用把数据 刷新到磁盘里。只要把空间直接释放掉 在页表重新填上磁盘当中代码和数据的 位置就可以完成一次基本的挂起 扩展知识 在vim中注释 Ctrl v 进入视图模式(V-BLOCK) hjkl 选中需要注释代码 输入大写的i左下角出现INSERT 输入 // 再按esc 自动注释选中的代码 取消注释还是上面的操作 选中需要注释的代码按d删除 ✨✨✨✨✨ 本篇博客完感谢阅读 如有错误之处可评论指出博主会耐心听取每条意见