【Linux】一文带你入门了解线程和虚拟地址空间中页表映射的秘密(内附手绘底层逻辑图 通俗易懂)
绪论
每日激励:“努力去做自己该做的,但是不要期待回报,不是付出了就会有回报的,做了就不要后悔,不做才后悔。—Jack”
绪论:
本章是LInux中非常重要的线程部分,通过了解线程的基本概念:线程到底是什么、进程和线程的关系、线程为什么叫轻量级进程、为什么要用线程(他的比较与进程的优点)…;当我们了解完线程后此次对虚拟地址空间进一步认识,它其中的一些细节页表到底是如何映射的找到物理内存中的正确位置的,后续还将持续更新Linux线程的更多知识,敬请期待~
————————
早关注不迷路,话不多说安全带系好,发车啦(建议电脑观看)。
思维导图:

1.线程的概念
- 线程是比进程更加轻量化的一种执行流
- 线程是在进程内部执行的一种执行流
- 线程是CPU调度的基本单位 / 进程是承担系统资源的基本实体
1.1解释线程的概念:

- 在Linux下线程本质就是进程,因为他们共用一套数据结构,线程不一样的是他并不用再次的申请资源,多线程和第一个创建的线程共用同一块资源(上图中线程都指向同一块地址空间!)
- TCB(Thread线程)结构体,线程的实现不同的平台不一样,Windows就单独实现了TCB,而Linux中线程的实现的TCB是复用了PCB(Process进程)的,Linux这是为了防止冗余(还要单独的实现一份TCP的数据结构以及系统调用的消耗)
- 线程是比进程更加轻量化的一种执行流:因为创建一个进程的成本是比较大的(地址空间,页表,状态设置,io的创建各种数据结构),而线程他们共用同一份资源(同一份地址空间),也就是不用再次创建各种数据结构和申请各种资源的,所以称他是轻量化的。
- 线程优点及原理:将原本一个进程完成的工作分成多份,各个线程分工的来完成各自部分,最终全部完成,所以线程在地址空间间运行。
- 线程(本质是)不同于进程(进程 = 内核数据结构pcb + 数据和代码),现在它由多个执行流(多个pcb)组成,所以我们可以把这种线程称为轻量级进程,因为其一个线程并不能代表整个进程,整个进程为上图蓝色框内的所有组成(不同于之前的是他由多个执行流 + 数据和代码组成)。
- Linux中TCB和PCB是一样,所以CPU并不用区分PCB和TCB,并且CPU拿到的其实是TCP中的LWP(后面会细说,先了解)而非线程中PCB的PID来 找到每个轻量级进程(PCB本质就是单独只有一个进程,它能理解为只有一个执行流的线程,其中第一个执行流被称为主线程,他的LWP和PID是一样的,所以就表示我们之前学的其实也没错,通过PID也能控制各个进程(因为虽然CPU控制的是LWP,但一个进程只有一个线程它的LWP就和PID相同,所以是一样的概念))。
- 线程是CPU调度的基本单位 / 进程是承担系统资源的基本实体:因为进程是分成多份的一份份线程,故CPU的调度时就会一份份的调度;进程整体能通过进程地址空间和页表找到所需的资源所以有进程是承担系统资源的基本实体。
- 一个进程里若有多个执行流,那么也就是线程,其中这些每个线程的pid是相同的
- 虽然线程TCB是进程PCP的一部分(相当于共用一个),但注意的是每个线程都有属于自己的TCB进行管理
可以想象成理解成:相当于社会上,都是以每个家庭为单位(一个个进程),每个家庭都有多个成员(相当于线程),并且所有家庭成员的工作都是为了总体(一个目的),只有当我们每个家庭成员都做好对应的事(每个线程做好事),才能让家庭过好(进程正常执行)。(而之前的进程相当于一个家庭只有一口人)
1.2 线程是属于一个进程的多个执行流:
原理:
通过函数pthread_create()创建线程,查看他们pid是否相同即可
#include<pthread.h>
//新线程
void *ThreadRoutine(void* arg)
{const char* threadname = (const char*)arg;while(true){cout << "I am a new thread" << threadname << ", pid:" << getpid() << endl;sleep(1);}
}
int main()
{//执行线程前已经有进程了!pthread_t tid;//创建线程并执行ThreadRoutine函数,后面的是传进去的参数pthread_create(&tid,nullptr,ThreadRoutine,(void*)"thread 1");//thread 线程tid,atttr 设置的线程属性,//start_routine 函数指针(传一个函数)//arg前面函数指针的参数//主线程,线程执行的同时 主线程会继续往后执行!while(true){cout << "I am main thread" << ", pid:" << getpid()<< endl;sleep(1);}return 0;
}
他们的pid相同,并且用ps ajx查看也进程也只有一个,所以就证明了他们是在同一个进程中的不同线程。

查看指定进程指令
ps ajx | grep process
查看进程并过滤出含process的进程,发现确实只有一个进程在运行:

查看所有轻量级进程指令:
ps -aL(all light)
查看发现此时有两个线程,也就对应了一个主线程和一个刚创建的新线程:

LWP:Light Weight Processes也就是轻量级进程,他就像进程的PID一样来区别不同线程!
CPU调度时本质看的是LWP(而不是PID),其中主线程他的PID = LWP,所以上图的第一个线程就是主线程
总结:
线程是CPU调度的基本单位,在Linux内核中它的结构复用了进程PCB,让线程复用进程的代码,所以多线程共用一个资源。Linux中所有的线程又叫做轻量级进程。若要谈进程那就不能只谈pcb还有进程地址空间和页表,谈执行流那就都是轻量级进程(线程),当进程内只有一个执行流就是进程,若有多个执行流就是线程,其中每个线程指向同一个地址空间让数据资源共享,并且通过划分代码给到各个线程来执行不同代码。
1.3 线程比进程更轻量化
- 从CPU调度上看:
- 线程间切换:地址空间和页表(会有寄存器存着他们的位置,指向他们)不用切换,只用切换产生的临时数据的寄存器。
- 进程切换:所有寄存器都要切换,页表,上下文保存,…。
- 从线程上看:
- CPU内有硬件级别的cache缓存,他的作用是把正在执行的代码的附近代码先缓存进cache中,原因一般数据正在访问某一行代码,此时较大可能会访问这一行附近的代码(称为局部性原理),所以会把附近的代码先缓存进cache(预加载),方便后续继续用,把存在cache中的代码/数据叫做热数据。
- 线程切换时就不用切换cache(里面的数据可能还有用)
- 进程间就要切换cache(一个进程的代码对另外一个进程无意义)

所以线程切换效率高是因为:
- 切换寄存器少
- 不用更新cache
假如一个进程的分配10ms的时间片,此时线程会瓜分这些时间片(时间片也是资源)
因为每个线程都分配了一定的时间片,所以调度时当把这些线程的时间片都用完后才算进程调度完。
1.4 线程的优点:
- 创建、调度、释放量级比进程轻。
- 创建成本低
- 并行进行多种任务的处理处理(进程也有)
附:
进程分为:
- 计算密集型应用(计算分为多份)
- IO密集型应用(下载时多线程下载)
1.5 线程的缺点:
- 线程的切换:性能损失
- 健壮性降低(鲁棒性):多线程程序,当一个线程崩溃会导致全部线程都崩溃(就好比一个团队一人做的事情就相当于整个团队的事情)
- 缺乏访问控制:线程间的资源可能会因为共享而出现问题
1.6 线程的用途:
- 合理的使用多线程,能提高CPU密集型程序的执行效率
- 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)
1.7 TCP和PCB的关系
线程除了地址空间共享外还有共享:
- 文件描述符
- 每种信号的处理方式(handler,每个线程有独立的tcb,但block和pending不是私有的)
- cwd表示当前所在目录的字符串(进程启动时tcb会记录)
线程他也有自己的私有成员:
- 线程ID(LWP)
- 调度优先级(每个线程被单独调用)
- 一组寄存器:线程有独立的上下文数据(动态切换)
- 独立的栈结构(函数中,动态运行)
2.重谈地址空间(虚拟地址 ->物理地址)
回顾之前文件系统IO他的基本单位(最小单位)大小:4kb(文件块)
操作系统文件系统 维护和管理 磁盘打开与加载文件到物理内存中运行。

-
物理内存和磁盘进行交互的基本单位是4kb,所以文件系统上看到的可执行程序内的数据都是一块块的4kb,对此物理内存上也是分成了一块块4kb的段,他们就像杯子与盒子必须大小适配,其中物理内存所分成的一块块数据称为页框,而可执行程序的称为页帧,所以物理内存分成的块和文件块都指定是4kb。
-
物理内存中其实是有无数个小的存储01的高低硬件电路,用来通过充放电的方式来存储或删除数据(但他的前提是有电,当掉电他就会丢失内存里的数据)。
-
一个页框的大小为4kb,那就会有32个INode文件(一个INode文件大小为128byte,1024 * 4 / 128 = 32)
-
物理内存的空间是4GB时会有1048,576(102410241024 * 4 / 4*1024)个页框
其中这里有点混乱,但只需要你始终保持区分 物理内存(页框) 和 磁盘空间(页帧) 即可更好的理解
而这些直接分出来的一块块数据区域(页框,页帧):
页框可以描述成结构体:
struct page
{//描述page的使用情况 int flag; 定义宏来描述其是否使用:#define unuse 0x1//page的属性
}
通过一个数组的形式来进行管理,这样形成一个数组,这样对内存的管理,就变成了对数组的管理。
struct page pages[1048576]

2.1页表的原理
页表的作用是用于将虚拟地址空间通过映射找到真正在物理内存上的空间的,之前我们把页表想象成一张类似哈希表的结构,左边是虚拟地址右边映射物理地址,但是实际我们算算就发现是不行的,一个页表存在两个地址那就8byte在加上一些标志位那么就算一行(组)是10byte,而我们32位机上会有2^32个地址,那么页表就需要有2 ^ 32个行每一行是10字节,那么一个页表就非常的大了,所以他是不完善不合理的。
对此页表存的虚拟地址其实是是分比特位来使用的,其中前20位用来找到正确的物理地址中的页框。
其中20位又分成
- 前10位 对应着页目录:存着页表项数组下标(用于从页框结构数组中找到正确的位置)
- 后10位 对应着页表项:存的就是页框的起始地
- 页目录、页表项本质它们都是数组
- 后12位用来找到页框中的准确位置(相当于找到了页框后地址需要确定偏移量找到具体位置)(12位 = 2 ^ 2 * 2 ^ 10 = 4kb,因为刚好是4kb所以就能找到页框中的所有数据)
- 设计成这样我们前20位所找到的页框(4kb),而非字节这样就能很大的缩小了所要的空间。

所以虚拟地址 -> 物理地址:前20比特位找到数据所在页框的起始地址 + 后12个比特位找到数据具体地址
页表中的标记位(物理地址旁边的3列)

- 访问期间若发现目标资源不在内存,则会触发缺页中断,再次进行内存的分配,并建立新映射。
- U/K权限:U表示当前是User用户态,K表示当前是Kernel内核态。
本章完。预知后事如何,暂听下回分解。
如果有任何问题欢迎讨论哈!
如果觉得这篇文章对你有所帮助的话点点赞吧!
持续更新大量Linux细致内容,早关注不迷路。
相关文章:
【Linux】一文带你入门了解线程和虚拟地址空间中页表映射的秘密(内附手绘底层逻辑图 通俗易懂)
绪论 每日激励:“努力去做自己该做的,但是不要期待回报,不是付出了就会有回报的,做了就不要后悔,不做才后悔。—Jack” 绪论: 本章是LInux中非常重要的线程部分,通过了解线程的基本概念&am…...
js面试some和every的区别
1.基础使用 some和every 都是数组的一个方法let num [1,2,3,4,5,6] let flag1 num.some((item,index,array)> item > 2)let flag2 num.every((item,index, array)> item > 2)1.some 遍历判断中是符合条件的值 一旦找到则不会继续迭代下去 直接返回 2.every 遍历…...
缓存类为啥使用 unordered_map 而不是 map
性能考虑: std::unordered_map 是基于哈希表实现的,而 std::map 是基于红黑树实现的。对于查找操作,std::unordered_map 的平均查找时间复杂度是 O ( 1 ) O(1) O(1),而 std::map 的查找时间复杂度是 O ( l o g n ) O(log n) O(l…...
ollama linux下载
实验室服务器(A6000)执行curl -fsSL https://ollama.com/install.sh | sh太慢了。 而sudo snap install ollama,容易爆cudalibrt.so12无法正常使用的bug。 发现 https://www.modelscope.cn/models/modelscope/ollama-linux 使用modelscope进…...
k8s服务发现有哪些方式?
在 Kubernetes 中,服务发现是指如何让应用程序在集群内互相找到并通信。Kubernetes 提供了多种服务发现的方式,适应不同的使用场景。以下是 Kubernetes 中常见的服务发现方式: 1. 环境变量(Environment Variables) 概…...
Flash Attention与Attention
原始Attention是: Flash Attention: 伪代码:4d(分别代表Q\K\V\O) Flash Attention2优化了...
vue 使用fetch-event-source 处理sse,实现ChatGpt逐字输出效果
1. 安装 npm install microsoft/fetch-event-source 2. 引用 import { fetchEventSource } from "microsoft/fetch-event-source"; 3. 使用 fetchEventSource(/api/chat, { method: POST,headers: {Content-Type: application/json,Accept: */*,Token: this.toke…...
JAVA进阶之线程
为神马有线程?这玩意儿在干嘛??? 回答这个问题,就先要知道一点点计算机的工作方式。 总所周知,计算机有五部分:输入输出、计算器、存储器、控制器。而在计算机内,CPU、内存、I/O之…...
机器学习专业毕设选题推荐合集 人工智能
目录 前言 毕设选题 开题指导建议 更多精选选题 选题帮助 最后 前言 大家好,这里是海浪学长毕设专题! 大四是整个大学期间最忙碌的时光,一边要忙着准备考研、考公、考教资或者实习为毕业后面临的升学就业做准备,一边要为毕业设计耗费大量精力。学长给大家整理…...
C++ 中的 `string` 类型:全面解析与高效操作
C 中的 string 类型:全面解析与高效操作 在 C 中,string 类型是对字符数组的高级封装,它提供了大量内置函数,使得字符串的处理变得更为简便和高效。与 C 风格的字符数组不同,string 类型不仅自动管理内存,…...
go语言中的Stringer的使用
Go 语言中的 Stringer 是一个非常有用的接口,它在标准库的 fmt 包中定义。Stringer 接口允许类型定义它们的字符串表示方式,这在格式化输出时特别有用。让我们深入了解一下: Stringer 接口定义: type Stringer interface {Strin…...
Java入门进阶
文章目录 1、常用API 1.1、Math1.2、System1.3、Object1.4、Arrays1.5、基本类型包装类 1.5.1、基本类型包装类概述1.5.2、Integer1.5.3、int和String相互转换1.5.4、自动装箱和拆箱 1.6、日期类 1.6.1、Date类1.6.2、SimpleDateFormat类 1.6.2.1、格式化(从Date到…...
【大数据技术】搭建完全分布式高可用大数据集群(Scala+Spark)
搭建完全分布式高可用大数据集群(Scala+Spark) scala-2.13.16.tgzspark-3.5.4-bin-without-hadoop.tgz注:请在阅读本篇文章前,将以上资源下载下来。 写在前面 本文主要介绍搭建完全分布式高可用集群Spark的详细步骤。 注意: 统一约定将软件安装包存放于虚拟机的/softwa…...
使用vLLM部署Qwen2.5-VL-7B-Instruct模型的详细指南
使用vLLM部署Qwen2.5-VL-7B-Instruct模型的详细指南 引言环境搭建安装vLLM安装依赖库下载模型启动vLLM服务器总结参考 引言 近年来,随着大规模语言模型(LLM)的快速发展,如何高效地进行模型推理成为了一个热门话题。vLLM作为一个专…...
AWS门店人流量数据分析项目的设计与实现
这是一个AWS的数据分析项目,关于快消公司门店手机各个门店进店人流量和各个产品柜台前逗留时间(利用IoT设备采集)和销售数据之间的统计分析,必须用到但不限于Amazon Kensis Data Stream,Spark Streaming,Sp…...
C#结合html2canvas生成切割图片并导出到PDF
目录 需求 开发运行环境 实现 生成HTML范例片断 HTML元素转BASE64 BASE64转图片 切割长图片 生成PDF文件 小结 需求 html2canvas 是一个 JavaScript 库,它可以把任意一个网页中的元素(包括整个网页)绘制到指定的 canvas 中…...
485网关数据收发测试
目录 1.UDP SERVER数据收发测试 使用产品: || ZQWL-GW1600NM 产品||【智嵌物联】智能网关型串口服务器 1.UDP SERVER数据收发测试 A(TX)连接RX B(RX)连接TX 打开1个网络调试助手,模拟用户的UDP客户端设…...
InnoDB和MyISAM的比较、水平切分和垂直切分、主从复制中涉及的三个线程、主从同步的延迟产生和解决
InnoDB和MyISAM的比较 事务支持: InnoDB支持:支持事务 (ACID 属性)。支持 Commit、Rollback 和 Savepoint 操作。适合需要事务处理的应用,例如银行系统。MyISAM:不支持事务。每次操作都是自动提交,不能回滚或中止。适合对事务要求…...
JDK9新特性
文章目录 新特性:1.模块化系统使用模块化module-info.java:exports:opens:requires:provides:uses: 2.JShell启动Jshell执行计算定义变量定义方法定义类帮助命令查看定义的变量:/var…...
基于Ubuntu2404搭建Zabbix7.2
Zabbix 搭建zabbix zabbix7.2已推出:官网 增加的新功能如下: 1.使用新的热门商品小部件全面概览指标 数据概览小部件已转换为热门项目小部件使用项目模式可以实现细粒度的项目选择利用条形图、指标和迷你图来可视化您的数据定义价值阈值以动态地可视化…...
Math Reference Notes: 符号函数
1. 符号函数的定义 符号函数(Sign Function) sgn ( x ) \text{sgn}(x) sgn(x) 是一个将实数 ( x ) 映射为其 符号值(即正数、负数或零)的函数。 它的定义如下: sgn ( x ) { 1 如果 x > 0 0 如果 x 0 − 1 如…...
【数据结构】链表应用-链表重新排序
重新排序 反转链表预期实现思路解题过程code力扣代码核心代码完整代码 总结 删除链表中间节点代码解惑 链表重新排序题目描述解题思路解题过程复杂度代码力扣代码完整代码 反转链表 预期实现 思路 你选用何种方法解题? 我选用了迭代法来反转链表。这是一种经典且高…...
学习threejs,pvr格式图片文件贴图
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️PVR贴图1.2 ☘️THREE.Mesh…...
数据库开发常识(10.6)——SQL性能判断标准及索引误区(1)
10.6. 数据库开发常识 作为一名专业数据库开发人员,不但需要掌握数据库开发相关的语法和功能实现,还要掌握专业数据库开发的常识。这样,才能在保量完成工作任务的同时,也保质的完成工作任务,避免了为应用的日后维护埋…...
2022年全国职业院校技能大赛网络系统管理赛项模块A:网络构建(样题2)-网络部分解析-附详细代码
目录 附录1:拓扑图编辑 附录2:地址规划表 1.SW1 2.SW2 3.SW3 4.SW4 5.SW5 6.SW6 7.SW7 8.R1 9.R2 10.R3 11.AC1 12.AC2 13.EG1 14.EG2 15.AP2 16.AP3 附录1:拓扑图 附录2:地址规划表...
100.7 AI量化面试题:如何利用新闻文本数据构建交易信号?
目录 0. 承前1. 解题思路1.1 数据处理维度1.2 分析模型维度1.3 信号构建维度 2. 新闻数据获取与预处理2.1 数据获取接口2.2 文本预处理 3. 情感分析与事件抽取3.1 情感分析模型3.2 事件抽取 4. 信号生成与优化4.1 信号构建4.2 信号优化 5. 策略实现与回测5.1 策略实现 6. 回答话…...
【前端】【Ts】【知识点总结】TypeScript知识总结
一、总体概述 TypeScript 是 JavaScript 的超集,主要通过静态类型检查和丰富的类型系统来提高代码的健壮性和可维护性。它涵盖了从基础数据类型到高级类型、从函数与对象的类型定义到类、接口、泛型、模块化及装饰器等众多知识点。掌握这些内容有助于编写更清晰、结…...
【前端】【Ts】TypeScript的关键知识点
一、知识点总结 (一)void 与 never 的区别 (1) void:声明函数无返回值,但可以走到 return 行。(2) never:表示函数不会走到 return 行,常用于抛异常或无限循环。 (二)字面量类型与联…...
C++,STL,【目录篇】
文章目录 一、简介二、内容提纲第一部分:STL 概述第二部分:STL 容器第三部分:STL 迭代器第四部分:STL 算法第五部分:STL 函数对象第六部分:STL 高级主题第七部分:STL 实战应用 三、写作风格四、…...
2502vim,vim文本对象中文文档
介绍 文本块用户(textobj-user)是一个可帮助你毫不费力地创建自己的文本对象的Vim插件. 因为有许多陷阱需要处理,很难创建文本对象.此插件隐藏了此类细节,并提供了声明式定义文本对象的方法. 你可用正则式来定义简单的文本对象,或使用函数来定义复杂的文本对象.如… 文本对…...

