进程地址空间
目录
回顾C/C++语言的程序地址空间
感性认识虚拟地址空间
虚拟地址空间与物理空间如何建立映射关系
为什么要虚拟地址空间?
回顾C/C++语言的程序地址空间
在学习C/C++语言时我们知道了一个概念叫程序地址空间。通俗来说就是如下一张表,从中可以得知系统的几个区域:
现在有个问题,这个表是内存吗?来看个例子就知道了:
可以看到,一开始父进程和子进程对应打印的全局变量数值和地址都一样,奇怪的事情出现了:当全局变量的值在子进程中被改变后,父进程的变量地址和子进程的地址还是同一个,但是两个的值不一样!
这是不可想象的,之前讲进程的时候说了,进程具有独立性,多进程下进程间是互不干扰的,即使共用代码但也会根据条件判断语句分开执行。 一个物理地址对应的数据流只有一个,对应的值也只有一个,但是这里确实出现了一个地址对应两个不同值的情况。这只能说明一个事实:这里的地址不是物理地址!
这里的地址叫做虚拟地址,也叫线性地址或逻辑地址。这就回答了上面的问题,那不是物理意义的内存,而是虚拟内存,所以以前学语言的时候说的指针不是物理地址,而是虚拟地址。
感性认识虚拟地址空间
进程在运行的时候会有一个错觉,它觉得它是独占CPU资源的,实际上在我们看来根本不是这样,CPU每时每刻都在调度不同进程,不断进行着切换。
根据这一点我举个例子帮助感性理解:
有个大帮派势力暗中统治着几座城市,帮主手下有两个心腹,但是他们俩互相不知道对方的存在。
为了让两个心腹好好干活别想着觊觎帮主之位,帮主对心腹A说,我死后这个集团就交给你打理了;同时,他也对心腹B说了一样的话,相当于分别给他们俩画了个大饼,而他们两人都不知道帮主和另一个人也许下了承诺。于是两人好好干,有一天A对帮主说想要更多实权,帮主说反正我快走了到时候一切都交给你,但你现在先别急,于是A愉快地干活去了,B也经历了同样的事。
这里帮主就好比操作系统,两个心腹A,B就是进程,两个进程以为自己独占着操作系统资源,操作系统为了更好地调度他们,给他们画了大饼,这张饼也就是”进程地址空间“,当进程想要向操作系统多要点空间资源时,操作系统不能一次全给它(本身内存吃紧要节省空间)就会分批一次一次给空间——对应我们malloc/new开空间。
那么操作系统是如何画饼的呢?————还是类比刚刚的例子,实际上画饼就是在人的脑中构建蓝图,可以看成一个数据结构:
struct 蓝图
{char* who;char* when;char* target;
};
那么操作系统也是一样,是对进程地址空间做管理,管理的本质是先描述再组织,描述也一样用struct结构体描述起来,地址空间的本质就是内核的数据结构mm_struct
地址空间上有heap,stack等区域,那么操作系统是怎么在地址空间上将它们描述起来的呢?————下面再举个例子:
帮主的帮派很大管理着几个城市,但是同等大小的势力也存在,并和该帮派对抗着。为了不引起大规模的乱斗,两个帮派划地分界,谁都不准越界半步。那么用数据结构进行区域划分是这样的:
struct area //区域
{unsigned int A_start; //帮派A区域起始位置unsigned int A_end; //帮派A区域结束位置unsigned int B_start; //帮派B区域起始位置unsigned int B_end; //帮派B区域结束位置
};struct area partition = {1,50,51,100};
//帮派A区域 [1,50]
//帮派B区域 [51,100]
两个帮派定好地界后还是有问题,因为货物运输交易经常牵扯到边界附近地带,容易造成摩擦,所以经过商量将小镇T作为公共使用区域,两边都可涉足:
struct area partition = {1,45,55,100};
这是区域调整的方法。
类比以上,操作系统也是一个道理。
地址空间描述的基本空间大小是字节,32位下是2^32个地址,1个地址1字节,一共大概4GB空间。每一个字节都要有唯一的地址。
如此一来,操作系统就可以根据划分好的地址描述heap等区域:
struct mm_struct
{unsigned int code_start;unsigned int code_end;unsigned int stack_start;unsigned int stack_end;.......
};
假设mm_struct这个内核数据结构就是这样的,里面区域划分假定如上,总之默认占4GB大小。
code_start 到 code_end这段区域里面有很多地址,叫做虚拟地址。
想要调整区域,像例子中的设定公共区域,其实就是改变区域的起始地址和结束地址,比如要扩大stack区域,可以增加stack_end。我们写C/C++代码时,malloc/new空间实际上就是扩大堆区,free就是缩小堆区。
我们知道,进程在创建的时候操作系统会创建一个PCB内核数据结构task_struct,里面存了进程的相关信息如优先级...然后操作系统给进程 “画大饼” ,进程为了得到这块大饼,它的task_struct里面有个指针指向mm_struct。
虚拟地址空间与物理空间如何建立映射关系
这里牵扯到页表等相关概念,我们处理一下简单讲解。
我们已经知道磁盘上的程序会加载到内存,并且一字节对应内存上的一个物理地址空间。
假设mytest.exe程序大小是8KB,内存起始地址是0x1111 1111,那么虚拟进程地址空间是如何与物理内存建立映射关系的呢?————系统中有一个东西叫页表,可以建立映射。
关于页表,页等概念这里先不说,主要看进程地址空间。
虚拟地址空间的一个字节0xFF01 EEEC存到页表左边,与右边存物理地址空间的0x2100 1110对应。 假设我们写代码int a = 10; 这里&a就是虚拟地址,会对应虚拟进程地址空间的一个字节,再对应页表的数据,再与物理空间对应。当修改a的值,物理内存存储的值也被改变。
补充:
1、在Linux下,我们认为虚拟地址和线性地址是一个概念(在其它某些OS下不一样)。因为2^32个虚拟地址都是紧挨一起线性排布下去的。
2、再次画图理解整个过程
每个进程都有自己的task_struct和mm_struct以及页表,当然物理内存只有一个,通过上述方式建立联系。操作系统OS操控管理着一切,给进程 “画大饼”,让进程看似好像独享操作系统资源,坐拥2^32字节大小空间,但其实进程无法直接查看物理内存大小和占用情况,只能通过 mm_struct ---> 页表---->物理内存的方式,而进程胃口也比较小,一次开个10MB.100MB很多了,再多OS也不会再给进程空间了。
为什么要虚拟地址空间?
1、虚拟地址空间可以保护物理空间不被错误修改,提高了系统的安全性。
试想一下要是没有虚拟地址空间,用户直接访问物理空间会发生什么。
假设你想修改一个数据,但是你访问错位置了,将一块重要的数据给修改了;或者你写的程序出现野指针等错误,容易造成系统的崩溃。
刚刚提到的页表除了建立虚拟空间地址和物理内存的映射关系外,还有其他作用,其中之一是保护与拦截。
当进程做出违反操作系统的行为访问物理内存时,页表会进行拦截。因为进程不能直接访问物理内存,需要通过mm_struct--->页表,此时页表就可以拦截越界行为,保护物理内存。
2、虚拟进程地址的存在,可以更方便的进行进程与进程数据代码的解耦,保证进程的独立性。
这段话很抽象,我们用例子来理解。还记得上面父子进程的那个程序吗,在子进程内部改变了全局变量globol_val,子进程和父进程打印的变量数值不同但虚拟地址相同。
现在我们结合刚刚获取的知识再来深入理解一下为什么虚拟地址相同。
系统生成父进程后,再生成子进程,子进程是以父进程为模板构建出来的,tast_struct , mm_struct,页表都一样。它们里面的全局变量globol_val的虚拟地址都依据映射指向物理内存的同一地址,所以一开始执行的时候打印的变量数值一样地址也一样。
当子进程内想要改变全局变量globol_val, 要经过虚拟地址、页表访问物理内存,此时操作系统会做一个工作——拷贝物理内存对应位置的数据给它另一块地址,然后更改页表映射,将子进程虚拟地址映射指向新的物理内存地址,最后更改新的globol_val的值。
为什么操作系统要做这一步?————因为进程具有独立性,当一个进程对被共享的数据进行修改时会破坏独立性,影响其他共享数据的进程,所以为了让进程改变数据的同时不破坏独立性,操作系统将该进程的页表映射更改,所以产生了虚拟地址相同,但对应值不同的现象。
这里的拷贝方式叫做写时拷贝,它可以让不同进程的数据分离。
3、 虚拟地址空间可以让进程以统一的视角来看待进程对应的代码和数据等各个区域,方便使用。
编译器也是以同样的视角来编译代码(地址)。
问题:可执行程序内部代码有地址吗?————有。进入汇编可以查看代码对应的地址,并且在链接的过程中也需要代码地址与库链接。
既然可执行程序代码有地址,那这个地址是什么地址呢?是物理地址还是虚拟地址?————自然是虚拟地址,或者这里更准确的说应该叫逻辑地址。
磁盘上的可执行程序里面有一个main函数,其内部调用了fun函数,并且fun函数的地址是0x1122,main函数的地址是0xFEE0, 程序也有全局数据区,代码区等,32位平台下和上面讲的虚拟地址空间编址方式一样是以32位编址的。
可执行程序加载到内存,天然地就有了一个外部的物理地址,和程序内部编址方式一样的。在程序内部main函数寻址调用fun函数,外部物理内存也是一样通过物理寻址main函数调用fun函数。
所以我们现在有两套地址,标识物理内存中代码和数据的地址,还有在程序内部进行跳转的虚拟地址。
内存将进程数据加载到CPU,CPU收到数据包括main函数和fun函数的地址(注意:此时CPU接收到的地址是虚拟地址!因为CPU读取的是指令,指令内部就有地址),PC指针记录下fun函数的地址,CPU先拿着main函数的虚拟地址,到页表查表映射物理内存,找到对应的main函数的物理地址然后执行main函数。
这里有个问题:CPU执行出来的地址是物理地址还是虚拟地址?————是虚拟地址,虚拟地址通过页表映射到物理内存。再调用main函数内部的fun函数,根据PC指针保存的fun函数地址,CPU再通过页表映射物理内存,此时CPU出来的还是虚拟地址。这样就形成了一个循环。
CPU在这整个过程中没有见到过物理地址,全都是虚拟地址。
相关文章:

进程地址空间
目录 回顾C/C语言的程序地址空间 感性认识虚拟地址空间 虚拟地址空间与物理空间如何建立映射关系 为什么要虚拟地址空间? 回顾C/C语言的程序地址空间 在学习C/C语言时我们知道了一个概念叫程序地址空间。通俗来说就是如下一张表,从中可以得知系统的几…...

数楼梯(加强版)
数楼梯(加强版) 题目背景: 小明一天放学回家,看到从1楼到2楼共有n个台阶,因为好奇,他想尝试一下总共有几种方案到二楼?他可以1步,2步,3步的跳,不能跳3步以上. 他试了很多次都没有解决这个问题,于是请求聪明的你帮忙解决这个问题. 题目描述: 1楼到2楼楼梯有n级台阶。小明每…...
MySQL-数据类型
数据类型简介数据表由多列字段构成,每一个字段指定了不同的数据类型,指定了数据类型之后,也就决定了向字段插入的数据内容。不同的数据类型也决定了 MySQL 在存储它们的时候使用的方式,以及在使用它们的时候选择什么运算符号进行运…...

剑指 Offer 32 - II. 从上到下打印二叉树 II(java解题)
剑指 Offer 32 - II. 从上到下打印二叉树 II(java解题)1. 题目2. 解题思路3. 数据类型功能函数总结4. java代码5. 踩坑记录1. 题目 从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。 例如: 给定二叉…...
C#网络爬虫开发
1前言爬虫一般都是用Python来写,生态丰富,动态语言开发速度快,调试也很方便但是我要说但是,动态语言也有其局限性,笔者作为老爬虫带师,几乎各种语言都搞过,现在这个任务并不复杂,用我…...
Fastjson 总结
0x00 前言 这一篇主要是针对已经完成的fastjson系列做一个知识点总结,一来是为了更加有条理的梳理已经存在的内容,二来是为了更好的复习和利用。 0x01 Fastjson基础知识点 1.常见问题: 问:fastjson的触发点是什么?…...
文件路径模块os.path
文件路径模块os.path 文章目录文件路径模块os.path1.概述2.解析路径2.1.拆分路径和文件名split2.2.获取文件名称basename2.3.返回路径第一部分dirname2.4.扩展名称解析路径splitext2.5.返回公共前缀路径commonprefix3.创建路径3.1.拼接路径join3.2.获取家目录3.3.规范化路径nor…...

Kerberos简单介绍及使用
Kerberos作用 简单来说安全相关一般涉及以下方面:用户认证(Kerberos的作用)、用户授权、用户管理.。而Kerberos功能是用户认证,通俗来说解决了证明A是A 的问题。 认证过程(时序图) 核心角色/概念 KDC&…...
DOM编程-全选、全不选和反选
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>全选、全不选和反选</title> </head> <body bgcolor"antiquewhite"> <script type"text/jav…...

C++11可变模板参数
C11可变模板参数一、简介二、语法三、可变模版参数函数3.1、递归函数方式展开参数包3.2、逗号表达式展开参数包一、简介 C11的新特性–可变模版参数(variadic templates)是C11新增的最强大的特性之一,它对参数进行了高度泛化,它能…...

Linux多线程
目录 一、认识线程 1.1 线程概念 1.2 页表 1.3 线程的优缺点 1.3.1 优点 1.3.2 缺点 1.4 线程异常 二、进程 VS 线程 三、Linux线程控制 3.1 POSIX线程库 3.1 线程创建 3.3 线程等待 3.4 线程终止 3.4.1 return退出 3.4.2 pthread_exit() 3.4.3 pthread_cancel…...

Webpack5 环境下 Openlayers 标注(Icon) require 引入图片问题
Webpack5 环境下 Openlayers 标注(Icon) require 引入图片问题环境版本Openlayers 使用 require 问题Webpack5 正确配置构建新环境的时候,偶然发现 Openlayers 使用 require 的方式加载图片(Icon)报错,开始…...

Zookeeper安装部署
文章目录Zookeeper安装部署Zookeeper安装部署 将Zookeeper安装包解压缩, [rootlocalhost opt]# ll 总用量 14032 -rw-r--r--. 1 root root 12392394 10月 13 11:44 apache-zookeeper-3.6.0-bin.tar.gz drwxrwxr-x. 6 root root 4096 10月 18 01:44 redis-5.0.4 …...

Cow Acrobats ( 临项交换贪心 )
题目大意: N 头牛 , 每头牛有一个重量(Weight)和一个力量(Strenth) , N头牛进行排列 , 第 i 头牛的风险值为其上所有牛总重减去自身力量 , 问如何排列可以使最大风险值最小 , 求出这个最小的最大风险值&am…...

MySQL:为什么说应该优先选择普通索引,尽量避免使用唯一索引
前言 在使用MySQL的过程中,随着表数据的逐渐增多,为了更快的查询我们需要的数据,我们会在表中建立不同类型的索引。 今天我们来聊一聊,普通索引和唯一索引的使用场景, 以及为什么说推荐大家优先使用普通索引…...

Spring Cloud alibaba之Feign
JAVA项目中如何实现接口调用?HttpclientHttpclient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持Http协议的客户端编程工具包,并且它支持HTTP协议最新版本和建议。HttpClient相比传统JDK自带的URL Connection&a…...

零信任-Google谷歌零信任介绍(3)
谷歌零信任的介绍? "Zero Trust" 是一种网络安全模型,旨在通过降低网络中的信任级别来防止安全威胁。在零信任模型中,不论请求来自内部网络还是外部网络,系统都将对所有请求进行详细的验证和审核。这意味着每次请求都需…...

Numpy基础——人工智能基础
文章目录一、Numpy概述1.优势2.numpy历史3.Numpy的核心:多维数组4.numpy基础4.1 ndarray数组4.2 内存中的ndarray对象一、Numpy概述 1.优势 Numpy(Nummerical Python),补充了Python语言所欠缺的数值计算能力;Numpy是其它数据分析及机器学习库的底层库&…...

电商仓储与配送云仓是什么?
仓库是整个供给链的关键局部。它们是产品暂停和触摸的点,耗费空间和时间(工时)。空间和时间反过来也是费用。经过开发数学和计算机模型来微调仓库的规划和操作,经理能够显著降低与产品分销相关的劳动力本钱,进步仓库空间应用率,并…...

【零基础入门前端系列】—HTML介绍(一)
【零基础入门前端系列】—HTML介绍(一) 一、什么是HTML HTML是用来描述网页的一种语言HTML指的是超文本标记语言:HyperText Markup LanguageHTML不是一种编程语言,而是一种超文本标记语言,标记语言是一套标记标签(ma…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
Java并发编程实战 Day 11:并发设计模式
【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天,今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案,它们不仅提供了优雅的设计思路,还能显著提升系统的性能…...
嵌入式面试常问问题
以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...

CMS内容管理系统的设计与实现:多站点模式的实现
在一套内容管理系统中,其实有很多站点,比如企业门户网站,产品手册,知识帮助手册等,因此会需要多个站点,甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...

ZYNQ学习记录FPGA(二)Verilog语言
一、Verilog简介 1.1 HDL(Hardware Description language) 在解释HDL之前,先来了解一下数字系统设计的流程:逻辑设计 -> 电路实现 -> 系统验证。 逻辑设计又称前端,在这个过程中就需要用到HDL,正文…...

Python异步编程:深入理解协程的原理与实践指南
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 持续学习,不断…...