当前位置: 首页 > news >正文

进程地址空间

目录

回顾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的新特性–可变模版参数&#xff08;variadic templates&#xff09;是C11新增的最强大的特性之一&#xff0c;它对参数进行了高度泛化&#xff0c;它能…...

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 标注&#xff08;Icon&#xff09; require 引入图片问题环境版本Openlayers 使用 require 问题Webpack5 正确配置构建新环境的时候&#xff0c;偶然发现 Openlayers 使用 require 的方式加载图片&#xff08;Icon&#xff09;报错&#xff0c;开始…...

Zookeeper安装部署

文章目录Zookeeper安装部署Zookeeper安装部署 将Zookeeper安装包解压缩&#xff0c; [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 ( 临项交换贪心 )

题目大意&#xff1a; N 头牛 &#xff0c; 每头牛有一个重量(Weight)和一个力量(Strenth) &#xff0c; N头牛进行排列 &#xff0c; 第 i 头牛的风险值为其上所有牛总重减去自身力量 &#xff0c; 问如何排列可以使最大风险值最小 &#xff0c; 求出这个最小的最大风险值&am…...

MySQL:为什么说应该优先选择普通索引,尽量避免使用唯一索引

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

Spring Cloud alibaba之Feign

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

零信任-Google谷歌零信任介绍(3)

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

Numpy基础——人工智能基础

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

电商仓储与配送云仓是什么?

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

【零基础入门前端系列】—HTML介绍(一)

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

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...

Pydantic + Function Calling的结合

1、Pydantic Pydantic 是一个 Python 库&#xff0c;用于数据验证和设置管理&#xff0c;通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发&#xff08;如 FastAPI&#xff09;、配置管理和数据解析&#xff0c;核心功能包括&#xff1a; 数据验证&#xff1a;通过…...

【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!

【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...