浅谈linux下的进程地址空间(虚拟地址/线性地址)

目录

什么是地址空间 - 虚拟地址空间

地址空间是如何设计的

为什么要有地址空间


什么是地址空间?

示例:

运行之后发现:同一个变量,同一个地址,在运行一段时间后,竟然会在同一时间出现两个不同的值?这是完全违背常理的,按道理来说一个 int类型 ,只能存储一个整数,为什么这里会出现两个完全不同的值呢???

要想了解这个问题,我们需要先了解一些东西。

linux下的地址空间:注意,这里指的是虚拟地址空间

【说明】
1.栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等,栈是向下增长的。

⒉.共享区:内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

3.堆区:堆用于程序运行时动态内存分配,堆是可以上增长的。一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配方式类似于链表。

4.数据段:--存储全局数据和静态数据。(未初始化/初始化数据)

5.代码段:--可执行的代码/只读常量。(正文代码)

上述的地址空间,也可以通过代码验证:示例:

打印结果如下:可以发现的确是符合进程空间地址排布规律的,地址也是由低到高进行增长的。(注:这里栈区之所以很大,是因为使用的是云服务器)

        上述的进程地址空间被称为 --- 虚拟地址空间 / 线性地址空间,它并不是实际上存储数据的物理内存空间,而是一种Linux下的内核数据结构,并且每一个进程都有独立私有的虚拟地址空间。

这里在Linux内核的源码中也有体现:进程的虚拟地址空间

那么如何管理各个区域呢?

在一个范围内定义start,end本质上就是在进行区域划分

所谓的范围变化,本质其实就是对start or end标记值 + -  特定的范围即可

地址空间是Linux内核数据结构,它里面一定有区域划分
 


地址空间是如何设计的

我们在虚拟地址和物理内存之间会设计一个页表,来维系二者的关联。

我们可以把各个区域及其对应的地址通过页表映射到不同的物理内存中,也就是说页表是维护映射关系的

这种映射关系是来维护虚拟地址和物理地址之间的的一种映射关系

换句话说,每个进程即使它的虚拟地址空间是完全一样的,但是只要它的页表映射关系是不同的,映射到物理内存的不同区域,那么它们就可以做到每个进程是具有独立性的

注:这里的页表是底层是哈希表,存储的是key_value结构,左边存储的的虚拟地址,右边存储的是物理内存地址


回答最开始的现象,为什么同一个地址,在同一个时刻,同时读取的时候,出现不同的值?

是因为这里的地址,绝对不是物理内存的地址,而是虚拟地址(线性地址)

所以,几乎所有的语言,如果它有”地址“的概念,那么这个地址一定不是物理地址,而是虚拟地址

        子进程继承了父进程大部分属性,页表和地址空间都相同,刚开始父进程对应的全局变量g_val的虚拟地址被映射到了物理内存上

        创建子进程的时候,因为页表的映射关系相同,此时父进程与子进程指向的是同一个变量g _val,所以地址相同,甚至值也相同

        但是当子进程尝试去修改g_val,因为要保障进程的独立性,当操作系统识别到当前的子进程想去通过页表去访问g_val,想要写入的时候

        那么操作系统会重新开辟一块空间,然后有必要的情况下,拷贝原来的值,然后再更改子进程的映射关系,防止子进程修改到原来地址的g_val

        完成更改之后,因为虚拟地址并不受影响,所以虚拟地址的值是相同的,但是物理数据经过页表的映射,被映射到了不同的区域,所以看到的g _val的值是不同的

补:最开始创建指向同一个位置,当子进程尝试去做修改的时候,才发现地址是不同的,这种策略叫做写时拷贝


深入了解虚拟地址空间:

当我们的程序,在编译的时候,形成可执行程序的时候,没有被加载到内存中的时候,其实已经有地址了! !

可执行程序其实编译的时候,内部已经有地址了!

地址空间不要仅仅是系统内部要遵守的,其实编译器也要遵守!

即编译器编译代码的时候,就已经给我们形成了各个区域代码区,数据区,...并且,采用和Linux内核中一样的编址方式,给每一个变量,每一行代码都进行了编址,故,程序在编译的时候,每一个字段早已经具有了一个虚拟地址!

物理内存中,可执行程序内部的地址,依旧用的是编译器编译好的虚拟地址

当程序加载到内存的时候,每行代码,每个变量边具有了一个物理地址,外部的
 

1.进程访问数据时,地址空间和页表(虚拟地址的一部分)会被编译时生成的虚拟地址填充,页表(物理地址的一部分)会被加载时产生的物理地址填充。

2例子:假设函数A调用函数B
此时,进程开始运行,cpu拿到的是编译时函数A产生的虚拟地址,通过函数A的虚拟地址映射到了物理内存
此时,访问内存空间,通过函数A中存储B函数的虚拟地址,获现到了的B函数的虚拟地址,再次通过映射关系访问到B函数(其中,A函数调用B函数,编译时A函数中一定会存储B函数的虚拟地址)
 


为什么要有地址空间

1.保障安全

历史原因:

        在当初,可执行程序被加载到内存中,进程是可以直接访问物理内存的,但是我们需要明白,内存本身是随时都可以被修改的,假如出现野指针的问题,进程之间是会被相互干扰,此时安全是很难保障的。

于是,现代计算机在此基础之上,提出了以下方式,虽然通过虚拟地址最终还是会访问到了物理内存,但是需要映射,而页表会对指针进行监测,如果出现非法访问,是可以禁止映射的。

凡是非法的访问或者映射,系统都会识别到,并终止你这个进程,此时有效的保护了物理内存

因为地址空间和页表是系统创建并维护的,也就意味着凡是想使用地址空间和页表进行映射,也一定要在OS的监管之下来进行访问!!

也就保护了物理内存中的所有的合法数据,包括各个进程,以及内核的相关有效数据!
 

2.方便管理

a.解耦合

因为有地址空间的存在,因为有页表映射的存在,我们的物理内存中,可以对未来的数据进行任意位置的加载

物理内存的分配就可以和进程的管理,做到没有任何关系!!

内存管理模块 vs 进程管理模块就完成了
 

注:所以,我们在C、C++语言上,new,malloc空间的时候,本质是在虚拟地址空间上申请的

因为如果我申请了物理空间,但是如果我不立马使用? 是不是空间的浪费呢?  是的!!

本质上,(因为有地址空间的存在,所以上层申请空间,其实是在虚拟地址空间上申请的,物理内存可以甚至一个字节都不给你! !而当你真正进行对物理地址空间访问的时候,才执行内存的相关管理算法,帮你申请内存,构建页表映射关系),然后,再让你进行内存的访问。这都是由操作系统,自动完成,用户,包括进程,完全0感知 ---缺页中断!
 

b.有序性

因为在物理内存中理论上可以任意位置加载,那么物理内存中的几乎所有的数据和代码在内存中是乱序的!

但是,因为页表的存在,它可以将地址空间上的虚拟地址和物理地址进行映射,那么在进程视角所有的内存分布,都可以是有序的!!

地址空间+页表的存在可以将内存分布,有序化!
 

c.独立性

通过进程映射到不同的物理内存,很容易做到让不同的进程具有独立性

地址空间+页表的存在可以实现进程的独立性


其实这也符合linux下,一切皆文件的概念

上帝视角下,内存可以任意读写,那么我们的非法访问也就是权限不够,也就是Linux下,一切皆文件的概念

注:以上仅代表个人观点,仅供参考

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/487096.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录day30(2)回溯:组合(leetcode77)

题目要求:给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 思路:首先定义两个变量,一个存放符合条件的单一结果,另一个存放符合条件结果的集合,for循环用来横向遍历,递归用来纵…

C语言例4-9:格式字符s的使用例子

代码如下&#xff1a; //格式字符s的使用例子 #include<stdio.h> int main(void) {printf("%s,%5s,%-5s\n","Internet","Internet","Internet");//以三种不同格式&#xff0c;输出字符串printf("%10.5s,%-10.5s,%4.5s\n&q…

Excel 打开后提示:MicrosoftExcel无法计算某个公式。在打开的工作簿中有一个循环引用...

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 MicrosoftExcel无法计算某个公式。在打开的工作簿中有一个循环引用&#xff0c;但无法列出导致循环的引I用。请尝试编辑上次输入的公式&#xff0c;或利用“撤消”命令删除该公式&#xff0c;如下图&…

什么是CVE? CVE漏洞应该如何防护?

CVE&#xff08;Common Vulnerabilities and Exposures&#xff09;的全称是公共漏洞和暴露&#xff0c;是公开披露的网络安全漏洞列表。IT人员、安全研究人员查阅CVE获取漏洞的详细信息&#xff0c;进而根据漏洞评分确定漏洞解决的优先级。 在CVE中&#xff0c;每个漏洞按CVE-…

webserver如何从零开始?

我们要做一个项目&#xff0c;过程是怎么样的呢&#xff1f;git clone ...部署&#xff0c;测试&#xff0c;然后开始写么&#xff0c;这样你大概率会“猪脑过载”&#xff0c;对一个项目的每个部分都没有清晰认识&#xff0c;能写出什么来&#xff1f;写之前当然需要测试每个功…

【Selenium】隐藏元素的定位和操作|隐藏与isDisplay方法

一、selenium 中隐藏元素如何定位&#xff1f; 如果单纯的定位的话&#xff0c;隐藏元素和普通不隐藏元素定位没啥区别&#xff0c;用正常定位方法就行了 但是吧~~~能定位到并不意味着能操作元素&#xff08;如click,clear,send_keys&#xff09; 二、隐藏元素 如下图有个输入框…

NOMA免调度接入技术

标题 系统模型 参考视频&#xff1a;添加链接描述 利用接收机的复杂度提升为代价&#xff0c;提升频谱效率。为了保证上行方向上面&#xff0c;能够接入更多的用户&#xff0c;NOMA的根本思路&#xff0c;就是让多个用户占用相同的资源进行上行传输 系统模型 采用TDMA的方式…

修复编译RK3568-buildroot时提示不能使用root权限编译的问题

问题 使用 rk3568 的 sdk 中的 buildroot 来编译根文件系统的时候&#xff0c;出现 "you should not run configure as root (set FORCE_UNSAFE_CONFIGURE1 in environment&#xff09;" 的错误。 解决方法 根据错误提示&#xff0c;我们将 set FORCE_UNSAFE_CON…

Docker在虚拟机中的基本配置

1、Docker解决依赖兼容问题&#xff0c;Docker是如何实现的呢&#xff1f; Docker为了解决依赖的兼容问题的&#xff0c;采用了两个手段&#xff1a; - 将应用的Libs&#xff08;函数库&#xff09;、Deps&#xff08;依赖&#xff09;、配置与应用一起打包 - 将每个应用放到…

【Excel表格中如何将单元格数据复制粘贴到合并后的单元格中】

要实现的效果如下&#xff1a; 方法一、使用插件 1、下载“方方格子”插件 下载地址&#xff1a;http://www.ffcell.com/home/ffcell.aspx 2、下载完成后&#xff0c;启动WPS或Excel软件&#xff0c;同意添加插件&#xff0c;选择【方方格子】-【复制粘贴】-【复制到合并区域…

Acwing_795前缀和 【一维前缀和】+Acwing_796子矩阵的和 【二维前缀和】

Acwing_795前缀和 【一维前缀和】 题目&#xff1a; 代码&#xff1a; #include <bits/stdc.h> #define int long long #define INF 0X3f3f3f3f #define endl \n using namespace std; const int N 100010; int arr[N];int n,m; int l,r; signed main(){std::ios::s…

网络七层模型之应用层:理解网络通信的架构(七)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

权限系统设计

权限设计 为什么要有权限设计&#xff1f; 对于一个系统&#xff0c;有多个模块&#xff0c;在需要给不同的用户分配不同的模块权限的场景下&#xff0c;就需要进行权限设计&#xff0c;按需给用户划分对应的权限。比如一个企业系统有用户管理模块、财务管理模块、库存管理模…

Flume详解(3)

Host Interceptor 主机拦截器&#xff0c;本质上不是拦截数据&#xff0c;而是在数据的headers中添加一个host字段&#xff0c;可以用于标记数据来源(被收集)的主机。 Host Interceptor可以配置的选项有&#xff1a; 表-22 配置选项 选项 备注 解释 type required 拦截…

翻过DP这座大山

1.AcWing 跳台阶 第一种方法:暴力搜索DFS #include <iostream> using namespace std;int dfs(int n) {if(n 1) return 1;else if(n 2) return 2;else return dfs(n-1)dfs(n-2); }int main() {int x; cin>>x;cout<<dfs(x)<<endl;return 0; }显然如…

CSS设置移动端页面底部安全距离

env(safe-area-inset-bottom)是一个CSS属性值&#xff0c;用于设置底部安全距离。它表示使用环境变量来获取底部安全距离的值。当使用环境变量时&#xff0c;需要使用env()函数来引用具体的环境变量。例如&#xff1a; <style> .box{padding-bottom: env(safe-area-inse…

OSCP靶场--Crane

OSCP靶场–Crane 考点(CVE-2022-23940sudo service提权) 1.nmap扫描 ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.229.146 -sC -sV --min-rate 2500 Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-25 08:07 EDT Nmap scan report for 192.16…

MySQL索引18连问,谁能顶住

前言 过完这个节&#xff0c;就要进入金银季&#xff0c;准备了 18 道 MySQL 索引题&#xff0c;一定用得上。 作者&#xff1a;感谢每一个支持&#xff1a; github 1. 索引是什么 索引是一种数据结构&#xff0c;用来帮助提升查询和检索数据速度。可以理解为一本书的目录&…

2-Flume之Sink与Channel

Flume Sink HDFS Sink 将数据写到HDFS上。数据以文件形式落地到HDFS上&#xff0c;文件名默认是以FlumeData开头&#xff0c;可以通过hdfs.filePrefix来修改 HDFS Sink默认每隔30s会滚动一次生成一个文件&#xff0c;因此会导致在HDFS上生成大量的小文件&#xff0c;实际过程…

蔚来JAVA面试(收集)

先叠加&#xff0c;这个是自己找的答案不一定对&#xff0c;只是给我参考看看而已。 一、项目 这个没有&#xff0c;根据实际项目情况来。蔚来比较喜欢拷打项目&#xff0c;所以要对项目非常熟悉&#xff08;慌&#xff09; 二、JAVA基础 2.1 Java中的IO模型有用到过吗&#…
最新文章