多线程(虚拟地址空间)

代码展示线程

既然我们提到了,线程隶属于进程,是进程的一个执行分支
真的是这样吗?
我们还需要用代码来验证
初步思路是创建三个线程,其中main函数里面的为主线程
不断循环,并且打印相应的pid
假如它们属于不同的进程,那打印出来的pid应该不同,并且由于是属于不同的进程,所以一次应该只有一个程序被循环执行

    1 #include <iostream>2 #include <pthread.h>3 #include <unistd.h>4 using namespace std;5 void* threadRun1(void* args)6 {7    while(true)8    {9      sleep(5);                                                                                                                                                    10      cout << "t1 thread:" << getpid() << endl;11    }12 }13 void* threadRun2(void* args)14 {15    while(true)16    {17      sleep(5);18      cout << "t2 thread:" << getpid() << endl;19    }20 }21 int main()22 { 23   pthread_t t1,t2,t3;   //创建三个线程24   //初始化t1,t2线程25   pthread_create(&t1,nullptr,threadRun1,nullptr);26   pthread_create(&t2,nullptr,threadRun2,nullptr);27 28   while(true)29   {30     sleep(1);31     cout << "main thread: " << getpid() << endl;32   }33   return 0;34 }

如果要创建线程成功,编译时还需要加-lpthread选项
对应的makefile内容如下:

  1 mythread:mythread.cc2   g++ -o $@ $^ -std=c++11 -lpthread3 .PHONY:clean4 clean:5   rm -f mythread 

下面是结果展示
在这里插入图片描述
可以看到对应的线程pid都是相同的,说明它们都是隶属于一个进程,而该进程的pid是27295
在命令行窗口输入下面的命令,还可以观察到对应线程的lwp

while :; do ps -aL | head -1 && ps -aL | grep mythread ; echo "**************************"; sleep 1;done

在这里插入图片描述
结果和预期完全相符,有一个PID和LWP相同的线程,这就是我们说的主线程,也解释了我们之前有关进程的说法是没有问题的,当只有一个PCB时,进程的PID,LWP是完全相同的
操作系统调度的时候,用PID识别进程,而用LWP来区别线程

虚拟地址空间

两个问题

现在有了初步线程的概念,我们就可以进一步深挖虚拟地址空间的概念
在这里插入图片描述
这张图片我们已经接触过很多次了,但是有两个重要的关键点,我们一直没有讲

第一点.
拿32位机器来举例子,其实就是对应的数据线有32条,那每条数据线都有0,1两个状态,总共可以表示多少个地址呢?
答案是2^32个地址 从0x0000 0000到0xFFFF FFFF 我们又知道 2 10 2^{10} 210 = 1KB,因此把 2 32 2^{32} 232
看作4 * 2 10 2^{10} 210 * 2 10 2^{10} 210 * 2 10 2^{10} 210 = 4 * 1GB = 4GB空间大小
但是这仅仅是数量的大小,地址本身也要空间来存 而虚拟地址空间需要存,真实的物理地址空间也需要存
在32位机器下,一个地址用4个字节,一个int类型来存,那总共就要8个字节,然后除了虚拟地址,真实物理地址,实际上还存在其它属性,也需要4字节
那满打满算其实总共需要48GB
那还谈什么页表?单单是页表,就已经耗费这么多的空间,还没谈地址空间里面存的数据,甚至一个虚拟物理内存也就4GB,还映射什么呢?

第二点
我们曾经提到过,物理内存的基本单位是一个字节
但是我们管理的时候,真的是按照一个字节来进行管理的吗?
过多的IO,注定了寻址会非常的频繁,磁盘是一个物理装置,也就对应着过多的机械运动,这样的效率是非常低下的!
因此,OS在和磁盘这样的设备进行IO交互的时候,绝对不是按照字节为单位,而是按照块为单位

这就好像学校最基本的单位是一个一个学生,但是举办校运会的时候,并不是为你一个人而举办,而是以班级为单位,一整块一整块的进行管理
操作系统追求的是效率,100MB的数据,我一次取100MB,和取100次1MB,这样的效率差距是非常明显的
前面在文件系统时,我们已经学习过,根据文件系统和操作系统的不同,块的大小也不同,在Linux操作系统下,一个块的大小是4KB
但是这样就结束了吗?
领导规定我们就按照小组来进行奖惩,小组整体目标完成优异,得到的奖励就多;小组整体表现很差,自然就要惩罚
这建立的基础就在于,员工都要达成一个共识——以团队为单位进行工作!
不然领导这么说,下面的人还是单打独斗,有用吗?
同样的道理,磁盘说,我会按照8格扇区为单位,4KB为一块进行划分
但是你文件系统管理数据的时候还是以1KB来取数据放到对应物理内存,那划不划分,其实区别不大,还是要取多次!
所以,所谓要按块来进行管理,必须达成两个条件

1.文件系统+编译器
两个软件管理数据的时候,都要以4KB为单位(块)进行管理
即便我们只是改一个比特位,或者只需要1KB的内存空间,我们申请的空间大小也必须以块来申请!
2.操作系统+内存
物理内存已经按照4KB进行划分好,操作系统从内存中存取数据,也要以4KB为单位(块)进行管理

所以所谓的内存管理,就是指
将磁盘中特定4KB(数据内容)放入到哪一个物理内存的4KB空间(数据保存的空间)
其中我们把物理内存划分的4KB大小的块,称之为页Page,也叫页框
磁盘划分的4KB大小的块,称之为页帧

但是,还有一个问题没有解决,物理内存和磁盘都是硬件,我说按照4KB来进行划分,难不成用水果刀一块一块切来划分吗?
所以所谓的划分,更多是指软件层面上的辅助,即存在对应的Page结构体,来描述这一个个块,而块的大小是4KB
先描述再组织
Page结构体多起来,就需要对它们进行管理
Linux采取的方式是用数组来进行管理,为一个结构体数组
为什么用数组来进行管理,其实也很好理解
因为数组有下标!有下标,每个块就有对应的块编号,有了块编号,就能和inode建立关系,这样就可以很方便的管理这一个个块!

局部性原理

但是,虽然说按照块来进行管理,那我一次性加载块这么多的数据,不会很浪费吗?
局部性原理指出的就是这个问题,它告诉我们并不会浪费!
它允许我们提前加载正在访问的数据与其附近的数据
通过预加载要访问数据的附近数据,来减少未来需要加载的次数
就像我们学习知识一样,现在看来,可能没有什么用,但是很大概率,某一天就会需要用到对应的这部分知识
现在吃亏,实际是为了未来享福

总结

以4KB为单位进行块管理数据的基础是什么?
1.IO基本单位(内核内存+文件系统)都要提供对应的支持
2.通过局部性原理,它告诉我们,以块为单位进行管理,能够提高未来命中情况,等到了相应部分,就不用再去磁盘中取,这能大大提高效率

为什么是4KB呢?

但是我们前面提到了这么多东西,还有一个问题没有解决
那就是为什么是4KB呢?不是5KB,6KB?
为了解答这个疑惑,我们还需要回归到我们一开始的问题,如果按照我们之前理解的方式来定义页表,那页表的大小不是我们能承受的
问题出在哪里呢?
中国领土非常广阔,如果只按照划分镇来管理,那镇的数目是非常庞大的
同样的,学校内部有很多的教学楼,如果按照教室进行划分,依次进行编号,那这个数目也是非常多的,而且非常混乱
但是,我们先划分省,再根据省划分市,即便镇的名字是相同的,我们也可以区分是不同的地区
无独有偶,我们先给教学楼命名,第一栋称为L1,第二栋称为L2,以此类推,则教室的编号,即便是相同的,都叫503,我们也知道这间教室的位置在哪
因此,问题的关键不是数量!而是需要将数据看作是整体使用,还是局部使用!

虚拟地址,并不是整体被使用的

对于一个有32位的虚拟地址来说
我们将它切割成10 + 10 + 12比特位的结构
用前10个比特位表征页目录,类比于书的一个个目录
中间10位表征页表项,类比于书每个目录里面的章节
页表项里面存放的就是对应物理内存中页框的起始地址
那接下来剩下的12个比特位, 刚好就是4KB大小 2 12 2^{12} 212 = 4KB)
它记录的就是偏移量,可以类比它为章节里面具体的每一回,从对应页框的起始地址处向后进行偏移,就可以找到物理内存中某一个对应的字节数据
它的核心思想就是沿用从8086最开始设计的理念
基地址+偏移地址的方式进行寻址

只不过原来的段寻址,变为现在的页内偏移寻址
第一个页目录,总共有 2 10 2^{10} 210个表项
每个表项对应一张页表项,一个页表项,也是 2 10 2^{10} 210个表项
按照每个表项12KB来说,总共也就 2 10 2^{10} 210 * 2 10 2^{10} 210 * 12 = 12MB
这大大节省了空间
为什么能节省空间?就是因为记录的偏移量,每个页框都是相同的!
都是从0x000 - 0xFFF,每个页框的大小都是4KB
我们进行地址区分的时候,不需要将这部分偏移地址还拿出来参与地址划分啊!
就算是相同的偏移量,但是我有了前面的页目录,页表项的划分,我已经可以锁定我们所想要的字节数据!
就好像教学楼L1503和教学楼L2503,两间教室编号(偏移量)相同,但很明显不会是同一间教室
否则如果依旧参与划分,12MB * 2 12 2^{12} 212 = 48GB,就是我们原来页表的存储思路
Linux系统就是采取这样二级映射的方式+设定文件块大小为4KB,巧妙解决了页表存储空间的问题

现象

这也解释了我们以前学过的很多现象
任何一个对象或者变量,可能存在多个字节,但是你会发现,永远取地址的时候,只会拿到一个地址数字
这个地址数字是什么?就是页框的起始地址
那具体要从页框中的哪个部位取我们的数据呢?
就需要结合我们的数据的类型

int a = 10;
double d = 30.0;

我们取变量的地址&a,&d,得到的都是一个地址,也就是起始地址
我们再根据它的类型,获取对应偏移量,这样就可以获取页框中的具体数据

核心思想:基地址+偏移量

补充

MMU

上面所说的所有映射过程,都是由MMU(MemoryManagementUnit)这个硬件完成的,该硬件是集成在CPU内的
页表是一种软件映射,MMU是一种硬件映射,所以计算机进行虚拟地址到物理地址的转化采用的是软硬件结合的方式

画大饼

另外,采取虚拟地址还有一个好处,就是提前规划
我们实际在申请malloc内存的时候,OS只需要给你在虚拟地址空间上申请就行
但是只有真正访问的时候,OS才会自动给你申请或者填充页表+申请具体的物理内存
不然你说给这么多,OS给你了,但你又不使用,造成空间闲置,这并不符合操作系统追求高效率的目的
还有一点,数据是按照块一块块加载进来的,并不是一口气全部加载进来,只有用到了,操作系统才进行相应的数据加载

缺页中断

同时,我们上面也已经指出来,页表除了存地址外,其实还存有其它属性
在这里插入图片描述
比如说是否已经被load到内存(是否命中)
是否有RWX权限,U/K权限(User or kernel)
假如没有对应的权限,而仍然执意操作,则会引发相应的缺页中断,默认中断执行方式为程序终止
比如说我们建了个常量字符串char* s = “hello world”,它对应的虚拟地址就被规划在字符常量区,s里面保存的就是指向字符的虚拟地址
此时我们对s进行修改,*s = ‘H’
既然是虚拟地址,就必定会伴随虚拟到物理转变的过程
--------->虚拟到物理转变,就必定经过页表
--------->MMU查页表的时候,会对你的操作进行权限审查,RWX中没有W
--------->发现你的操作非法
--------->MMU发生异常
--------->OS识别到异常,转换成信号,发送给目标进程
--------->在从内核态切换到用户态的时候,进行信号处理
--------->执行的默认方法就是终止进程
于是我们会看到程序报错

总结

内存管理于文件系统配合(4KB,块为单位管理) +页表(二级映射) +虚拟地址+缺页中断(保证权限正常) = 物理地址的寻址方式

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

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

相关文章

Java 大厂八股文面试专题-JVM相关面试题 类加载器

Java 大厂八股文面试专题-设计模式 工厂方法模式、策略模式、责任链模式-CSDN博客 JVM相关面试题 1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序…

JDK、JRE 和 JVM 的区别和联系

三者关系 就这三者的关系而言&#xff0c;jvm是jre的子集&#xff0c;jre是jdk的子集&#xff0c;具体关系如下图&#xff1a; Java的执行流程 对于一个Java程序&#xff0c;其执行流程大致如下&#xff1a; 开发人员使用JDK编写和编译Java源代码&#xff0c;生成Java字节码文…

spring源码解析——IOC-开启 bean 的加载

概述 前面我们已经分析了spring对于xml配置文件的解析&#xff0c;将分析的信息组装成 BeanDefinition&#xff0c;并将其保存注册到相应的 BeanDefinitionRegistry 中。至此&#xff0c;Spring IOC 的初始化工作完成。接下来我们将对bean的加载进行探索。 BeanFactory 当我…

C语言 数据类型

变量声明 格式&#xff08;变量类型变量名称&#xff09; 变量类型&#xff1a;整数类型&#xff08;int&#xff09;&#xff0c;浮点数类型&#xff08;float&#xff09; float类型可以存储带小数的数字。 用printf()打印变量&#xff0c;使用%d来处理整数值&#xff0c…

学习记忆——宫殿篇——记忆宫殿——记忆桩——风景

河边街道窗框空间房顶楼房水塔山顶塔桥舟桥楼观景台 车顶架碧水池&#xff08;喷泉&#xff09;塔腰楼顶房檐碑石狮箱车叉牌摩托灯

VUE指令语法解析标签属性

我们可以在标签体中使用插值语法 {{ }} 来直接读取data中的属性 那我们能使用相同的方法将我们的网址给填入a标签的href属性中吗&#xff1f; 我们运行后会发现并没有给我们变为<a href"https://blog.csdn.net/XunLin233">&#xff0c;而是<a href"{{…

机器学习与数据挖掘第三、四周

为什么第二周没有呢……因为刚换老师&#xff0c;自学要适应一段时间。 本课程作者之后的学习目标是&#xff1a;实操代码&#xff0c;至少要将作者参加数学建模中用到的数据处理方法都做一遍。 首先&#xff0c;作者复习一下李宏毅老师的两节课程。 机器学习概述 机器学习就…

【Linux】:Kafka组件介绍

目录 环境简介 一、消息 二、主题 三、分区 四、副本 五、生产者 六、消费者 七、消费者组 八、offsets【偏移量】 环境简介 Linux内核&#xff1a;Centos7 Kafka版本&#xff1a;3.5.1 执行命令的目录位置&#xff1a;Kafka安装目录的bin目录下&#xff1a;/usr/loca…

著名数字音频工作站FL Studio 21.0.3.3517中文破解安装图文激活教程

在一个技术继续塑造我们日常生活的世界里&#xff0c;创造力找到了表达自己的新渠道。FL Studio 21成为一个强大的工具&#xff0c;使个人能够创作自己的音乐杰作。一个人需要广泛的乐器知识或一个成熟的工作室来创作交响乐的日子已经一去不复返了。有了FL Studio 21&#xff0…

前端架构师进阶之路07_JavaScript函数

1 函数的定义与调用 1.1 初识函数 函数是用于封装一段完成特定功能的代码。 相当于将一条或多条语句组成的代码块包裹起来&#xff0c;在使用时只需关心参数和返回值&#xff0c;就能完成特定的功能&#xff0c;而不用了解具体的实现。 // 内置函数 console.log(parseFloat…

华为云云耀云服务器L实例评测|华为云云耀云服务器L实例CentOS的存储和备份策略

1 华为云云耀云服务器L实例介绍 华为云云耀云服务器L实例是华为云计算服务中的一种虚拟云服务器&#xff0c;它提供了强大的计算资源&#xff0c;可以在云端运行各种应用程序和服务。 华为云服务器提供了多种实例类型&#xff0c;包括通用型、计算优化型、内存优化型等&#…

【数据结构-图】最短路径

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

华为乾坤区县教育安全云服务解决方案(2)

本文承接&#xff1a; https://blog.csdn.net/qq_37633855/article/details/133276200?spm1001.2014.3001.5501 重点讲解华为乾坤区县教育安全云服务解决方案的部署流程。 华为乾坤区县教育安全云服务解决方案&#xff08;2&#xff09; 课程地址解决方案部署整体流程组网规划…

曲线救国-通过Magisk安装burp证书到系统根目录

0x01前言 需要对某APP做渗透测试&#xff0c;但该APP做了限制&#xff1a;不信任用户证书。因此需要将burp证书导入到存放系统证书目录下。虽然手机装了Magic&#xff0c;但似乎root有点问题。其挂载有问题&#xff0c;导致无法将 最初尝试&#xff1a;mount -o rw,remount -t…

【牛客网】排序子序列

代码 import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);int n in.nextInt();int[] array new int[n1];//此处n1 防止后面判断i1下标时数组越…

python中使用matplotlib绘图

一、背景 当我们在写python程序时&#xff0c;不可避免的需要将数据可视化&#xff0c;也就是绘制出数据的曲线图&#xff0c;以便我们更直观的观察数据间的变化&#xff0c;以及方便对比。此时就要用到matplotlib库了。 matplotlib官方给出的定义是&#xff1a; 翻译过来也就…

云HIS 医院综合运营管理系统源码

医院管理信息系统&#xff08;HIS&#xff09;是医院基本、重要的管理系统&#xff0c;是医院大数据的基础。 基于云计算的云医疗信息系统&#xff08;云HIS&#xff09;。以SaaS的方式提供服务&#xff0c;系统遵循服务化、模块化原则开发&#xff0c;具有强大的可扩展性&…

深度学习-学习率调度,正则化,dropout

正如前面我所说的&#xff0c;各种优化函数也依赖于学习率&#xff0c;保持学习率恒定总是有所限制&#xff0c;在执行梯度下降过程中&#xff0c;我们可以使用各种方法来调节训练过程的学习率&#xff0c;这里只是稍微介绍一下&#xff0c;不会写代码实现的。同时&#xff0c;…

Apache DolphinScheduler 在奇富科技的首个调度异地部署实践

奇富科技&#xff08;原360数科&#xff09;是人工智能驱动的信贷科技服务平台&#xff0c;致力于凭借智能服务、AI研究及应用、安全科技&#xff0c;赋能金融机构提质增效&#xff0c;助推普惠金融高质量发展&#xff0c;让更多人享受到安全便捷的金融科技服务。作为国内领先的…

Java BigDecimal 详解

目录 一、BigDecimal 1、简介 2、构造器描述 3、方法描述 4、使用 一、BigDecimal float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算&#xff0c;这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而&#xff0c;它…