多线程(虚拟地址空间)

代码展示线程

既然我们提到了,线程隶属于进程,是进程的一个执行分支
真的是这样吗?
我们还需要用代码来验证
初步思路是创建三个线程,其中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.xdnf.cn/news/142993.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

四,立方体贴图

Pbr的间接光用到立方体贴图&#xff0c;所以&#xff0c;先用shader进行立方体贴图。 立方体贴图很简单&#xff0c;就是用方向向量&#xff08;不一定是单位向量&#xff09;采样cubeMap的颜色。 也就是在片元着色器中传递。 "float x outPos.r;\n" "float y…

位运算符与高级操作

位运算符与高级操作 运算符 高级操作 左移实现乘法 左移n位等价于乘以2的n次方 int x; x 2; x x << 2; x x << 3;使用左移实现乘法运算仅限于乘以2的倍数 是不是只要左移就能够实现乘以2的倍数呢? char x 120; x x << 1;右移实现除法 右移n位等价于除…

查看基站后台信息

查看基站后台信息 电脑配置固定ip: 192.168.1.99: 打开“网络和共享中心”&#xff0c;选择更改适配器设置&#xff1a; 右键“本地连接”&#xff0c;选择属性 基站网线直连电脑网口 Telnet 登录基站 打开dos窗口 windows键R”&#xff0c;输入cmd&#xff0c;点确定&…

MySQL的执行流程

在聊mysql的执行流程之前&#xff0c;咱们要先聊聊mysql的逻辑架构。 逻辑架构 可以将上图简化为下图 连接层 客服端访问mysql服务器前&#xff0c;要先和mysq建立tcp连接。经过3次握手建立连接成功后&#xff0c;mysql服务器对tcp传输过来的账号密码进行身份认证&#x…

【大数据】Doris 构建实时数仓落地方案详解(二):Doris 核心功能解读

本系列包含&#xff1a; Doris 构建实时数仓落地方案详解&#xff08;一&#xff09;&#xff1a;实时数据仓库概述Doris 构建实时数仓落地方案详解&#xff08;二&#xff09;&#xff1a;Doris 核心功能解读Doris 构建实时数仓落地方案详解&#xff08;三&#xff09;&#…

Selenium —— Web自动化多浏览器处理!

一、多浏览器测试介绍 1.1、多浏览器测试背景 用户使用的浏览器(firefox,chrome,IE 等)web 应用应该能在任何浏览器上正常的工作&#xff0c;这样能吸引更多的用户来使用 1.2、多浏览器测试概述 是跨不同浏览器组合验证网站或 web 应用程序功能的过程是兼容性测试的一个分支…

git学习使用

git使用 1、cmd #查看版本 git version2、初识 Git GUI: Git提供的图形界面工具 Git Bash: Git提供的命令行工具 1.打开Git Bash2.设置自己的用户名和邮箱地址git config --global user.name "xxx"git config --global user.email "123456789163.com"查…

大数据Flink(八十七):DML:Joins之Regular Join

文章目录 DML:Joins之Regular Join DML:Joins之Regular Join Flink 也支持了非常多的数据 Join 方式,主要包括以下三种: 动态表(流)与动态表(流)的 Join动态表(流)与外部维表(比如 Redis)的 Join动态表字段的列转行(一种特殊的 Join)细分 Flink SQL 支持的

【数据结构与算法】链表的实现以及相关算法

目录 单选链表的基本实现 有序列表的合并&#xff08;双指针法&#xff09; 链表的反转 链表实现两数之和 判定链表是否有环 双链表的实现 public class DLinkedList<E> {private Node<E> first;private Node<E> last;int size;/*** 头插法* param i…

Prettier - Code formatter格式化规则文件

文章目录 前言安装使用 前言 先前公司在规范代码时,由于个人业务繁忙跟技术总监是后端出身用的IDEA不熟悉vsCode;以及大多数时都自己一个人负责一个项目,当时并不看重这些;最近在整理vue3tsvite的脚手架模板(平时工作用的react),开始整理格式化代码,方便之后 vue 和 react 中应…

Android Shape设置背景

设置背景时&#xff0c;经常这样 android:background“drawable/xxx” 。如果是纯色图片&#xff0c;可以考虑用 shape 替代。 shape 相比图片&#xff0c;减少资源占用&#xff0c;缩减APK体积。 开始使用。 <?xml version"1.0" encoding"utf-8"?…

高效查询大量快递信息,轻松掌握技巧

在如今快节奏的生活中&#xff0c;快递已经成为我们日常不可或缺的一部分。然而&#xff0c;对于一些忙碌的人来说&#xff0c;单个查询每一个快递单号可能会浪费太多时间。因此&#xff0c;我们需要一款可以帮助我们批量查询快递的软件。 在市场上&#xff0c;有很多款专门用于…

【2023年11月第四版教材】第15章《风险管理》(第四部分)

第15章《风险管理》&#xff08;第四部分&#xff09; 8 过程4-实施定量风险分析8.1 实施定量风险分析★★★8.2 数据分析★★★8.3 定量成本风险分析S曲线示例8.4 决策树示例8.5 龙卷风图示例8.6 项目文件&#xff08;更新&#xff09;★★★ 9 过程5-规划风险应对9.1 规划风险…

【2023款奔驰改款E260 L运动型:豪华与性能的完美结合】

在汽车市场中&#xff0c;奔驰一直以其卓越的品质和卓越的性能赢得了消费者的喜爱。而2023款奔驰改款E260 L运动型&#xff0c;更是将豪华与性能完美结合&#xff0c;让人无法抗拒。首先&#xff0c;让我们来看一下这款车的外观设计。新款E260 L运动型的前脸设计更加犀利&#…

【Linux】——基操指令(一)

个人主页 代码仓库 C语言专栏 初阶数据结构专栏 Linux专栏 LeetCode刷题 算法专栏 目录 前言 基操前的碎碎念 计算机的层状结构 基础指令 查看登录用户指令 查看用户指令 查看当前所处工作目录 清屏指令 基操指令 ls命令 cd命令 makdir指令 rmdir指令 &…

十二、MySql的事务(下)

文章目录 一、事务隔离级别二、如何理解隔离性三、隔离级别&#xff08;一&#xff09;读未提交【Read Uncommitted】&#xff1a;&#xff08;二&#xff09;读提交【Read Committed】 &#xff1a;&#xff08;三&#xff09;可重复读【Repeatable Read】&#xff1a;&#x…

【计算机网络笔记六】应用层(三)HTTP 的 Cookie、缓存控制、代理服务、短连接和长连接

HTTP 的 Cookie HTTP 的 Cookie 机制要用到两个字段&#xff1a;响应头字段 Set-Cookie 和请求头字段 Cookie。 Cookie 可以设置多个 key-value 对&#xff0c; 响应头中可以设置多个 Set-Cookie 字段&#xff0c;请求头Cookie后面可以设置多个键值对&#xff0c;用分号隔开&a…

西门子KTP触摸屏做画面时如何把设备图片或Logo做到画面上?

西门子KTP触摸屏做画面时如何把设备图片或Logo做到画面上&#xff1f; 如下图所示&#xff0c;新建一个项目&#xff0c;添加一个触摸屏设备&#xff0c;这里以TP1200 Comfort触摸屏为例进行说明&#xff0c;双击进入根画面&#xff0c; 如下图所示&#xff0c;在右侧的工具箱中…

学习路之工具--SecureCRT的下载、安装

百度盘&#xff1a; 链接: https://pan.baidu.com/s/1r3HjEj053cKys54DTqLM4A?pwdgcac 提取码: gcac 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 感谢大佬 简单介绍下SecureCRT SecureCRT是一款支持SSH&#xff08;SSH1和SSH2&#xff09;的终端仿真程序&a…

[C++ 网络协议] 多线程服务器端

具有代表性的并发服务器端实现模型和方法&#xff1a; 多进程服务器&#xff1a;通过创建多个进程提供服务。 多路复用服务器&#xff1a;通过捆绑并统一管理I/O对象提供服务。 多线程服务器&#xff1a;通过生成与客户端等量的线程提供服务。✔ 目录 1. 线程的概念 1.1 为什…