14 程序地址空间

背景

kernel 2.6.32
32位平台

空间布局图

在这里插入图片描述

如何理解地址划分
地址划分,本质是调整地址空间的定义start和end,内存中定义了管理每个区域范围的结构体,叫mm_struct,每个进程都有一个这个结构体指针变量
在这里插入图片描述

验证上面划分的结构,写一段各个属性变量的代码来验证地址

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int g_unval;
int g_val = 100;int main(int argc, char *argv[], char *env[])
{// int a = 10;//字面常量const char *str = "helloworld";// 10;// 'a';//main函数地址printf("code addr: %p\n", main);printf("init global addr: %p\n", &g_val);printf("uninit global addr: %p\n", &g_unval);static int test = 10;char *heap_mem = (char*)malloc(10);char *heap_mem1 = (char*)malloc(10);char *heap_mem2 = (char*)malloc(10);char *heap_mem3 = (char*)malloc(10);printf("heap addr: %p\n", heap_mem); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem1); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem2); //heap_mem(0), &heap_mem(1)printf("heap addr: %p\n", heap_mem3); //heap_mem(0), &heap_mem(1)printf("test stack addr: %p\n", &test); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem1); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem2); //heap_mem(0), &heap_mem(1)printf("stack addr: %p\n", &heap_mem3); //heap_mem(0), &heap_mem(1)printf("read only string addr: %p\n", str);for(int i = 0 ;i < argc; i++){printf("argv[%d]: %p\n", i, argv[i]);}for(int i = 0; env[i]; i++){printf("env[%d]: %p\n", i, env[i]);}return 0;
}

在这里插入图片描述

首先是代码地址,在最小的位置。最往上是字符常量区,然后是数据区,static也在数据区,本质是将局部的变量开辟在全局区域。堆区后面是栈区,中间空了很大一部分的共享区。两个箭头表示,堆的变量是往上增长,地址越来越大,栈则是越来越小,栈上面有命令行参数,在之后是环境变量的地址,最后是内核空间1G

在32位系统下,一个进程的地址空间,取值范围是0x00000000 - 0xffffffff
用户空间【0,3GB】 ,内核空间【3,4GB】

地址空间是操作系统专门为进程设计的内核数据结构,包含了各个区域的划分,起始和结束,还有更多属性
所以创建一个进程,操作系统先创建PCB结构,再创建地址空间,用地址空间指针指向进程的地址空间对象,就可以找到地址空间和对应的页表

每一个进程都有一份地址空间

虚拟内存

#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{ //parentsleep(3);printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;
}
//与环境相关,观察现象即可
child[3046]: 100 : 0x80497e8
pa

上面的代码运行后,子进程修改了全局变量的值,结果同一个变量产生了两个不同的值,且他们的地址是一样的,这是为什么

  • 变量内容不一样,但地址一样,所以这个地址肯定不是真正的物理地址
    需要说明一下地址空间的设计
    由于直接访问物理内存是不安全的,如果进程都直接访问物理内存,那么很容易一个进程用指针修改另一个进程的值,不遵守进程的独立性。所以,引入了虚拟内存的概念,进程直接访问虚拟内存,虚拟内存就是上面的0x0-0xffffffff的内存布局,每个进程都划分了这么大的空间,实际上每个进程这些空间只是物理内存的一部分

在这里插入图片描述

那么一个虚拟内存地址如何转化为对应的物理内存地址,物理内存和虚拟内存中间有一个页表的结构,里面记录了地址的对应关系。地址空间和页表(用户级)每个进程都有一份,只要保证,每一个进程的页表,映射的物理内存的不同区域,这样进程之间就不会互相干扰,保证了独立性

分页和虚拟地址空间

在这里插入图片描述

这时就可以解释上面为什么同一个变量会有两个不同的值,fork产生子进程后,子进程的变量分页结构等很多都是基于父进程的拷贝,共享一段内存。当父子进程对某个值写入时,会为子进程单独拷贝这个变量的一个区域,映射和父进程不同的物理内存位置。所以虽然都是同一个变量的虚拟内存地址,但在物理内存处是两块不同的区域,这个过程就叫写时拷贝

fork为什么会有两个返回值
前面说过是因为return执行了两次,return的本质就是对id变量进行写入,发生了写时拷贝,父子进程在物理内存中都有属于自己的地址空间,只不过用同一个虚拟地址来标识了

虚拟内存的产生

在没有虚拟内存的时候,进程是直接加载到物理内存上,通过进程的起始和边界控制是不是自己的部分。这样可以通过指针访问到另一个进程的数据,如果一个进程有问题,越界也可能会导致另一个程序崩溃

所以引入了虚拟内存的概念,虚拟内存通过页表映射到物理内存中,虚拟内存作为中间者,如果转换后是非法的,要么转换后是非法的,要么越界,要么没有权限,就会禁止访问,解决了安全性的问题

当我们程序还没有运行,编译形成可执行程序的时候,就已经有了地址。地址空间不仅仅是OS内部遵守的,编译器也要遵守。即编译器编代码的时候就已经形成了各个代码区,数据区,并且,采用和linux内核中一样的编址方式,给每一个变量代码都编址,所以,程序编译的时候,每一个字段早已经有了一个虚拟地址,每一个变量,每一个函数,编译器给的都有地址
,同样也一定被加载到了物理内存

有两套地址,一套是程序内部使用的,一套是加载到内存分页的物理内存地址。用代码和函数的地址作为页表的左值,得到对应的物理地址右值

cpu读到的是物理地址还是虚拟地址?
在这里插入图片描述
cpu读到的也是虚拟地址,通过页表转换为物理地址操作

为什么要有地址空间

1.凡是非法的访问或者映射,操作系统都会识别到,并终止这个进程。这样,有效的保护了物理内存,地址空间和页表是OS创建的,凡是使用地址空间和页表进行映射,都在OS的监管之下,保护了所有的合法数据和进程有效数据

所有的进程崩溃,就是进程退出,系统杀掉了这个进程

2.因为有地址空间的存在,页表映射的存在,代码和数据就可以加载到任意位置。内存管理和进程管理完成了解耦合

new和malloc空间的时候,本质是在虚拟内存申请的,使用了延迟分配的策略,提高效率。
如果申请了物理空间如果不立马使用,造成了空间浪费。所以,申请一段空间的时候,物理内存可以一个字节都不给,只有在真正对这个空间访问的时候,才执行相关内存的管理算法,申请内存,构建映射关系,内存的访问由操作系统自动完成,用户和进程0感知。操作系统如何知道虚拟内存分配的空间需要被访问而未被加载物理内存,用了缺页中断技术。这样就不会存在申请了但不用的空间

3.物理内存理论上可以任意位置加载,会造成数据和代码乱序。但是,因为有页表的存在,可以将内存分布有序化。进程的代码和数据如果已经运行完了可以去除,映射关系也删掉,但不影响后面的代码运行。不同的进程映射到不同的物理内存,很容易做到,独立性的实现

因为有地址空间的存在,每一个进程都认为自己拥有4GB空间(32),并且各个区域有序的,进而可以通过页表映射到不同的区域,来实现进程的独立性。每个进程不需要知道其他进程的存在

重新理解挂起

加载程序是创建进程,并不需要立马把所有程序的代码和数据加载到内存中,创建映射关系。极端情况下,只有内核数据结构被加载到内存中,当真正执行这段代码和数据的时候,才创建物理内存,这样就可以实现分批加载和分批换出。比如100G的游戏怎么运行,只加载需要运行的进程,分批换出。

挂起的时候,页表不仅仅只能映射物理内存,还可以映射磁盘,只需要填入映射到磁盘中代码数据的位置,将物理内存中的位置释放。就可以把程序挂起,不用交换到磁盘上

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十四经脉最全总结(什么是经脉、十二经脉动态图、经脉走向、人体和手足哪面为阳,哪面为阴?)

目录 一.什么是经脉二.人体和手足哪面为阳&#xff1f;哪面为阴&#xff1f;三.任督二脉3.1 任脉3.2 督脉 四.十二经脉4.1 什么是 厥阴&#xff0c;少阴&#xff0c;太阴&#xff1b;少阳&#xff0c;阳明&#xff0c;太阳&#xff1f;4.2 十二经脉总分布4.3 手三阴经1.手厥阴心…

4、pod运维replicationCtroller、replicaSet、DeamonSet、Job、Cronjob

1、kubenetes 会自动重新运行失败的pod应用 pod运行失败&#xff0c;会自动重启&#xff0c;但是节点失败&#xff0c;pod会被移除&#xff0c; 除非配置了relicationController来管理资源 2、保持pod的健康存活 配置探针&#xff0c;发送http请求 3、查看前一个pod的运行日…

mysql-视图,创建表,存储过程,循环,判断实操命令

数据库操作命令在IDEA工具database的console命令 数据库表结构与视图 -- 查询隔离级别 select transaction_isolation;-- 设置隔离级别 set session transaction isolation level read committed ; set session transaction isolation level REPEATABLE READ ;start transacti…

蚂蚁感冒c++

题目 思路 “两蚂蚁碰面会掉头&#xff0c;若其中一只蚂蚁感冒了&#xff0c;会把感冒传染给碰到的蚂蚁”&#xff0c;这句话看作是“两蚂蚁碰面会互相穿过&#xff0c;只是把感冒的状态传给了另一只蚂蚁”&#xff0c;因为哪只蚂蚁感冒了并不是题目的重点&#xff0c;重点是有…

如何在Word里一次性给全部汉字加拼音?

word是大家日常使用频率较高的工作软件&#xff0c;功能性很强&#xff0c;有上乘的文档格式设置工具&#xff0c;利用它可更轻松、高效地组织和编写文档&#xff0c;熟练运用word&#xff0c;在职场上很重要。那么word如何添加拼音呢?下面给大家介绍一下吧。 方法一&#xf…

线性dp P4310-绝世好题/P4933 大师【日记】

1.绝世好题&#xff08;P4310&#xff09; 绝世好题https://www.luogu.com.cn/problem/P4310 比较考验思维的一道题目&#xff0c;码量和理解难度都不大&#xff0c;重在思维。 一开始看错题&#xff0c;以为是求子串&#xff08;还在想为啥考的纯位运算枚举&#xff0c;whe…

vue iis 配置

下载安装两个IIS模块 1). 传送门&#xff1a;URL Rewrite 2). 传送门&#xff1a;Application Request Routing 注 : 只有在 服务器的主页 有Application Request Routing 部署VUE网站 生成网站 在VUE项目打包生成出发布文件,即文件夹 dist,此处忽略 复制到你需要存放网站的…

Skywalking官方的实战模拟项目Live-Demo

Skywalking 官方的实战模拟项目Live-Demo Live-Demo 是 Skywalking 官方的实战模拟项目&#xff0c;其中包含4个子模块项目 projectA访问projectB、projectC两个SpringBoot项目 projectB访问本地的H2数据库 projectC访问www.baidu.com并同时向一台Kafka消息队列写入数据 proje…

入门指南:使用uni-app构建跨平台应用

入门指南&#xff1a;使用uni-app构建跨平台应用 &#x1f31f; 前言 欢迎来到我的小天地&#xff0c;这里是我记录技术点滴、分享学习心得的地方。&#x1f4da; &#x1f6e0;️ 技能清单 编程语言&#xff1a;Java、C、C、Python、Go前端技术&#xff1a;Jquery、Vue.js、R…

六、软考-系统架构设计师笔记-软件工程基础知识

1、软件工程 软件工程是将系统化的、严格约束的、可量化的方法应用于软件的开发、运行和维护&#xff0c;即将工程化应用于软件并对上述方法的研究。 软件要经历从需求分析、软件设计、软件开发、运行维护&#xff0c;直至被淘汰这样的全过程&#xff0c;这个过程称为软件的生…

Android使用OpenGL和FreeType绘制文字

Open GL主要是渲染图形的&#xff0c;有时候需要绘制文字&#xff0c;网上搜了一下&#xff0c;基本思路都是把文字转成位图&#xff0c;再使用Open GL纹理进行渲染。加载纹理在特定阶段才能成功&#xff08;在onSurfaceCreated中加载&#xff09;&#xff0c;这样就无法动态的…

部署LVS负载均衡架构

目录 一、ipvsadm 工具 二、NAT模式下部署LVS负载均衡 1、部署NFS共享存储服务器 1.1 安装NFS软件 1.2 新建共享目录和站点文件 1.3 设置共享策略 2、部署节点服务器1 2.1 安装并启动nginx软件 2.2 挂载共享目录到网页站点目录 2.3 修改网关 3、部署节点服务器2 3.…

在ABAP中创建一个简单的守护进程

原文地址&#xff1a;Create a simple Daemon in ABAP 目录 一、ABAP语言中的守护进程是什么&#xff1f;二、ABAP 守护进程框架 (ADF)三、ABAP 守护进程类四、创建一个简单的ABAP守护进程步骤1&#xff1a;创建一个新的ABAP Daemon类步骤2&#xff1a;实现ON_ACCEPT方法第三步…

「滚雪球学Java」:GUI编程(章节汇总)

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

Kosmos-1: 通用接口架构下的多模态大语言模型

Kosmos-1: 通用接口架构下的多模态大语言模型 FesianXu 20230513 at Baidu Search Team 前言 在大规模语言模型&#xff08;Large Language Model, LLM&#xff09;看似要带来新一番人工智能变革浪潮之际&#xff0c;越来越多尝试以LLM作为通用接口去融入各种任务的工作&#…

【vue】ant-design弹出框无法关闭和runtimecore提示isFucntion is not function的问题修复

【vue】ant-design弹出框无法关闭和runtimecore提示isFucntion is not function的问题修复&#xff0c;初步分析是vue发布3.4版本以后引起的兼容性问题。 问题截图&#xff1a; 1.isFucntion is not function&#xff0c;是由于vue升级后众多插件版本不匹配造成的问题 2.弹框…

计算机中msvcp140.dll,丢失怎么修复与解决

一、msvcp140.dll20个软件环境 msvcp140.dll文件是许多软件运行环境的组成部分&#xff0c;通常与Microsoft Visual C Redistributable关联。以下是可能使用该文件的软件环境&#xff1a; 微软办公软件&#xff1a;如Microsoft Office套件&#xff0c;包括Word、Excel、Power…

Python给图片加水印

受到“手动给证件加文字太麻烦”的感触&#xff0c;想用Python来实现给图片加水印&#xff0c;这不方便多了。 这里使用PIL模块&#xff1a; from PIL import Image from PIL import ImageFont from PIL import ImageDrawimg_t Image.open(cat.jpg) img_size_t img_t.size…

OJ:循环队列

622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09; 思路 思路&#xff1a;首先循环队列的意思是&#xff1a;空间固定&#xff0c;就是提前开辟好&#xff0c;满了就不能插入了&#xff0c;但是删除数据后仍有空间&#xff0c;删除循环队列里面的数据后&#xff0c;保…

Apache ECharts数据可视化技术

介绍 官方地址:Apache ECharts 快速入门案例echarts.init //初始化方法 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>ECharts</title><!-- 引入刚刚下载的 ECharts 文件 --><script src"echart…