【Linux:程序地址空间--原来操作系统也喜欢画大饼】
目录
1 代码感受
2 进程地址空间
3 扩展
1 代码感受
在正式讲程序地址空间前我们先来看一段简单的代码来分析分析:
1 #include<iostream>2 #include<unistd.h>3 using namespace std;4 5 int g_val=100;6 7 int main()8 {9 pid_t id=fork();10 if(id==0)11 {12 //child13 while(true)14 {15 cout<<"我是一个子进程,我的pid是:"<<getpid()<<",我的ppid是:"<<getppid()<<",g_val:"<<g_val<<";&g_val:"<<&g_val<<endl;16 g_val=200;17 sleep(1);18 }19 }20 else21 {22 //parent 23 while(true)24 {25 cout<<"我是一个父进程,我的pid是:"<<getpid()<<",我的ppid是:"<<getppid()<<",g_val:"<<g_val<<";&g_val:"<<&g_val<<endl;26 sleep(1);27 }28 }29 return 0;30 }31
大家可以自己先分析一下结果。
我们来运行一下结果:
大家看前面几行可能就会立马发现问题:我们定义的g_val是全局变量,当子进程修改g_val的值时我们发现父进程的g_val是不受影响的,那么说明父子进程所用的g_val并不是同一个变量(这个很好理解,之前的我们说过父子进程是相互独立的,互相不受干扰的),但是问题出现在最后一列,我们惊奇的发现居然父子进程的g_val变量的地址居然是相同的,前面不是说父子进程的g_val不是同一个变量吗?这里为啥打印出来的地址会是相同的呢?
这里就说明我们打印出来的地址并不是真正的物理地址,我们语言层面打印出的地址叫做虚拟地址或者线性地址。我们在用C/C++语言所看到的地址,全部都是虚拟地址!而物理地址,用户一概看不到,由OS统一管理 。OS必须负责将虚拟地址转化成物理地址 。
2 进程地址空间
首先我们来讲一个故事:从前有一个企业家很有钱,他的家产大概有一亿美金左右的样子。他有4个私生子,并且这四个私生子互相并不知道对方的存在。第一个私生子是个学霸,在国内顶尖学校上学,这个富豪便对他说,你要好好读书,将来我这一亿美金全部都是你的;第二个私生子是一个三线演员,富豪便对他说,我帮你打开你红的渠道,你不要辜负了我对你的期望,好好努力,将来我这一亿美金都是你的;第3个私生子是个女儿,当的是小学老师,富豪便对他说,你也不用太过努力工作,我就你这一个女儿,等我老了这一亿美金就是你的了;第四个私生子是一个初中的小混混,富豪对他说,你只要好好听我的话,这一亿美金就是你的了。
富豪给每个私生子都做出了承诺要将一亿美金给他们,但是实际富豪并没有那么多的钱给每个私生子一亿美金,而这一亿美金就是富豪给私生子们画的一张大饼,但是它的私生子们却信以为真。
那这个故事与我们讲的知识有什么关系呢?其实操作系统就是那个富豪,私生子们就是一个一个的进程,而那一亿美金就是进程地址空间。
PS:我们在生活中要尽量少画饼。
操作系统给进程画了一张大饼,操作系统的资源是有限的,所以他就得要好好的把这张饼给管理起来,不让这些进程乱来,而如何管理呢?
那就要先描述,再组织,Linux中用的是一种叫做mm_struct的内核数据结构来管理的。
我们来用一张图带大家来看看程序地址空间:
这张图相信大家多多少少也不会陌生,在C语言的学习中我们也见到了很多次。
那么程序地址空间如何编码的呢?(32位的平台下虚拟地址空间大概是4GB)
ps:下面图每个小空格代表着一个字节。
所以从这里我们也不难看出为啥虚拟地址也叫做线性地址。那么我们究竟是如何管理虚拟地址空间的每个区的呢?
我们可以用下面这种方式来描述管理:
struct mm_struct
{
long code _start;
long code _end;
long init _start;
long init _end;
…………
long brk _start;
long brk _end;
long stack _start;
long stack _end;
}
而_start和_end限定的区域就是叫做虚拟地址(线性地址)
那么问题来了,既然上面我们讲了那么多虚拟地址,真正的物理地址又在哪里呢?
我们画一个图方便大家理解:
通过这张图大家并不难发现,我们在语言层面上的地址是地址空间的虚拟地址,而虚拟地址要与物理地址建立映射,就需要一张页表(页表的工作原理我们将放到后面来讲)。
我们在学习C语言时大家在书上看到这样的一句代码:const char* str="hello world";
这时书上会告诉大家这句str指向的内容是只读的,不可修改的,但是这时为什么呢?这时我们就可以自己来分析分析:str指向的内容是在常量字符区,当常量字符区通过页表与物理地址建立映射时在页表中就将该数据设置为只读,当我们后续有修改操作时就会直接报错。
有了上面的基础我们就可以来解释解释为啥开头我们的g_val是同一个地址,但是指向的内容却不相同的问题了:
当不修改数据时就不会发生写时拷贝,父子进程指向的是同一块物理空间(为了节约资源);当要修改数据时就会发生写时拷贝,父子进程指向的是不同的物理空间,但是虚拟地址空间是相等的。
我们再来回答为啥fork会有两个返回值的问题就很容易了,就是因为父子进程的返回值是不同的,所以肯定会发生写时拷贝将不同的返回值用相同的虚拟地址来进行返回,虽然虚拟地址是相同的,但是他们通过页表建立映射的关系却是不一样的。
到目前为止,程序地址空间的基本内容已经ok,接下来给出一些扩展。
3 扩展
首先引出一个问题:假如没有程序地址空间,OS是如何工作的?
我们知道如果没有了地址空间,那么cpu将直接跟物理地址打交道,这样做的后果是什么?
我们不难知道假如cpu直接跟物理地址打交道的话那么当我们从cpu中读到非法地址时那就坏了,通过非法地址将我们程序中其他变量的值给修改了那不就扯淡了吗。所以我们要通过一层屏障来保护数据,而这一层保护就是通过程序地址空间来进行的,当我们访问的数据非法时通过页表的映射就会拒绝你的非法操作。
所以我们得出了程序地址空间的第一个好处:防止地址随意访问,保护物理内存和其他进程。
在向大家提出一个小问题:当我们在堆上new空间时OS是立马就把空间给你,还是等你需要的时候再给你?
这个问题大家应该都能够答对,与我们想得一样,OS会在我们需要该空间的时候再去在堆上申请。
而页表暂时没有与物理内存建立映射关系称作页表中断,当我们需要空间的时候再与·物理内存建立映射。大家从这张图看出来没有,当我们通过页表建立映射时将进程管理与内存管理给解耦合了。我进程管理不需要关心你是怎样在内存上申请空间的,内存管理也不需要关心进程是如何管理起来的,这样下来维护成本就会变得更低,维护效率会更加高效一些。
所以我们得出了程序地址空间的第二个好处:将进程管理与内存管理进行解耦合。
再提出一个问题:程序在被编译的时候没有被加载到内存,那么程序内有没有地址呢?
答案是有的。源代码再被编译的时候就是按照虚拟地址空间的方式将对应的代码和数据进行编制,编译器也会遵守虚拟地址的规则。
当我们把程序加载到内存,程序里保存的地址(虚拟地址,并不是程序本身在内存中的物理地址)就会被cpu读取,cpu通过虚拟地址找到对应的虚拟地址空间,然后虚拟地址空间又通过页表映射到物理内存中,这样就将程序的整个运转给联系起来了。
所以我们得出了程序地址空间的第三个好处:可以让进程以统一的视角看待自己的代码和数据。
相关文章:
【Linux:程序地址空间--原来操作系统也喜欢画大饼】
目录 1 代码感受 2 进程地址空间 3 扩展 1 代码感受 在正式讲程序地址空间前我们先来看一段简单的代码来分析分析: 1 #include<iostream>2 #include<unistd.h>3 using namespace std;4 5 int g_val100;6 7 int main()8 {9 pid_t idfork();10 if(i…...
Python实现简单信号滤波实战
在有些项目中需要对信号进行滤波处理,尤其是在医疗的设备中如心跳、脉搏等设备的采样后进行处理。滤波的目的就是除去某些频率的信号如噪声。常见的包括有低通滤波、高通滤波、带通滤波。 低通滤波指的是去除高于某一阈值频率的信号;高通滤波去除低于某…...
Java(110):非对称加密RSA的使用(KeyPair生成密钥)
Java(110):非对称加密RSA的使用(KeyPair生成密钥) RSA 算法是一种非对称加解密算法。服务方生成一对 RSA 密钥,即公钥 私钥,将公钥提供给调用方,调用方使用公钥对数据进行加密后,服务方根据私钥进行解密。 1、RSA生…...
(Mybatis 学习【1】)整合 Mybatis 开发流程
Mybatis 整合流程 ① 添加MyBatis的依赖 ② 创建数据库表 ③ 编写pojo实体类 ④ 编写映射文件UserMapper.xml ⑤ 编写核心文件mybatis-config.xml ⑥ 编写测试类** 编写 pojo 实体类 (设计相应的数据库) Data AllArgsConstructor NoArgsConstructor public class…...
一文搞懂Kerberos
Kerberos一词来源于古希腊神话中的Cerberus——守护地狱之门的三头犬,Kerberos是为TCP/IP 网络设计的可信第三方鉴别协议,最初是在麻省理工学院(MIT)为Athena 项目而开发的。Kerberos服务起着可信仲裁者的作用,可提供安全的网络鉴别ÿ…...
Go爬虫学习笔记(三)
day3 04|敏捷之道:大型Go项目的开发流程是怎样的? 瀑布模式 流程: 市场调研需求分析产品设计研发实现集成与测试项目交付与维护 适用场景: 需求在规划和设计阶段就已经确定了,而且在项目开发周期内&…...
CASTEP参数设置(2)
虚拟试验(分子模拟) 在表征材料以及材料的相关性质时,只要是采用已有的理论加以解释 但是通常来说,需要采用已有的理论来进行设计和探索,伴随着工业软件的发展,应当选用仿真技术来缩小探索范围 传统试验V…...
浅谈对Promise的理解以及在工作中的应用
浅谈对Promise的理解以及在工作中的应用Promise的概念背景知识JavaScript的同步和异步JavaScript事件循环回调函数进行异步操作解决方案:PromisePromise 在工作中的运用创建PromisePromise封装AJAXPromise链式操作Promise.all()Promise.race()async和await总结Promi…...
开源|快速入门和理解并模拟实现GPS户外机器人的定位与导航
户外机器人的定位导航相对于需要建图的场景来说,是比较简单容易实现的,因为可以借助第三方地图完成定位,并在第三方地图中完成路径规划和下发航点等操作,实现的难题在于如何控制机器人完成步行和转弯。 这些在不引进RTK高精度定位…...
Java多线程系列--synchronized的原理
原文网址:Java多线程系列--synchronized的原理_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java的synchronized的原理。 反编译出字节码 Test.java public class Test {private static Object LOCK new Object();public static int main(String[] args) {synchro…...
QEMU启动ARM64 Linux内核
目录前言前置知识virt开发板ARM处理器家族简介安装qemu-system-aarch64安装交叉编译工具交叉编译ARM64 Linux内核交叉编译ARM64 Busybox使用busybox制作initramfs使用QEMU启动ARM64 Linux内核前言 本文介绍采用 qemu 模拟ARM-64bit开发板(针对ARM-32bit的有另一篇文…...
Linux->进程程序替换
目录 前言: 1 程序替换原理 2 单进程替换 3 替换函数 3.1 函数使用 4 程序去替换自己的另一个程序操作方式 5 实现自己的shell 前言: 通过我们之前对于子进程的应用,我相信大家一定是能够想到创建子进程的目的之一就是为了代劳父进程执…...
最强分布式锁工具:Redisson
1 Redisson概述1.1 什么是Redisson?Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, Sorted…...
Java9-17新特性
Java9-17新特性 一、接口的私有方法 Java8版本接口增加了两类成员: 公共的默认方法公共的静态方法 Java9版本接口又新增了一类成员: 私有的方法 为什么JDK1.9要允许接口定义私有方法呢?因为我们说接口是规范,规范时需要公开…...
电脑开机找不到启动设备怎么办?
电脑正常开机,却提示“找不到启动设备”,这时我们该怎么办呢?本文就为大家介绍几种针对该问题的解决方法,一起来看看吧!“找不到启动设备”是什么意思?可引导设备(又称启动设备)是一…...
使用langchain打造自己的大型语言模型(LLMs)
我们知道Openai的聊天机器人可以回答用户提出的绝大多数问题,它几乎无所不知,无所不能,但是由于有机器人所学习到的是截止到2021年9月以前的知识,所以当用户询问机器人关于2021年9月以后发送的事情时,它无法给出正确的答案&#x…...
assert()宏函数
assert()宏函数 assert是宏,而不是函数。在C的assert.h文件中 #include <assert.h> void assert( int expression );assert的作用是先计算表达式expression, 如果其值为假(即为0),那么它会打印出来assert的内容…...
Docker圣经:大白话说Docker底层原理,6W字实现Docker自由
说在前面: 现在拿到offer超级难,甚至连面试电话,一个都搞不到。 尼恩的技术社群(50)中,很多小伙伴凭借 “左手云原生右手大数据”的绝活,拿到了offer,并且是非常优质的offer&#…...
Redis+Caffeine多级(二级)缓存,让访问速度纵享丝滑
目录多级缓存的引入多级缓存的优势CaffeineRedis实现多级缓存V1.0版本V2.0版本V3.0版本多级缓存的引入 在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中࿰…...
C#和.net框架之第一弹
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录C# 简介一、微软平台的编程二、使用VS创建第一个c#程序1、第一步2、第二步3、第三步4、第四步5、第五步C# 简介 C# 是一个现代的、通用的、面向对象的编程语言&…...
C++---背包模型---潜水员(每日一道算法2023.3.12)
注意事项: 本题是"动态规划—01背包"和"背包模型—二维费用的背包问题"的扩展题,优化思路不多赘述,dp思路会稍有不同,下面详细讲解。 题目: 潜水员为了潜水要使用特殊的装备。 他有一个带2种气体…...
C++类的成员变量和成员函数详解
类可以看做是一种数据类型,它类似于普通的数据类型,但是又有别于普通的数据类型。类这种数据类型是一个包含成员变量和成员函数的集合。 类的成员变量和普通变量一样,也有数据类型和名称,占用固定长度的内存。但是,在定义类的时候不能对成员变量赋值,因为类只是一种数据类…...
(枚举)(模拟)(位运算)116. 飞行员兄弟
目录 题目链接 一些话 切入点 流程 套路 ac代码 题目链接 116. 飞行员兄弟 - AcWing题库 我草,又~在~水~字~数~啦!我草,又~在~水~字~数~啦…...
详解Array.prototype.shift.call(arguments)
经常看到如下代码: function foo() {let k Array.prototype.shift.call(arguments);console.log(k) } foo(11,22) //11 Array.prototype.shift.call(arguments)的作用是: 取 arguments 中的第一个参数 一、为啥要这么写,不直接使用argume…...
Tina_Linux_Wi-Fi_开发指南
Tina Linux Wi-Fi 开发指南 1 前言 1.1 文档简介 介绍Allwinner 平台上Wi-Fi 驱动移植,介绍Tina Wi-Fi 管理框架,包括Station,Ap 以及Wi-Fi 常见问题。 1.2 目标读者 适用Tina 平台的广大客户和对Tina Wi-Fi 感兴趣的同事。 1.3 适用范…...
Spring AOP(AOP概念、组成、Spring AOP实现及实现原理)
文章目录1. Spring AOP 是什么2. 为什么要用 AOP3. 怎么学 Spring AOP4. AOP 组成5. Spring AOP 实现5.1 添加 Spring AOP 框架支持5.2 定义切面和切点5.3 实现通知方法5.4 使⽤ AOP 统计 UserController 每个⽅法的执⾏时间 StopWatch5.4 切点表达式说明 AspectJ6. Spring AOP…...
8.条件渲染指令
目录 1 v-if v-show 2 v-if v-else-if v-else 1 v-if v-show v-if与v-show都可以控制DOM的显示与隐藏 由于flag是布尔值,所以这里可以直接写 v-if"flag" 当flag为true的时候,v-if与v-show控制的div都会被显示出来 当flag为false的时候&a…...
2023年全网最全最细最流行的自动化测试工具有哪些?你都知道吗!
下面就是我个人整理的一些比较常用的自动化测试工具,并且还有视频版本的详细介绍,同时在线学习人数超过1000人! B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)一:前言 随着测试工程师技能和…...
网络安全——数据链路层安全协议
作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.数据链路层安全协议简介 1.数据链路安全性 二.局域网数据链路层协议 1.…...
编译原理基础概念
一、什么是编译程序编译程序是一种程序,能够将某一种高级语言编写的源程序改造成另一种低级语言编写的目标程序,他们在逻辑上等价、完成相同的工作二、编译阶段1、当目标程序是机器语言时,编译阶段:(1)编译…...
我想克隆个网站 怎么做/女生学网络营销这个专业好吗
安装MySQLdb pip install MySQLdb安装不成功请使用离线安装方式,pypi:https://pypi.org/project/mysqlclient/#files 选择对应的Python版本,下载.whl文件,并离线安装 pip install mysqlclient-2.0.3-cp37-cp37m-win_amd64.whl操…...
找个为公司做网站的/武汉seo排名扣费
2019独角兽企业重金招聘Python工程师标准>>> Bottle中文文档 基于官方0.12版本翻译和Linux操作示范 原作者:Marcel Hellkamp 译者:徐斌斌 2012.12.02 Bottle是一个用Python语言开发的简单,快速并且轻量级的WSGI微型Web框架.整个框架只有一个文件,并且没有任何外…...
江苏建设会计学会网站/网站是怎么做出来的
一、简述 JavaScript是用来实现浏览器和用户交互的,与html和css不同,它是门弱类型编程语言。 二、引入方式 1、内部标签 <script>代码块 </script>2、外部引入 <script src"js文件路径"></script>三、基础语法 …...
删除网站域名/交换友链是什么意思
在这个浏览器百花争鸣的时代,作为前端开发的我们为了我们漂亮的设计能适应各个浏览器可为煞费苦心,主要体现在javascript和css上面。javascript我这次就不谈了,先说说css。 为了适应不同浏览器不同的版本(版本主要就ie来说&#x…...
茂县建设局网站/谁有恶意点击软件
// template_one.cpp : 定义控制台应用程序的入口点。 //#include "stdafx.h" #include<iostream> using namespace std;//如果想要将类模板的声明与实现分别放在.h和.cpp文件中,那么在使用到类模板实现的文件,需要将该.cpp文件include进…...
网站自己维护/十大网络推广公司
File_operations结构体 结构体file_operations在头文件 linux/fs.h中定义.在驱动程序中,用来存储对设备进行各种操作的函数的指针,可以看做是这些函数与系统调用的对应关系表。系统调用发生时,系统会读取file_operation中相应的函数指针&…...