Linux->进程地址空间
目录
前言:
1. 程序地址空间回顾
2. 进程空间是什么
3. 进程地址空间与内存
4. 进程地址空间和内存的关联
5. 为什么要有进程地址空间
前言:
我们在平时学习的过程当中总是听到栈、堆、代码段等等储存空间,但是这些东西到底是什么,我们又怎么理解呢?本篇就为大家解惑。
1. 程序地址空间回顾
相信大伙们对于这张图片还是很熟悉吧,特别是对于堆和栈这一片区域,堆向上延伸,栈向下扩展,基本上是共同使用这一片空间。这片空间这样使用主要是服务于我们的动态库。
如果大伙想要验证这一张图片的正确性,可以自己验证一下,低地址代码地址长度更小,高地址更大。代码如下:
#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
#include<stdlib.h>int global_uval; //全局变量未初始化
int global_val = 1; //全局变量初始化int main(int argc, char* argv[],char* envp[])
{const char* point = "hello world"; //栈内指针和常量区字符串char* heap = (char*)malloc(100); //栈内指针和堆区空间printf("代码区起始地址: %p\n", main);printf("常量区字符串: %p\n", point);printf("全局变量初始化: %p\n", &global_val);printf("全局变量未初始化: %p\n", &global_uval);printf("堆区地址: %p\n", heap);printf("栈区地址: %p\n", &point);printf("命令行参数: %p\n", argv[0]);printf("环境变量: %p\n", envp[0]);return 0;
}
Linux下可以看到地址空间的变化逐渐增加,也正好对应了我们的地址空间的图。
但是同样的代码在vs下却运行出了不同的结果,如下:
仔细看vs下运行堆区地址和栈区地址大小与Linux下运行相反,具体原因我也不是很清楚,博主在这里也疑惑了半天,估计是vs悄悄的改了一些实现方法。
2. 进程空间是什么
看了上面的解释也没有理解,啥是进程地址空间?博主知道你很急,但是你先别急,什么?非要看?好吧,请看下方结论:
进程地址空间不是真实物理地址而是虚拟地址
知道了吗?没看懂是不?这就对了,还是等我徐徐道来。
请先看下方代码:
利用fork创建了一个子进程,count用于计数,当循环走过两次时,a被修改为100
1 #include<stdio.h>2 #include<unistd.h>3 #include<sys/types.h>4 5 int main()6 {7 int a = 100;8 int count = 0;9 pid_t res = fork(); //创建一个子进程 10 11 while(1)12 {13 if(res == 0)14 {15 printf("我是一个子进程,a = %d, &a = %p\n",a,&a);16 sleep(1);17 }18 else{19 if(count == 2)20 a = 200;21 printf("我是一个父进程,a = %d, &a = %p\n",a,&a);22 count++;23 sleep(1);24 25 }26 }27 28 29 return 0;30 }
运行结果如下:
上图中大家有发现什么不对的地方吗?首先,变量a定义在两个进程分流之前,那么父子进程应该拿到的是同一块数据,通过查看地址,再对应值,确实如此,但继续往下看,当计数两次之后,我们将父进程的a修改了,父进程的a也确实被更改了值,但是,父子进程拿到了同一块数据,为什么子进程的a没有被改变呢?更离谱的是它们两个的地址竟然一模一样,这合理吗?这很不合理。
根据我们学习语言所知,同一块空间只能只能存一个数据,这里竟然存了两个数据,那么根据理论而言,我们能够分析出,我们获取到的地址不可能是真实的物理地址,而是虚拟地址。
但是根据我们学习的计算机体系结构可以知道,任何一个磁盘上的内容想要跑到CPU中被运行,必须经过被载入内存这一过程,但是上面又显示我们拿到的不是真实的物理地址,就不可能是再内存当中的,难道是我们的冯诺依曼体系在这里出错了?很明显不可能,所以只有一种可能,那就是我们在程序中获取到的地址与我们的内存之间有联系。
3. 进程地址空间与内存
这一部分有些抽象,所以我先为大家讲一个故事吧。(这个故事是博主去别人哪里copy的,博主认为十分生动形象)。
咱们在学校总是经历过划分三八线这种事情吧。就是给桌子划分区域,假设有小明和小妞两个人,他们有自己的区域,不能越过区域,到别人的领域去。如下:
我们可以想一下,他们是怎么区分他们自己的区域的呢?不就是记录自己区域的起始结束位置嘛,也就是说小妞想要表示自己的位置,那就是如下:
struct xiaoniu_area{
size_t begin = 0;
size_t end = 50;
};
小明也是同理,也就是说他们自己知道这一片空间的所属就行,不过呢,小明和小妞关系很好,可以亲亲抱抱的那种,所以这一条三八线也就是当作一个提醒罢了。所以呢,小妞有一天要画画,她需要更大的位置操作,那么她就向小明说,我想要多一点点位置,之后还你,小明说:“反正现在我也不用,你用吧”。这样小妞的位置就变为了如下:
struct xiaoniu_area{
size_t begin = 0;
size_t end = 70;
};
那么,相对的小明的位置就被缩减了,图如下:
看到上面的图,大伙有没有将我们的地址分配图联系起来呢?我们的进程地址也就是通过这个方式表示的,就像是我们的栈、堆等等,它的位置都是能够被调整的。
在Linux当中有一个struct mm_struct这样的结构体,用于表示各个区所对应的位置。如下:
struct mm_struct
{//代码区
unsigned long code_start;
unsigned long code_end;//堆区
unsigned long heap_start;
unsigned long heap_end;
//栈区
unsigned long stack_start;
unsigned long stack_end;
}
也就相当于整个进程地址空间的使用被这样一个结构体划分的明明白白。
但是这又跟我们的内存有什么联系呢?别着急,接着看。
我先给出结论:
每个进程都可以独占整个内存空间
怎么理解上面这句话呢?咋一看是不是感觉很奇怪?仔细一看更奇怪了是不?
我还是举一个例子。
有一个10亿家产的富豪,他有2个私生子,这两个私生子不知道彼此的存在,此时呢,富豪对两个孩子分别说了,只要我寄了,这10亿就是你的了,所以呢,这两个还是都认为这10亿自己稳得了。这时,大儿子说,老爸,我想要20万买个车上班,富豪说,行,好好加油,之后小儿子又说,老爸,给我5个亿,我要单开一家公司,做大做强,富豪转身就抽出了七匹狼,你要皮带不要?小儿子就跑了。但是对于大儿子和小儿子来说,他们认为这10还是自己得吗?肯定是啊。
这里的富豪就是操作系统,10亿就是内存,而两个儿子就是进程,他们可以向操作系统申请空间,操作系统也可以拒绝他们的请求,但是他们能得到的空间是内存大小的空间。这也就表明了,每一个进程都认为自己是可以独占内存的,但是操作系统是能够自己判断是否给你这么多。
4. 进程地址空间和内存的关联
进程地址空间通过页表和内存关联起来。
什么是页表?
页表就是:进程将自己的代码和数据首先放在虚拟地址空间的对应的区域,在这其中会有一种表结构,叫做页表,页表的核心工作就是完成虚拟地址到物理地址之间的映射,最终我们的可执行程序的代码和数据可以加载到物理内存的任意位置,因为最终只需要建立代码和数据与物理内存之间的映射关系,就可以通过虚拟地址找到物理内存的对应地址。
咱们可以想到每一个进程都是独立的,父子进程也是不例外的,那么每一个进程也都有属于知道的页表去对应真实的内存。看到这里,想必大家也能回到之前父子进程不同值,地址相同得问题了吧。那就是父子进程得虚拟地址相同,但是这个虚拟地址存在于两张页表当中,这两个页表通过相同的虚拟地址却映射了两个内存空间。
那么,这两个映射不会映射到一个空间吗?
不会,还记得我之前讲的mm_struct结构体,内存将这些空间用结构体划分了起来,并且划分之后还会标识这片区域已经被某个进程使用了,那么页表就知道了这篇空间不能使用了,它就会换一片区域去映射。如下:
看到上面的图,我们可以认为父子进程是完全独立的吗?
其实不能,因为我们的操作系统是十分会节省空间的,也就是当父进程或子进程的数据没有被改变的时候,两个页表指向的真实空间也是相同的,只有在数据发生改变的时候操作系统会为这一个空间开辟一个新的空间,然后对应给页表,但是页表的虚拟内存没有改变。这一过程被称为写时拷贝。
5. 为什么要有进程地址空间
我们使用malloc时,操作系统在我们申请内存的时候并没有直接的给我们那一片地址,但是这一片空间并不能被其他的进程使用,该片地址会处于一个闲置状态。这篇地址不能使用是这一个进程不能使用,而是其它进程可以使用,但是操作系统会保证数据不会冲突。
也就是说,我们能够获取到虚拟地址,但是我们没有在意到底有没有实际的物理地址在哪里,反正我们要使用的时候,操作系统能给我们就是了。所以这样做就让我们的进程管理和内存管理之间解耦了。
还有就是当操作系统要加载一个很大的程序,比如32个G,这样一个程序一定是不可能全部运行起来的,所以操作系统会慢慢的加载,没有被使用的部分就被先睡眠起来了。我们唯一的感受也就是程序变慢了。
并且,通过进程地址空间,我们操作系统能够清楚地知道你写的进程是否正确,有没有越界?如果越界了,那么操作系统就会让你的进程崩溃,因为你的指针或者其它数据指向了其它进程的区域,保证了进程之间的独立性和安全性。
以上就是我对进程地址空间的全部理解咯,有问题请帮忙指出啦,博主也是努力进步当中,哈哈。
相关文章:
Linux->进程地址空间
目录 前言: 1. 程序地址空间回顾 2. 进程空间是什么 3. 进程地址空间与内存 4. 进程地址空间和内存的关联 5. 为什么要有进程地址空间 前言: 我们在平时学习的过程当中总是听到栈、堆、代码段等等储存空间,但是这些东西到底是什么&…...
【奶奶看了也不会】AI绘画 Mac安装stable-diffusion-webui绘制AI妹子保姆级教程
1.作品图 2.准备工作 目前网上能搜到的stable-diffusion-webui的安装教程都是Window和Mac M1芯片的,而对于因特尔芯片的文章少之又少,这就导致我们还在用老Intel 芯片的Mac本,看着别人生成美女图片只能眼馋。所以小卷这周末折腾了一天&#…...
基于stm32电梯管理系统设计
基于stm32电梯管理系统设计这里记录一下以前自己做的嵌入式课程设计,报告中的图片和文字太多了,全部一个一个把搬过来太麻烦了,需要完整文本和代码自行q我963160156,也可在微信公众号 *高级嵌入式软件* 里回复 *电梯* 查看完整版文章摘要关键…...
Spring中的FactoryBean 和 BeanFactory、BeanPostProcessor 和BeanFactoryPostProcessor解析
文章目录FactoryBean 和 BeanFactory后置处理器BeanPostProcessor 和 BeanFactoryPostProcessorBeanPostProcessorBeanFactoryPostProcessorFactoryBean 和 BeanFactory BeanFactory接⼝是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的…...
【C++从入门到放弃】类和对象(上)
🧑💻作者: 情话0.0 📝专栏:《C从入门到放弃》 👦个人简介:一名双非编程菜鸟,在这里分享自己的编程学习笔记,欢迎大家的指正与点赞,谢谢! 类和对…...
什么牌子的蓝牙耳机便宜好用?四款高品质蓝牙耳机推荐
随着时代的发展,蓝牙耳机的使用频率越来越高,不少人外出时除了带手机外,蓝牙耳机也成为了外出必备的数码产品之一。现在的蓝牙耳机品牌众多,什么牌子的蓝牙耳机便宜好用?下面,我来给大家推荐四款高品质的蓝…...
eddsa 算法
信息安全课程设计:eddsa 算法 一、项目要求 使用 C 语言开发;可以实现公私钥生成、签名、认证;只需要手动输入明文,代码会自动生成公私钥、签名、认证;记录公私钥生成、签名、认证的时间;在 VS 上运行&am…...
Xcode Developer Document 开发者文档
总目录 iOS开发笔记目录 从一无所知到入门 文章目录IntroDeveloper Documentation 打开方式菜单栏点击 | 快捷键方式另一种打开方式Intro 2016年我在学校学Java的时候,要查某个Java类/方法的用法还得自己手动下载一种.chm格式的开发文档文件,…...
IntelliJ插件开发教程之新建项目
JetBrains公司系列产品IDEA、WebStrom、PyCharm、CLion、GoLand等都是基于IntelliJ Platform开发而成,掌握IntelliJ插件开发技能便能拥有提升开发效率的终极武器。本教程Demo源码请微信公众号“开发效率”进行获取。阅读原文如果您是JetBrains产品的用户,…...
解决SpringBoot中@RequestBody不能和Multipart同时传递的问题
问题描述 今天在做文件上传的时候,遇到了这么一个错误日志: Resolved[org.springframework.web.HttpMediaTypeNotSupportedException: Content type ‘multipart/form-data;boundary--------------------------771899451541318130280588;charsetUTF-8’…...
【华为OD机试模拟题】用 C++ 实现 - 统计匹配的二元组个数(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明统计匹配的二元组个数题目输入输出描述示例一输入输出说明示例二输入输出说明备注Code使用说明 参加华为od机试,一定要注意不要完全背诵代码&...
Vuex 面试题总结 的历史汇总!
一.vuex是什么?怎么使用?哪种功能场景使用它? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。简单来说就是:应用…...
Redis缓存更新策略与缓存穿透、雪崩等问题的解决
文章目录一、缓存更新策略1、三种策略2、策略选择3、主动更新的方案二、缓存存在的问题1、缓存穿透2、缓存雪崩3、缓存击穿三、解决缓存问题1、自定义分布式锁2、解决缓存穿透问题3、解决缓存击穿问题一、缓存更新策略 1、三种策略 内存淘汰:redis自带的内存淘汰机…...
OSI和TCP/IP网络模型细讲
文章目录一、OSI七层参考模型二、TCP/IP体系结构三、TCP/IP参考模型四、沙漏计时器形状的TCP/IP协议族五、两种国际标准对比相似之处不同之处一、OSI七层参考模型 OSI参考模型共分为7层,低三层面向通信,可用软硬件实现;高三层面向信息处理&am…...
【正点原子FPGA连载】第十九章FreeRtos Hello World实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第十九章FreeRto…...
php mysql高校田径运动会成绩管理系统
第一章 引言 1 1.1 选题背景 1 1.2 编写目的 2 1.3 目标 2 1.4 功能需求 3 第二章 开发工具介绍 4 2.1 PHP 4 2.2 APACHE 5 2.3 MYSQL数据库 5 2.4 运行环境 WINDOWS XP 6 2.5 XAMPP 6 2.6 DREAMWEAVE8 6 2.7 EDITPLUS 7 第三章 需求…...
scrum敏捷项目管理软件三款
Leangoo领歌Leangoo是国产的一个项目管理软件,www.leangoo.com , 专门的Scrum敏捷开发工具,看板的管理方式,高度可视化。它支持敏捷开发全流程。从产品路线图-需求-迭代-缺陷-测试-上线。燃尽图,工作量,迭代…...
【项目设计】高并发内存池(二)[高并发内存池整体框架设计|threadcache]
🎇C学习历程:入门 博客主页:一起去看日落吗持续分享博主的C学习历程博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记&…...
西电编译原理期末核心考点汇总(期末真题+相关知识点)
文章目录前言一、正规式1.1 相关知识点1.1.1 正规式定义1.1.2 辅助定义1.2 历年真题二、二义文法2.1 相关知识点2.1.1 二义性概念2.2 历年考题三、全部短语、直接短语和句柄3.1 相关知识点3.1.1 短语,直接短语和句柄定义3.1.2 短语,直接短语和句柄例题3.…...
追梦之旅【数据结构篇】——详解C语言实现二叉树
详解C语言实现二叉树~😎前言🙌什么是二叉树?二叉树的性质总结:整体实现内容分析💞1.头文件的编写:🙌2.功能文件的编写:🙌1)前序遍历的数值来创建树——递归函…...
独家 | Gen-1——可以改变视频风格的AI模型
翻译:吴振东校对:张睿毅本文约1000字,建议阅读3分钟 本文简单介绍了Runway公司的发展史,以及他们新推出的生成式AI模型Gen-1,可用于通过应用文本提示或者参考图像所指定的任意风格,将现有视频转换为新视频。…...
戴尔dell inspiron-5598电脑 Hackintosh 黑苹果efi引导文件
原文来源于黑果魏叔官网,转载需注明出处。硬件型号驱动情况主板X99 K9 v2 Machinist处理器i5-10210U / *i7-10510U已驱动内存20GB已驱动硬盘1000GB SAMSUNG 860 QVO SATA已驱动显卡Intel UHD 620已驱动声卡Realtek ALC3204/236已驱动网卡RTL8168H Gigabit Ethernet已…...
3.2 网站图的爬取路径
深度优先与广度优先方法都是遍历树的一种方法,但是网站的各个网页 之间的关系未必是树的结构,它们可能组成一个复杂的图形结构,即有回路。如果在前面的网站中每个网页都加一条Home的语句,让每个网页都能回到主界面,那么…...
《SQL基础》12. SQL优化
SQL优化SQL优化数据插入insert优化大批量插入数据主键优化order by优化group by优化limit优化count优化count用法update优化SQL优化 数据插入 insert优化 如果我们需要一次性往数据库表中插入多条记录,可以从以下三个方面进行优化。 批量插入手动控制事务主键顺…...
fork之后是子进程先执行还是父进程先执行
CFS(完全公平调度器)是Linux内核2.6.23版本开始采用的进程调度器,它的基本原理是这样的:设定一个调度周期(sched_latency_ns),目标是让每个进程在这个周期内至少有机会运行一次,换一种说法就是每个进程等待CPU的时间最长不超过这个…...
2023年java初级面试题(5道)
一、两个对象值相同(x.equals(y) true),但却可有不同的hash code,这句话对不对?答:不对,如果两个对象x和y满足x.equals(y) true,它们的哈希码(hash code)应当相同。Java对于eqauls…...
【内网安全】——Linux权限维持
作者名:白昼安全主页面链接: 主页传送门创作初心: 以后赚大钱座右铭: 不要让时代的悲哀成为你的悲哀专研方向: web安全,后渗透技术每日鸡汤: 钱至少对于现在的我来说,的确是万能的在…...
Linux 真实使用内存计算
获取Linux内存信息,可通过cat /proc/meminfo查看,比如,Ubuntu 20.04.5 LTS上会显示以下信息: leoyaDESKTOP-LMR:~$ cat /proc/meminfo MemTotal: 16017572 kB MemFree: 15637472 kB MemAvailable: 15533140 kB Bu…...
Unity Jobsystem ECS
简介随着ECS的加入,Unity基本上改变了软件开发方面的大部分方法。ECS的加入预示着OOP方法的结束。随着实体组件系统ECS的到来,我们在Unity开发中曾使用的大量实践方法都必须进行改变以适应ECS,也许不少人需要些时间适应ECS的使用,…...
Java中创建线程有哪几种方式
1.继承Thread类 总结:通过继承 Thread 类,重写 run() 方法,而不是 start() 方法 Thread 类底层实现 Runnable 接口类只能单继承 接口可以多继承2.实现Runnable接口 总结:通过实现 Runnable 接口,实现 run() 方法,依然…...
网站改版iis301跳转如何做/淘宝推广怎么做
因为公司需要分类链接所以再输出的时候就查了很多的函数。。用来用去这个最方便直接: 做笔记 strstr是C一个函数。 会返回一个指针位置,如果这个指针为空就是不存在这个标志符,指针不为空就证明存在,那就输出整段 Http_GW it…...
做塑胶原料用什么网站好/网站用户体验优化
AWS - 弹性计算云( Elastic Compute Cloud)Amazon EC2 (Elastic Compute Cloud)是一种Web服务接口,可在AWS云中提供可调整大小的计算容量。 它专为开发人员设计,可以完全控制Web扩展和计算资源。可以调整EC2实例的大小,并根据我们的要求按比例…...
住房和成乡建设部网站/推广联盟平台
Scala的变量分为两种:val和var。val跟java的final变量类似,一旦初始化就不能被重新赋值。而var则不同,类似于Java的非final变量,在整个生命周期内var可以被重新赋值。 val msg"Hello";var msg:String "hello"…...
dw做六个页面的网站/今日头条新闻大事
转自:http://blog.csdn.net/totogo2010/article/details/9100767 准备2个文件: 文件一,ThisService.app文件二,Doxygen.rb下载上述2个文件:ThisService&Doxygen_rb准备好之后,两步配置,一步…...
企业型网站有哪些特点/seo培训赚钱
一、缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而…...
贵阳做网站哪家公司好/2022年最火的关键词
很简单的fibonacci数列性能测试. C用int,C#用int, Parallet用double, int Fib(int a){ if(a<2) return a; return Fib(a-1)Fib(a-2);} Fib(40) 所需时间分别为C : 1500 C# : 1400 Parallet : 1750 C#竟然比C快? 这个结果让我对在C上实现Parallet的欲望减…...