Linux:地址空间的转换以及线程的理解和使用

文章目录

  • 线程的理解
  • 地址空间的转换问题
    • 总结
  • 线程的优点
  • 线程的缺点
  • 线程的健壮性问题

本篇主要进行对于进程和线程的理解,以及对于线程的一部分使用方法和使用的原理

线程的理解

首先回顾前面一篇的内容中,对于进程的基本认识:

什么是线程,如何理解线程?线程是进程内部的一个执行分支,那么也就意味着肯定是先有进程,再有线程,这也是最基本的一个逻辑,那么在Linux系统中也确实是这样,当一个代码被编译完成之后,如果想要执行这个进程,那么在进行启动这个进程的时候,就会对这个进程建立起对应的地址,空间,页表,代码数据,内存区域的映射关系等等,这些就都是对于进程在创建之初就应该要储备好的部分,后续在用特定的方案创建一个PCB的时候,其实只需要让多个PCB去指向同样的一部分内容,就可以实现在共享地址空间中做到让多个不同的执行流去访问同一块进程内的代码和数据,那么对于进程的代码和数据资源进程合理的划分,就能实现让这些代码之间的串行变成并发执行,那么这一个一个的分支就叫做线程,因此基于上述的这两个点,可以得出下面的结论

  1. 线程在创建和释放的时候可以比进程更加轻量化,只需要对于各种各样的PCB进行管理即可
  2. 线程是进程内部的执行流,本质上线程是在进程的地址空间中进行运行的
  3. 对于进程的理解应该更加深一点:在之前的理解中,进程就是代码和数据以及内核的数据结构,这个结论是没有任何问题的,但是放在现在要多一个新的理解,进程是承担分配系统资源的基本实体,具体是如何进行承担的,简单来说就是从各种的内核数据结构和表现上来体现的,就是要花内存占用对应的CPU资源,这样就是体现出分配系统资源的基本实体
  4. 有了对于线程的理解,那么在之前对于进程的理解就应该更加广泛一些,之前对于进程的理解是体现在了内部只有一个PCB结构,这样的进程在现在看来是可以叫做是一个执行流的进程,现在对于进程就转换成了一个进程内会包含有一大堆的申请的资源,并且还会有至少一个的PCB结构,而多线程其实就是内部有多个这样的PCB结构,这就实现了一个单进程中有多线程
  5. Linux当中线程和进程都会被操作系统调度,那么现在的问题是,是否需要重新设计一个线程的结构呢?事实上从Linux的操作系统设计模式中可以看出并没有这样做,而是把之前对于进程的相关字段都重新复用了起来,也就是说Linux中的线程设计是完全用进程来进行模拟的,站在CPU的角度来说,当它拿到一个具体的PCB结构执行流的时候,它认为自己将来在CPU上跑的时候的执行的那个PCB,是要比传统意义上理解的那个单个进程PCB是不一样的,最起码是小于等于单PCB的进程,所以自此之后,可以换一个角度去看Linux中的进程,可以把进程线程都看做事一个轻量级的进程,这个轻量级的进程指的就是单纯这个PCB,而对应的整个地址,比如有地址空间页表等等信息,这些全部加起来就叫做是真正意义上的进程,而并非是轻量级进程

地址空间的转换问题

下面要谈的一个问题是虚拟到物理地址转换的问题,这个问题其实已经提过了,在最初对于进程的理解中就已经有了一个初步的描述,在前面的对于地址空间的转换问题的理解只是停留在于,从虚拟地址到物理地址是要进行页表的映射进行转换,而这样的理解其实并不是特别到位的,下面这个模块将对于这个过程进行更加详细的描述和理解

在之前对于文件系统的理解当中,提及过这样的一个观点,文件系统最终会指导操作系统和磁盘的设备进行io交互的时候,是以4kb为一个单位进行数据交互的,那么这个4kb也被叫做是一个文件块,换句话说,站在操作系统的角度来讲,它并不关心这个数据是啥,到底是图片还是视频还是音频,它关心的是读取这个文件的内容,要从外设搬到内存中,是要以一个数据块为一个单位进行搬运的,那在进程间通信的时候,也提及过对于共享内存的创建大小,必须要以这个配置为单位

为了方便描述,画出下面的最初步的示意图

在这里插入图片描述
在上述的这个系统中,物理内存和磁盘进行io交互的时候,在硬件的层面上,可以通过某种方式把数据从磁盘弄到内存当中,而从纯硬件的角度来讲,这个过程就是把数据从一个设备拷贝到另外一个设备上,但是现在的一个问题是,把数据加载到内存的什么位置?加载多少?从哪里开始加载?这些问题都是需要解决的,甚至以至于还有比如说这个数据的访问权限是多少,这个数据的属性是什么,这些问题本质上来说就已经不属于是硬件的范畴了,那这个问题的解决措施是由操作系统中的文件系统来解决的,文件系统把磁盘整体进行一个管理,那么此时文件系统就可以通过文件路径来把对应的文件打开,再把对应的数据块中的信息加载到物理内存中,从而实现了一个文件的加载和读取的过程,而这个过程其实也是在之前的理解范围之内的

这里要引出的一个结论就是,不管是什么数据,可能是这个文件的内容,也可能是这个文件的属性,但只要是属于这个文件的数据范围内,它在进行加载的时候的基本单位就是4kb,这是由文件系统来进行设置的,也就是说在文件系统的层面上看这个新建的hello.exe这个可执行程序,本质上可以看成是由若干个小的4kb这样的文件块组成的,一般给这样的数据段叫做是ELF数据段,那么最终的结论是,这里的每一个数据块都是4kb为单位的,那么在最终的操作系统中,本质上也会这样进行划分,而这样的每一个这样的4kb大小的数据块,在磁盘的角度看来就叫做是页帧,而在物理空间中的这个4kb的一块一块的空间就被叫做是页框,所以上面的图可以修改为下面的模样:

在这里插入图片描述
那么这样就能得出一个朴素的结论是,未来这个文件当中哪怕只是需要修改一个比特位,也必须要把这个数据所在的这个4kb的数据块全部弄出来加载到内存中,在内存中进行修改后,再刷新到磁盘上,这个就是基本单位的含义

下面的一个问题是,既然物理空间被划分成了这样,那么实际上计算一下:

在这里插入图片描述
在地址空间中按4kb的划分,是需要有100多万个页框的,这是一个相当庞大的数据,那么下一个问题是,对于这个页框的管理该如何进行管理呢?所以操作系统的内部会维护一个结构体对象,用来描述这个页框的属性,而在操作系统中就存在这样一个数组,用来描述这个结构体,可以近似的理解为是struct page pages[1048576],而在这个结构体page中,里面存在了很多的字段,这些字段可能是意味着有物理内存是否被锁定,也有这段内存是否被使用,当前内存是否准备释放,每一种情况都用一个比特位来表示,所以在内存中对于物理内存的管理,就可以转换成对于这个标记位的每一个比特位的管理

下一个问题是,页表的大小问题,在虚拟地址中有2^32个地址,这些地址都要被页表进行映射吗?经过简单的计算就可以很容易知道,这是不可能的,如果每一个地址都进行映射,那么页表所占据的空间可能就已经是一个很大的概念了,所以得出的一个结论是,上面图中的这个页表其实是不完全准确的,真正的页表结构还有其他的结构组成

虚拟到物理地址到底是如何进行转换的呢?在CPU中有很多的寄存器,其中一个寄存器叫做eip,那这个寄存器主要是存储的是虚拟地址,而在CPU上还会有一个小组件叫做MMU,那这个组件其实就可以理解为可以完成从虚拟到物理地址的转换工作,那这个转换是如何进行转换的?

以32位的机器为例,现在有一个地址,假设它是1111 1111 1001 0001 0000 0000 0010 1000,那么在实际的内存中其实并不会把这32个数字看成一个整体,而是会把前10个数字看出一个整体,中间10个数字看成一个整体,后面的12个数字看成一个整体,而实际上页表也并非是一个大的页表,而是会存在分级的情况,按32位的机器来看,会分成两级,第一级叫做是页目录,页目录整体上来说是被用作查找的,但是页目录的查找只会使用前10个比特位,那页目录总共会有多少个呢?经过计算总共会有1024个组合方式,那么在之后进行查询页表的时候,就会先找到前10位对应的内容,再用中间的十位进行查找,那中间的这10位就被叫做是页表项,也叫做是二级页表,在实际进行查找的时候,可以把前10个转换成一个二进制的数字,而这个数字就对应了这个数组的下标,而这个数组里面的内容指向的是下一级的页表,从这个开始的地方就能搜索到下一级的页表是谁,那么实际上,在这个一级页表中其实存储了的是一个映射关系,而这个第一部分的内容是不需要被存储起来的,直接把前10个比特位当成是一个数组的下标用就可以了,实现了一个二进制的拆分机制

那为什么要这样做,这样做的好处在哪里?实际上页表的存储是物理内存的哪个位置,本身我们也不清楚,所以当前在进行页表的构建的时候,从虚拟地址映射的这个过程中,如果没有对应的这级页表,在进行重新申请的时候就填到这个里面,最终凡是从这个地址开始的都会通过这个页表来查,而这个页表也可以当成是一个数组,而这个数组其中也有10个比特位,也就是1024,那么这个数字实际上也可以看出是一个数组的下标,里面存放的内容就是实际这个地址最终映射到所要申请的页框的起始地址,因为物理内存的角度来讲这个页框的大小是以4kb进行io的,所以在实际进行寻找这个具体内容的时候,就要通过先找到这个内容属于哪个页框,再通过这个页框找到内部的这个内容,那要找某一个数据或者是代码,前提就是要找到虚拟地址所在的页框,所以在这个页表中存储的就是物理内存的页框,因此,本质上来说,对于页表这个结构来说,整个页目录和页表项当中真正有用的部分是帮助操作系统找到页框,随后再找到具体的对应的内容,而找到页框之后,就可以根据最后的那12个比特位当成是一个偏移量,来找到与之对应的内容数据了,来用下面的这个图描述一下更为清楚:

在这里插入图片描述
借助这样的结构,就可以使得整个页表的体积大大减小,但是更为重要的是,此时找到的就是物理内存中的页框了,但是在平时进行访问的时候其实并不关心页框,关心的是页框中的某一个数据,但是只需要借助最后的偏移量就可以找到了,只需要由前10个比特位找到页目录,然后再通过下标找到对应的页表,再通过中间的10个比特位找到对应的页表项,再通过页表项的内容找到页框,再通过最后的这个偏移量来找到所需要的数据内容

所以说,页表中不存物理地址,但是会有页框的物理地址,严格意义来说,它当中只有页框的物理地址,通过第二级索引就能找到物理内存中页框的物理地址,再根据偏移量就能直接定位到具体的地址了

完善一下刚才的图:

在这里插入图片描述

总结

划分页表到底是什么呢?划分页表的本质是什么呢?本质上来说,就是在划分地址空间,站在进程的角度来讲,地址空间本身就是资源,所以划分页表就是在划分资源

在这里插入图片描述

线程的优点

  1. 创建一个新线程的代价要比创建一个新进程小得多
  2. 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  3. 线程占用的资源要比进程少很多
  4. 能充分利用多处理器的可并行数量
  5. 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  6. 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  7. I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作

线程的缺点

  1. 性能损失
    一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变
  2. 健壮性降低
    编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的
  3. 缺乏访问控制
    进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响
  4. 编程难度提高
    编写与调试一个多线程程序比单线程程序困难得多

线程的健壮性问题

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>// 新线程
void *ThreadRoutine(void *arg)
{const char *threadname = (const char *)arg;while (true){std::cout << "I am a new thread: " << threadname << ", pid: " << getpid() << std::endl;sleep(1);}
}int main()
{// 已经有进程了for (int i = 0; i < 5; i++){char threadname[64];snprintf(threadname, sizeof(threadname), "%s-%d", "thread", i);pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)threadname);sleep(1);if (i == 4)int a = 10 / 0;}return 0;
}

运行结果如下:

在这里插入图片描述
从中看出的一个问题是,当出现除0错误的时候,所有的线程都被终止了,这是因为所有的线程都共享信号的处理方式,所以当有一个线程收到信号后,其实所有的线程也就都被终止了

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

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

相关文章

【LeetCode:2368. 受限条件下可到达节点的数目 + BFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

SpringBoot-yaml语法

1.概念 在Springboot的项目中&#xff0c;配置文件有以下几种格式&#xff1a; Application.propertiesApplication.yamlApplication.yml 其中官方推荐我们使用yaml的格式(因为能表示的数据类型很多样) 2.基本语法 # yaml形式的配置文件# 普通的key-value&#xff08;分号之后…

用numpy搭建自己的神经网络

搭建之前的基础与思考 构建模型的基本思想&#xff1a; 构建深度学习的过程&#xff1a;产生idea&#xff0c;将idea转化成code&#xff0c;最后进行experiment&#xff0c;之后根据结果修改idea&#xff0c;继续idea–>code–>experiment的循环&#xff0c;直到最终训练…

matplotlib条形图

matplotlib条形图 假设你获取到了2017年内地电影票房前20的电影(列表a)和电影票房数据(列表b), 那么如何更加直观的展示该数据? from matplotlib import pyplot as plta ["Wolf Warrior 2", "Fast and Furious 8", "Kung Fu Yoga", "Jo…

线性表——单链表的增删查改

本节复习链表的增删查改 首先&#xff0c; 链表不是连续的&#xff0c; 而是通过指针联系起来的。 如图&#xff1a; 这四个节点不是连续的内存空间&#xff0c; 但是彼此之间使用了一个指针来连接。 这就是链表。 现在我们来实现链表的增删查改。 目录 单链表的全部接口…

位运算---求n的二进制表示中第k位是1还是0 (lowbit)

操作&#xff1a; 先把第k位移到最后一位&#xff08;右边第一位&#xff09; 看个位是1还是0 lowbit(x)&#xff1a;返回x的最右边的1。 原理&#xff1a; 其中 &#xff0c;意思是 是 的补码。 就可以求出最右边的一位1。 应用&#xff1a; 当中 的个数。 int re…

AI-数学-高中-33概率-事件的关系与运算

原作者视频&#xff1a;【概率】【一数辞典】2事件的关系与运算_哔哩哔哩_bilibili 事件&#xff1a; 和/并事件&#xff1b;积/交事件&#xff1b;互诉事件&#xff1b;对立(补集)事件&#xff1b;

【详识JAVA语言】面向对象程序三大特性之二:继承

继承 为什么需要继承 Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是 现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0c;那在设计程序是就需要考虑。 比如&…

04.其他方案

其他方案 1.事务状态表调⽤⽅重试接收⽅幂等 介绍 调⽤⽅维护⼀张事务状态表&#xff08;或者说事务⽇志、⽇志流⽔&#xff09;&#xff0c;在每次调⽤之前&#xff0c;落盘⼀条事务流⽔&#xff0c;⽣成⼀个全局的事务ID 事务开始之前的状态是Begin&#xff0c;全部结束之…

码垛工作站:食品生产企业的转型助推器

在当今高度自动化的工业生产中&#xff0c;码垛工作站的应用正逐渐成为一种趋势。某食品生产企业在面临市场竞争加剧、人工成本上升等多重压力下&#xff0c;决定引入码垛工作站&#xff0c;以期实现生产流程的升级与变革。 一、码垛工作站引入背景 该企业主要从事休闲食品的…

【贪心算法】Leetcode 455.分发饼干 376. 摆动序列 53. 最大子数组和

【贪心算法】Leetcode 455 分发饼干 376. 摆动序列【规律很多】53. 最大子数组和 455 分发饼干局部最优推全局最优&#xff1a;尽量用大饼干去满足大胃口的小朋友 376. 摆动序列【规律很多】思想&#xff1a;注意考虑一个坡度留首尾两个点、平坡、首尾 53. 最大子数组和【好思想…

15.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-发送通信数据包至分析工具

上一个内容&#xff1a;14.数据包分析工具界面与通信设计 码云地址&#xff08;master 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/titan 码云版本号&#xff1a;2d6491e3c51a1a7ab4da0ee6dc4cf566a80fd6e1 代码下载地址&#xff0c;在 titan 目录下&…

模版进阶C++

非类型模版 之前我们写的模版都是在不知道模版&#xff08;类&#xff09;中有的变量的类型是什么的时候&#xff0c;我们先用模版参数定义&#xff0c;当类实例化的时候在传参确认 非类型模版&#xff1a;模版参数定义的时候也可以定义整型类型&#xff08;c20之后才支持其…

奇点云:SAFe框架下,我们对平台软件工程生产线做了4项改造

导读&#xff1a; 客户规模扩大&#xff0c;如何保证大数据软件产品和服务质量始终如一&#xff1f;几乎所有成长中的软件厂商&#xff0c;尤其是需要通过私有化部署交付的厂商&#xff0c;都会面临这个问题。正如《人月神话》中多次表明的&#xff0c;单纯地增加人手、扩大团队…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的植物病害检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发高效的植物病害检测系统对于提升农业生产效率和作物健康管理意义重大。本篇博客详细阐述了如何运用深度学习技术构建一个植物病害检测系统&#xff0c;并提供了完整的实现代码。该系统基于先进的YOLOv8算法&#xff0c;对YOLOv7、YOLOv6、YOLOv5进行了性能…

考研数学——高数:微分方程

一、一阶线性微分方程 两种形式&#xff1a; 非齐次&#xff1a; 齐次&#xff1a; 推导过程 推导公式的过程一般由特殊到一般&#xff1a;所以先求解齐次方程的解 &#xff08;然后对等式两边同时积分&#xff09; 再来求非齐次方程的解&#xff0c;由…

03-grafana的下拉列表选项制作-grafana的变量

一、准备环境 为了实现下拉列表筛选的样例&#xff0c;我们监控两个linux节点&#xff1b; 目前&#xff0c;我们已经有了一个节点了&#xff0c;再添加一个&#xff1b; 二、grafana的仪表盘变量 如果想给仪表盘自定义下拉列表&#xff0c;那么&#xff0c;需要设置变量&#…

线上问题——2021-12-27 父子线程共用线程池导致死锁故障

一、事故现象 从早上6点开始edu-wings-admin的timer-task和mq就开始报警任务堆积&#xff0c;且数量持续上升&#xff0c;到6点50左右mq也开始告警&#xff0c;8点左右发现问题&#xff0c;开始排查&#xff0c;直到11点才找到问题&#xff0c;任务开始正常消费。 二、事故影响…

基于springboot+vue的疾病防控综合系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

计算机设计大赛 深度学习猫狗分类 - python opencv cnn

文章目录 0 前言1 课题背景2 使用CNN进行猫狗分类3 数据集处理4 神经网络的编写5 Tensorflow计算图的构建6 模型的训练和测试7 预测效果8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习猫狗分类 ** 该项目较为新颖&a…