线程...

文章目录

  • 1.Linux中线程该如何理解
  • 2.重新定义线程 和 进程
  • 3.重谈地址空间 --- 第四讲
  • 4.Linux线程周边的概念

线程:是进程内的一个执行分支。线程的执行粒度,要比进程要细
很多教材喜欢这么说,这只是一个线程的特征之一,来解释线程。

1.Linux中线程该如何理解

地址空间是进程的资源窗口!
你的进程想访问用户空间和访问OS都必须通过地址空间去查看,加上页表转化去物理内存中去找
在这里插入图片描述

我们以前创建子进程,父子进程在数据结构上相互独立,需要给子进程开辟新的pcb,地址空间,页表,虽然大部分都是从父进程来的。
如果我今天再创建一个“进程” 不再给它创建新的地址空间,新的页表,我只创建pcb,让其指向父进程的地址空间,也指向父进程的页表(我们不管)
他们两个pcb把代码区分一部分,全局数据区分一部分,堆区分一部分给这个新的“进程”,或者两个Pcb直接共享某个区域
此时新的子进程就在父进程的地址空间内运行,同时把页表中对应分出来区域的映射给新的子进程就可以了
既然能创建一个,就能创建多个,只创建pcb,把一个进程的地址空间分成若干份分给子进程部分代码。
此时线程的执行粒度,要比进程更细,为什么这么说?
以前主进程执行全部代码,现在只需要执行全部代码一部分。
它只执行其中一部分,所以把它称为进程内的一个执行分支。
为了区分和fork创建子进程的区别,把这种形式的进程起个名字叫做 线程

Linux实现方案 – 结论

1.在Linux中,线程在进程“内部”执行,更详细的说是线程在进程的地址空间内运行(为什么? )
任何执行流要执行,都要有资源! 地址空间是进程的资源窗口
你进程或线程你要执行你要不要代码,没有代码和数据你就跑不起来,你要的话就得在地址空间内要。

2.在Linux中,线程的执行粒度要比进程要更细? 线程执行进程代码的一部分

站在CPU角度它知不知道哪个task_struct是进程,哪个是线程?或者他需不需要知道?
cpu不需要关心它执行的是进程还是线程,它只有调度执行流的概念
cpu要代码和数据你就给他,让他找到代码和数据执行就行了

2.重新定义线程 和 进程

什么叫做线程呢?我们给个定义
我们认为,线程操作系统调度的基本单位!
(我们以前可根本没说进程是OS基本调度单位,没说过。)

以前不都是拿着个进程调度来调度去,那什么叫进程呢?
所有的执行流都叫进程执行流,地址空间都叫做进程所占有的资源,页表和进程在物理内存中代码和数据,把这一整套称之为进程
重新理解进程? 内核观点: 进程是承担分配系统资源的基本实体
在这里插入图片描述

所以显而易见地址空间页表代码和数据都是要占资源的,那pcb执行流是资源吗?
是的
不要认为一个进程能被调度它就是进程的所有,它只是进程内部的一个对应的执行流资源被cpu执行了。
线程只是进程概念中的基本调度单元,所以进程和线程的关系是进程内部包含线程,因为进程是承担分配系统资源的基本实体,而你线程是我进程内部的执行流资源。

以前给的进程概念:进程= 内核数据结构(task_struct) +代码和数据— v1
当然也是对的,指的是所有的内核数据结构,所有的PCB

创建进程OS会给他分配很多资源,如果你要创建线程,在进程内部创建pcb,然后把进程的资源分出一部分给你线程你去调度吧,你去执行吧。

可是如何理解我们以前的进程???
在这里插入图片描述

操作系统以进程为单位,给我们分配资源,我们当前的进程内部,只不过当前进程只有一个执行流 !

复用进程数据结构和管理算法
struct task_strut ----模拟线程
如果你的资源PCB只有一个那你就是进程,如果内部有多个PCB就是线程

甚至linux中我不区分PCB是进程还是线程,我都把他叫执行流,承担分配系统资源的基本实体才是进程


(那么如果我们真正的一个进程内部要有对应的线程,所以进程和线程它对应的比例一定是1:n的至少是1:1,也就是一个进程里面应该有多个线程
所以你这个线程执行的时候,那么当前的状态是什么?那么你这个线程当前执行到什么位置了?当前需要访问哪些资源?即将访问哪些资源?你这个线程是属于哪个进程的?你这个线程呢在调度的时候啊,那么什么时候被切换了?需要被切换吗?时间片有没有到等等等等啊,
第二线程可不是一创建就退出,一创建就完成,是创建才是开始,操作系统要能够调度这个线程,那要运行这个线程,切换这个线程,所以线程又多,比进程还多,你还要来对他做调度,一个问题,操作系统要不要管理线程?
那我当然要管理啊,你不管理我,我这个线程我应该属于哪个进程?我的地址空间在哪里?我的代码在哪里?我调度到哪里了,我状态是什么?
必须得管怎么管理,先描述再组织
先描述再组织,你想一想吧,曾经光光这么多的PCB就把你搞得头昏脑胀的,那么再来给你搞一大堆的tcb啊,你先描述tcb再组织,你组织一下试试,,这个线程出问题还要影响整个进程,等那个复杂的关系维护会特别特别特别特别特别复杂
Windows操作系统他就这么干了,他就给线程创建的tcp,然后再把进程和线程之间还有关联起来
那么我们Linux呢,他们是这么认为的你这个线程不也被调度吗?你线程要的代码和进程的代码,无非线程的代码少了一点儿,你也要切换,也要调度,无非就是线程的资源少一点嘛,好,那么Linux的设计者来说,我们当然要遵守人家的设计哲学,对线程要管理,先描述,再组织,可是谁规定描述必须得用新的方法来描述,谁规定描述都必须得用组织都必须得用新的,组织方式用新的来组织。
其实你的进程和线程高度类似可以复用tast_struct结构体来模拟线程,那么进程我们已经描述了,他们都有状态,有优先级,要有自己的上下文要被切换。)


我们把LInux当中的执行流,叫做轻量级进程
因为 执行流 <= 进程 你执行流要是进程 那就相等 ,执行流要是线程那就是粒度<进程

3.重谈地址空间 — 第四讲

问题:如何理解把资源分配给每个线程(执行流)?

CPU有寄存器保存当前调度进程的PCB,PCB找到地址空间就找到了,而地址空间其实也有字段找到它的页表
CR3寄存器能找到页表
物理内存分成了一个一个页框,每个页框4KB 按照字节换算 是 2^12 byte
在这里插入图片描述
下面重点谈页表
虚拟地址是如何转换到物理地址的?? ?

从物理内存页框内容当中读取到CPU的地址是虚拟地址,然后在CPU内部做转化找到物理地址
3位虚拟地址为例
虚 拟地址是多少位的? 32位
如何理解页表呢?
第一 32位虚拟地址 不是一个整体 而是转化成了 10 + 10 + 12 = 32
第二 页表也不是一整块的,如果他是一整块,每一个行中有虚拟地址,物理地址,权限位假设有10字节,页表被写满,有2^32个地址,也就是2 ^32行,再乘以10结果是字节进行换算大概是40G
这样整个物理内存放不下这一张页表,更别提所有进程都有页表了
所以页表不可能是我们以前画的一张大表
在这里插入图片描述
页表是拆成了两级的
第一级页表有1024个条目,二级页表也有1024个条目
你将来在CPU寻址读到的虚拟地址有32位 ,假如是 0000 0000 0000 0001 0000 0000 0000 0101
会从左往右按照10+10+12被拆成 0000000000 0000010000 000000000101
10,10,12每个区域都有自己的十进制数,范围:从全0到全1
在这里插入图片描述

用第一个十个比特位转化成十进制数 充当第一级页表的数组下标。
一级页表存放的是二级页表对应的地址,接着找到二级页表,
拿着第二个十进制数索引二级页表中的下标,
二级页表中保存的是物理内存中页框的起始地址(低地址),所以就能找到物理内存中的页框了。

一级页表一般叫 页目录
页目录里面的内容 叫 页目录表项

二级页表里面 的内容 叫 页表表项

其实只需要通过虚拟地址前20位查一级查二级页表其实已经找到对应的页框了。
接下来还有剩下的12位范围[0,2^12-1]一共2 ^12个,刚好是页框的大小,12位相当于你要访问物理内存在页框中的偏移量,用页框地址 + 虚拟地址的最后12位 = 物理地址
在这里插入图片描述
下面我们来算算账

先算一个页表有多大?
二级页表一行中保存了页框的起始地址 ,按4字节算,不算二级页表中的权限位
一共有1024行,也就是 4 x 1024 = 4096byte = 4kb
则一个页表是可以放进一个页框里面的

一级页表中一共有 1024个二级页表,所以所有页表大小: 1024 x 4kb = 4096 kb = 4MB
一级页表就一个4KB 没算进去
所以说这1024 个二级页表 4MB 和之前40G的大表相比 少了很多

一个进程会把整个地址空间全部用完吗?一部分地址空间根本不需要给每个进程都维护的,内核级页表只需要维护一份就行了。
每个进程只需要维护0-3G

每个进程不一定把整个地址空间全用完,二级页表不一定全部存在。
二级页表在大部分情况下都是不全的!
这样算就比4MB还小,即便是进程把页表用全了,还有页面置换算法来维护
所以极端情况是4MB,但是大部分情况进程只会用到其中很少一部分,所以进程的页表就大大减少了

页表不会很大架不住进程个数多,所以我们说创建一个进程依旧是一个很 “重” 的工作,所以才有线程存在的意义和背景。


二级页表保存的页框地址个数 和 物理内存的页框个数对着呢吗?
对着呢
这是物理内存一共有1048576 = 2 ^ 20个页框,从下往上你就算吧
在这里插入图片描述
一共有1024个二级页表,每个二级页表保存了1024个地址,也就是1024 X 1024 = 1048576 个地址


虚拟地址整个10 10 12划分 ,它为什么这么划分呢? 这个12为什么要有呢?
答:
根本原因就在于配合内存管理
今天页框大小是4KB,有的OS把页框干成4MB,最终页表还会更小
因为物理内存分的页框个数少了,要保存的地址也就少了,页表也就小了

它为什么是4KB呢,因为它和12是相对应的!


所以要访问一个虚拟地址时,OS怎么知道这个虚拟地址有没有加载到内存呢?
答:
1.可能你查一级页表时二级页表不存在,那就没有被加载到内存,所以缺页中断

2.可能你访问二级页表里和对应的页框并没有建立映射关系,此时也没有加载到内存,二级页表里面有标记位确认映射关系有没有。


因为二级页表只能索引到页框,所以内存管理的基本单位是4KB


你现在虚拟转物理只能找到一个字节的地址,那我们的int, double
float,各种自定义类型 怎么说?
在这里插入图片描述

int a = 10;
整形有4个字节,&a只拿到了一个地址,用它的低地址做代表
C/C++中任何变量只有一个地址就是内存中开辟的多字节起始地址
找到这个起始地址,根据类型连续读取多个字节就把数据读取上来了。

计算机他怎么知道我要读取几字节?
类型被CPU转化成偏移量,类型是给cpu看的,
汇编中内置了命令读取1,2字节的命令,dword ,word字
CPU和内存连着呢(冯诺依曼),软件定位到了内存中起始物理地址,CPU拷贝时它就知道拷贝几字节了

你说的是内置类型,我要是结构体,类呢?
结构体,类不都是由内置类型的集合描述的。
就算结构体很大,CPU很小,我就能读多少读多少

C/C++中任何变量只有一个地址就是内存中开辟的多字节起始地址
说白了我们访问任何一个变量都叫做起始地址+类型 本质就是 起始地址 + 偏移量


所以最终一句话
虚拟地址到物理地址的转化 它只需要查10 10 12 找到页框再在页框内索引
至此就完成了虚拟到物理的转化

CPU内部的CR3寄存器直接指向一级页表,任何进程二级页表可以没有或残缺,但任何进程必须要有一级页表存在!后面随着运行过程页表缺页中断会被填充越来越完善。

当物理地址访问时,物理地址不存在 or 越界了
CPU内部还有一个寄存器叫做CR2 保存 引起缺页中断 or 异常的虚拟地址
相当于你进行访问二级页表,而二级页表不存在,不存在你说要缺页中断 把页面调换进来在内存里申请构建映射关系,你把这些做完了我怎么知道上次访问的虚拟地址是谁呢?
当它把这个工作做完就会把CR2保存的虚拟地址拿出来重新访问。


下面在回答一下最开始的问题
如何理解资源分配?
上面所讲的这个线程,它所对应的所有的资源分配全部都是通过地址空间来的,而你所有的代码和数据都是通过我们的地址空间页表然后映射过来的,
那么现在我的问题,我们线程分配资源在地址空间角度
线程目前分配资源 , 本质就是分配地址空间范围 ,页表你就别动了。
页表,物理内存都给我分配好了,一个线程它 要分配资源,目前站在地址空间角度,因为地址空间本身就是资源窗口,线程分配资源本质就是把地址空间划分一部分,就是你用你的,我用我的,比如说把代码分成几部分给几个线程,凡是不划分的比如堆区,全局数据区所有线程全部共享
所以线程资源分配本质就是空间范围的分配,因为所有的线程也共同属于同一个进程,大家使用同一个页表映射,查同一个页表,换句话说,
你把哪一部分资源给这个线程,其实就是把对应代码范围给它就可以了

那这个划分工作难不难呢?
以代码为例,全局数据不需要划分,就是要被所有线程共享
剩下的大部分区域线程都能共享
最重要的是代码,你怎么让每一个线程执行不同的代码?
代码有地址吗?
有,所以才有函数指针的概念。代码的地址也是虚拟地址
我们可以定义上10个函数,每个函数地址都不一样,所以把一个函数交给一个线程运行它天然就在代码层面上已经做好了地址空间划分上的分开。
10个函数每一个函数内部所有地址都是互相独立的,所以把每个函数给每个线程去跑,天然在函数上就划分好了。这就叫线程之间代码就分离了
你做了什么呢?
你只需要在代码中编译,编译好让所有线程执行不同函数就行了
所以线程就跑起来了

4.Linux线程周边的概念

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

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

相关文章

基于51单片机的交通灯_紧急开关+黄灯倒计时+可调时间

51单片机交通灯_紧急开关黄灯倒计时可调时间 开题报告系统硬件设计主控制器选择系统硬件结构图时钟及复位电路指示灯及倒计时模块 倒计时模块&#xff1a;程序软件主流程框架main函数 设计报告资料清单资料下载链接 基于51单片机交通灯_紧急开关黄灯倒计时可调时间 仿真图prote…

题目:神奇的进制

解题思路&#xff1a; 用电脑自带的计算器&#xff0c;切换到程序员模式。里面有进制转换功能。 由题目&#xff0c;要求严格递增且都为字母&#xff0c;还要大于2023&#xff0c;则数字16进制为ABC。

【STM32】电机驱动

一、电机分类 二、直流电机的分类 1.有刷电机 2.无刷电机 3.直流减速电机 三、H桥电路 正向旋转 驱动Q1和Q4 反向旋转 驱动Q2和Q3 四、MC3386电机驱动芯片 1.基本原理图 1&#xff09;前进/后退&#xff1a;IN1和IN2的电平顺序决定电机的正反转 2&#xff09;调节速度&#…

Redis对象

Redis根据基本数据结构构建了自己的一套对象系统。主要包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象 同时不同的对象都有属于自己的一些特定的redis指令集&#xff0c;而且每种对象也包括多种编码类型&#xff0c;和实现方式。 Redis对象结构 struct redisOb…

Linux 防火墙

目录 安全技术 防火墙的分类 按保护范围划分 按实现方式划分 按网络协议划分 应用层防火墙&#xff08;7层&#xff09; 防火墙的工作原理 linux防火墙的基本认识 防火墙工具介绍 1.iptables 2.firewalld 3.nftables 安全技术 —— 入侵检测系统&#xff08;Intru…

centos7中通过kubeadmin安装k8s集群

k8s部署官方提供了kind、minikube、kubeadmin等多种安装方式。 其中minikube安装在之前的文章中已经介绍过&#xff0c;部署比较简单。下面介绍通过kubeadmin部署k8s集群。 生产中提供了多种高可用方案&#xff1a; k8s官方文档 本文安装的是1.28.0版本。 建议去认真阅读一下…

使用coco数据集进行语义分割(1):数据预处理,制作ground truth

如何coco数据集进行目标检测的介绍已经有很多了&#xff0c;但是关于语义分割几乎没有。本文旨在说明如何处理 stuff_train2017.json stuff_val2017.json panoptic_train2017.json panoptic_val2017.json&#xff0c;将上面那些json中的dict转化为图片的label mask&am…

Docker下安装MySQL

如果在Docker下直接拉取MySQL并运行镜像&#xff0c;由于没有指定字符编码集&#xff0c;可能会存在插入中文出现乱码的情况&#xff0c;并且当容器删除后&#xff0c;容器里面存在的数据会丢失&#xff0c;所以在运行容器时应该使用数据卷进行挂载&#xff0c;按照如下步骤操作…

大模型中幂律缩放法则和涌现能力

幂律缩放法则是一种用于描述两个变量之间关系的数学模型。 根据幂律缩放法则&#xff0c;当一个变量的值变化时&#xff0c;另一个变量的值以指数方式变化。具体而言&#xff0c;幂律缩放法则可以表示为Y a * X^b&#xff0c;其中Y表示一个变量的值&#xff0c;X表示另一个变…

网页中的json文档,怎么保存到本地

火狐浏览器操作方法 方法一 右键——>另存页面为 方法二 点击右边的三条横线——>另存页面为 谷歌浏览器操作方法 方法一 右键——>另存为 方法二

阵列信号处理---均匀线阵和均匀加权线阵

均匀线阵 均匀线性阵列(ULA&#xff1a;Uniform Linear Array)&#xff1a;有N个阵元位于z轴上且具有均匀间距d。 一般都把阵列的中心放在坐标系的原点。如下图 阵元的位置为 p z n ( n − N − 1 2 ) d &#xff0c; n 0 , 1 , … , N − 1 p_{z_n}\big(n-\frac{N-1}{2}\b…

LLM 开发模式 RAG,MRKL,Re-Act,Plan-Execute 模式对比

本心、输入输出、结果 文章目录 LLM 开发模式 RAG&#xff0c;MRKL&#xff0c;Re-Act&#xff0c;Plan-Execute 模式对比前言RAG、MRKL、Re-Act和Plan-Execute模式的一些对比花有重开日&#xff0c;人无再少年实践是检验真理的唯一标准 LLM 开发模式 RAG&#xff0c;MRKL&…

Wireshark抓包分析RTMP协议时,出现Unknown问题

进行rtmp推流时&#xff0c;使用wireshark抓包&#xff0c;发现部分包显示Unknown 解决方法&#xff1a; 编辑 -> 首选项 -> Protocols -> RTMPT&#xff0c;这里Maximum packet size默认是32768 将该值调大&#xff0c;比如调成1048576&#xff0c;即可解决该问题。…

pytorch 中的dim 的作用范围

1. 二维矩阵时 不同的运算&#xff0c; dim 的作用域都是一样的思想&#xff1b; 当数据是二维矩阵时&#xff0c; 可以按照下面的思想理解&#xff1a; 对于矩阵&#xff1a; dim0 按列操作&#xff08;沿列向下&#xff09;。 dim1 按行操作&#xff08;跨行&#xff09;。 …

Windows修改MAC地址的方法(以windows11为例)

我们在日常的工作中&#xff0c;如果mac地址被限制&#xff0c;就需要修改mac地址&#xff0c;本文总结一下修改windows的mac地址的方法。 方法一&#xff1a;网络适配器中配置 网络适配器中配置的方式适用于能够在网络适配器中找到物理地址(NetworkAddress)的情况。 1、打开…

腾讯云轻量服务器通过Docker搭建外网可访问连接的redis5.x集群

原创/朱季谦 最近买了一台4核16的腾讯云轻量应用服务器,花了我快四百的大洋&#xff0c;打算搭建一堆docker组件集群&#xff0c;最先开始是通过docker搭建redis集群&#xff0c;计划使用三个端口&#xff0c;分别是7001,7002,7003。 腾讯云服务器有防火墙限制&#xff0c;故…

使用trigger-forward跨流水线传递参数

参考文档&#xff1a;https://docs.gitlab.com/ee/ci/yaml/#triggerforward 今天给大家介绍一个gitlab CI/CD的关键字 - forward&#xff0c;该关键字是一个比较偏的功能&#xff0c;但同时也是一个很实用的功能&#xff0c;我们通过在gitlab的ci文件中使用forward关键字&#…

火星探索:技术挑战与前沿进展

火星探索:技术挑战与前沿进展 一、引言 火星,这颗红色的星球,长久以来一直吸引着人类的目光。随着科技的飞速发展,火星探索已经从纯粹的科幻梦想逐渐转变为现实的研究课题。然而,火星探索仍然面临着诸多技术挑战。本文将深入探讨火星探索的关键技术、现有技术瓶颈以及前沿…

从零构建属于自己的GPT系列1:数据预处理(文本数据预处理、文本数据tokenizer、逐行代码解读)

&#x1f6a9;&#x1f6a9;&#x1f6a9;Hugging Face 实战系列 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在PyCharm中进行 本篇文章配套的代码资源已经上传 从零构建属于自己的GPT系列1&#xff1a;文本数据预处理 从零构建属于自己的GPT系列2&#xff1a;语…

记一次若依二开的简单流程

记一次若依二开的简单流程 前言: 搞Java后端的应该都知道若依框架&#xff0c;是一个十分强大且功能齐全的开源的快速开发平台&#xff0c;且毫无保留给个人及企业免费使用。很多中小型公司会直接在该系统上进行二次开发使用。本文记录一次使用若依二开零编码的简单实现&#…