当前位置: 首页 > article >正文

Linux——线程(1)线程概念与控制

线程?这个名字我们似乎有些眼熟?没错,我们之前提到过的进程和这个有点像。但进程和线程有什么关系呢?本系列我们讲从线程的概念出发,了解一下Linux中的线程以及线程和进程的关系等内容。

一、线程的概念

线程是一个执行流,执行力度比进程要更加细,是进程内部的一个执行分支,是进程中实际运作单位。也就是说,一个进程可以有多个线程,那么操作系统如果要支持线程,就必须对当前的线程进行管理!——先描述再组织。进程对应的我们有PCB,线程我们有TCB。 

但是我们一想,一个进程有一个PCB,但同时会有多个tcb,一个task_struct要连接这么多的结构,是不是有些太麻烦了?因此,操作系统直接把pcb看成是tcb,也就是说,当我们的进程创建线程时与进程的task_struct共享一份地址空间,然后把代码区分成若干份分别指向不同的线程执行。

也就是说,线程是靠进程模拟实现的。我们把图片左侧的task_struct和红框部分的线程统一称为执行流,其中一个执行流我们称线程,只有task_struct+地址空间+页表称为进程。我们之前的进程系列的进程也算是进程,只不过只有一个执行分支。(线程)在Linux中,我们把执行流统一称为轻量级进程(LWP)。

二、线程的基本模拟

上图是线程创建的接口(稍后会对参数进行解释)

以下是演示代码

执行流程是,创建了新线程后,之前的执行流会继续向下执行,而新创建的执行流会去执行上面的run函数。但我们编译后发现居然发生了链接报错。

其实,pthread_create并不是一个系统调用,而是glibc封装的一个原生线程库,解决这个问题我们只需要在编译选项后加 -lpthread即可

运行结果也的确像我们说的

我们通过get两个线程的pid发现与进程的pid相同,也说明了来自于同一个进程

除此之外,我们也可以查询线程的状态,进程的状态我们说过用 ps axj |  grep processname

查询线程的状态:
 

ps -aL | grep processname

在众多的执行流中,pid与LWP相同的是主线程。

我们讲过,线程没必要再创建一个tcb,只要和进程共享一个pcb即可,那么也就不需要给线程执行的代码开辟额外空间,只需要把要执行的函数起始地址交给某执行流即可(函数还是在pcb内)

三、进程与线程

进程是资源分配的基本单位,而线程是调度的基本单位

此外,线程虽然共享进程的数据,但也拥有自己的一部分数据,比如线程id,上下文,调度优先级等。

在文件方面,同一进程的多个线程是共享的,也就是说如果一个线程打开了某个文件,那么其他线程也能看到该文件的内容。包括信号的处理方式等。

在一个进程中,如果有一个线程发生了崩溃,那么整个进程也将崩溃。

与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。

另外⼀个隐藏的损耗是上下文的切换会扰乱处理器的缓存机制。简单的说,⼀旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。还有⼀个显著的区别是当你改变虚拟内存空间的时候,处理的页表缓冲 TLB (快表)会被全部刷新,这将导致内存的访问在⼀ 段时间内相当的低效。但是在线程的切换中,不会出现这个问题,当然还有硬件cache。cache位于CPU用于记录当前位置的附近的数据(也是缓存)。

四、关于虚拟地址与页表的补充

1.虚拟地址和页表的由来

如果没有这两个的分配内存机制,试想一下,我们每一个用户在使用内存时在空间上必定是连续的

因为每个程序的代码的数据大小不同,所以对应所占的空间大小不同,因此,物理内存会被分成若干个大小不同的块(如上图),导致相同的数据类型放在不同的各个区域。此时如果有些程序要推出的话,所占的内存就会被回收,导致这些内存被碎片化。

我们希望改变这种现状,但又不想改变用户使用空间连续的习惯(即用户的空间可以连续但物理内存不要连续),所以才有了虚拟地址空间和页表

图中有一个名词:页框,其本意就是把物理内存按照固定大小进行分隔的块,最常见的是4KB,而每个页框中存放一个页,大小就是一个页框的大小。(页是数据块,页框是一个存储区域)。这种机制就保证了CPU并非直接访问物理内存,而是通过虚拟地址来间接访问,也就是通过页表,页表上记录了页与页框的对应关系。

2.物理内存的管理

现在我们知道了,一大块物理内存被分割成无数的页(数据块),我们假设每个大小是4KB,假设整个内存是4GB,那么就要有4GB/4KB=100w+个页框。这么多个页框的内存,OS必定要管理,那么就会有对应的结构体来描述组织。而在结构内部我们发现我们发现有类似与数组下标的结构struct page *mem_map[N],可以大胆猜测,虽然物理内存被分割成无数块,但实际上他们还是连续的,那么我们就可以把每一个页框标记一个数组下标,整个内存就是一个巨大的数组,要想找到对应的内存,页表内只需记录映射到内存的下标就可以了!

3.页表的真实面目

目前我们对页表的了解,也就只知道它可以帮助我们映射到物理内存,但其内部还存放着其他信息。

我们之前了解的页表结构并不严谨,试想一下,如果把4GB的物理内存分成若干个4KB的块,然后把虚拟地址的每一块(每个地址是4字节)都记录在内形成映射关系以及对应的物理内存,那么单看页表的大小就要达到16GB左右了,这显然是不合理的。

其实,从虚拟地址到物理地址的转化并不只有页表,还有一个页目录,页目录中有1024个页目录表项,而每个表项对应着一个页表,然后每个页表中有1024个页表项,每个页表项指向物理地址的每一块起始地址,(所有页表的大小就是1024*1024*4=4MB),这样我们用4MB大小就可以映射到整个物理内存了

这里的每⼀个表,就是真正的页表,所以⼀共有 1024 个页表。⼀个页表自身占用 4KB,那么1024个页表就占用4MB的物理内存空间,和之前没差别啊?那么 从总数上看是这样,但是⼀个应用程序是不可能完全使用全部的4GB空间的,也许只要几十个页表就 可以了。例如:一个用户程序的代码段、数据段、栈段,⼀共就需要 10 MB 的空间,那么使用 3 个页表就足够了。 计算过程: 每⼀个页表项指向⼀个4KB的物理页,那么⼀个页表中1024个页表项,⼀共能覆盖4MB的物理内存; 那么10MB的程序,向上对齐取整之后(4MB的倍数,就是12MB),就需要3个页表就可以了。

把1024个页表管理起来的就是页目录,每个页目录表项的大小也是4字节。

4.虚拟地址如何转化为物理地址

我们的虚拟地址,一般是由32个比特位组成的,代表要访问某一个字节的地址(不是块的地址),我们把前10个比特位去查页目录表项(1024个,拿下标去查找到对应的页表),再拿次10位在对应的页表下找对应的页表项,然后我们就找到了页框的起始地址。而剩下的12位就是偏移量,利用偏移量就能找到该块中的某一个字节(2的12次就是4096,每个块是4kb*1024=4096字节)。

这个转化过程是MMU硬件完成的(CPU中)。然后再通过CR3寄存器查表找到对应的物理地址。

多级页表给我提供了便利的同时也引入了新的问题——查找次数增多导致效率降低,需要提升效率。在CPU中,还真有一个东西帮助我们——TLB,MMU在查页表前先问问TLB有没有,如果有就直接拿到物理地址,但没有的话就只能查表,然后把地址缓存到TLB方便下次查询,TLB的本质就是缓存。

5.缺页中断

我们的页表中记录的地址其实还记录了有关地址权限的相关信息(RW),假设现在有一个只读的地址,我此时进行写操作,此时MMU在查询地址时就发现不对,就会把错误告诉CPU发生软中断进行错误处理。同时,我们的数据并不用同时加载到内存里,其实一次加载一定部分就可以,等要执行下面的内容再进行加载(也说明了页表虽多但不都用)。

五、线程的控制

1.接口介绍

(1)pthread_create

上面提到过,这并不是系统调用而是一个库,是用户级别的线程库。

参数 :

thread: 返回线程 ID   (输出型)

attr: 设置线程的属性, attr 为 NULL 表示使用默认属性

start_routine: 是个函数地址,线程启动后要执行的函数

arg: 传给线程启动函数的参数

arg参数可以是任意类型(变量,数字,对象等) 

成功返回0,失败返回错误码。 

(2)pthread_self

用于获取tid,哪个线程调用就获取哪个tid。

我们也可以同时多个线程执行同一个函数,但如果不加保护的情况下,会发生数据错乱(类似于多态??)此外,进程内的函数,全局变量,线程也是共享的。

(3)线程等待——pthread_join

等待哪个线程就输入哪个LWP,至于第二个参数,是一个二级指针(输出型),原理和进程类似,只要等待线程不退出,主线程就会阻塞等待。(返回值等于0等待成功)

第二个参数一般用于接收线程执行的函数返回值,既然是一个二级指针,我们就需要用一个void*的变量地址传参。

相当于把10写进了ret。(我们要把return的值强转位void*)

(4) 线程终止

exit():其实在此并不常用,因为exit放在任何位置,只要触发就表示进程退出,所以如果只想让某一线程退出就不能使用这个。用pthread_exit。

等价于return。

还有一种退出方法

线程可以被取消

2.线程分离

当主线程正在做某些事情时,某个线程要退出,就需要阻塞等待,但如果我们要让主线程做自己的 事情不用等待呢?——把目标线程进行分离(joined:默认要等待,detach:分离态)

接口:pthread_detach(pthread_t thread)

用法通俗易懂,有一点,线程把自己分离需要传的是pthread_self()。一般要分离的线程是我不关心线程的返回值,不需要去等待,等线程退出自动释放资源。

 六、关于线程ID

获取线程id可以用接口也可以用输出型参数带出,我们发现线程ID都是很大的一串数字,我们把其转成16进制就是这样的:

有点眼熟?好像地址啊,的确就是地址!那线程的ID为什么要是地址呢,这个地址的意义在哪里?

我们知道,一个带线程的可执行程序是要第三方库线程库支持的,那么当程序执行时库也要加载(加载到内存空间的共享区以让task_struct看见)。

但对于Linux,没有线程这个概念,只有统一的LWP,用户要用线程,但系统只有创建LWP的接口,库中给我们提供相关线程的接口,同时我们也要获取线程的相关信息(id,优先级等),这就需要库中给我们维护,当我们创建一个线程就可以填充相关属性。用库进行封装,我们就可以避开系统调用,直接去库找就可以了。

库中的每一个线程的tcb可以看成是数组存放

相关文章:

Linux——线程(1)线程概念与控制

线程?这个名字我们似乎有些眼熟?没错,我们之前提到过的进程和这个有点像。但进程和线程有什么关系呢?本系列我们讲从线程的概念出发,了解一下Linux中的线程以及线程和进程的关系等内容。 一、线程的概念 线程是一个执…...

Redis LFU 策略参数配置指南

一、基础配置步骤‌ 设置内存上限‌ 在 redis.conf 配置文件中添加以下指令,限制 Redis 最大内存使用量(例如设置为 4GB): maxmemory 4gb选择 LFU 淘汰策略‌ 根据键的作用域选择策略: # 所有键参与淘汰 maxmemory-…...

备忘录模式:实现对象状态撤销与恢复的设计模式

备忘录模式:实现对象状态撤销与恢复的设计模式 一、模式核心:在不破坏封装性的前提下保存和恢复对象状态 在软件开发中,经常需要实现 “撤销” 功能(如文本编辑器的撤销修改、游戏存档读取)。直接暴露对象内部状态会…...

蓝桥杯 5. 交换瓶子

交换瓶子 原题目链接 题目描述 有 N 个瓶子,编号为 1 ~ N,放在架子上。 例如有 5 个瓶子,当前排列为: 2 1 3 5 4每次可以拿起 2 个瓶子,交换它们的位置。 要求通过若干次交换,使得瓶子的编号从小到大…...

本地使用Ollama部署DeepSeek

以下是在本地使用Ollama部署DeepSeek的详细教程,涵盖安装、修改安装目录、安装大模型以及删除大模型的操作步骤。 安装Ollama 1. 系统要求 确保你的系统满足以下条件: 操作系统:macOS、Linux或者Windows。足够的磁盘空间和内存。 2. 安装…...

freecad参数化三维模型装配体解析至web端,切换参数组或修改参数

用免费开源的freecad制作全参数化的三维模型,并且装配,上传至服务器,解析至web端,用户可以切换参数或修改参数,驱动模型改变。 freecad全参数化装配体模型解析至web端进行参数切换、修改完整展示_哔哩哔哩_bilibili …...

前端基础之《Vue(9)—混入》

一、什么是混入 1、是一种代码复用的技巧 Vue组件是由若干选项组成的,向组件中混入可复用的选项。 2、作用 比如我封装两个组件,一个是A组件,一个是B组件,发现它里面有相同的选项,就可以用混用的方式来复用它。 二、…...

ORACLE DATAGUARD遇到GAP增量恢复方式修复RAC环境备机的实践

ORACLE DATAGUARD技术是一个常用的数据保护机制,在DATAGUARD运行过程中,遇到异常导致备机不同步,而主库的归档日志也被清理,此时出现GAP,无法同步;就需要人工处理;对于小型数据库重新全量同步数…...

机器人进阶---视觉算法(六)傅里叶变换在图像处理中怎么用

傅里叶变换在图像处理中怎么用 傅里叶变换的基本原理应用场景Python代码示例逐行解释总结傅里叶变换在图像处理中是一种重要的工具,它将图像从空间域转换到频域,从而可以对图像的频率特性进行分析和处理。傅里叶变换在图像滤波、图像增强、图像压缩和图像分析等方面都有广泛应…...

Java知识日常巩固(五)

Java中wait()和 sleep()的区别? 在Java中,wait()和sleep()方法用于线程控制,但它们之间存在几个关键区别: 1. 用途 wait():用于线程间的协作。当一个线程需要等待某个条件满足时,它会调用wait()方法释放锁并进入等待状态,直到其他线程调用相同对象的notify()或notifyAl…...

浅析锁的应用与场景

锁的应用与场景:从单机到分布式 摘要:在多线程和分布式系统中,“锁”是避免资源竞争、保障数据一致性的核心机制。但你真的了解锁吗?什么时候该用锁?用哪种锁?本文通过通俗的比喻和代码示例,带…...

语音合成之五语音合成中的“一对多”问题主流模型解决方案分析

语音合成中的“一对多”问题主流模型解决方案分析 引言“一对多”指的是什么?优秀开源模型的方法CosyvoiceSparkTTSLlaSA TTSVITS 引言 TTS系统旨在模仿人类的自然语音,但其核心面临着一个固有的挑战,即“一对多”问题 。这意味着对于给定的…...

ElementUi的Dropdown下拉菜单的详细介绍及使用

Dropdown是 ElementUI 中用于创建下拉菜单项的一个组件,通常el-dropdown-item 包裹在 el-dropdown 组件中使用。以下从功能特性(一些属性及方法)、使用和高级功能(高亮显示,滚动,额外传参数)三个方面进行详细介绍。 一、功能特性 1.触发方式…...

Linux麒麟 V10 系统找回 root 密码的步骤

Linux麒麟 V10 系统找回 root 密码的步骤 1 环境介绍2 操作步骤2.1重启系统并进入 GRUB 菜单2.2 输入 GRUB 账户密码2.3 修改启动参数2.4 启动系统2.5 修改root 密码2.6 重启系统 3 Linux命令全方位指南实战教程Linux命令学习使用列表 1 环境介绍 有时候root 密码忘记&#xf…...

20、 DeepSeekMoE论文笔记

DeepSeekMoE 1、**研究背景与动机**2、传统MoE一、MoE架构核心原理详解1. **标准Transformer块的结构**2. **MoE层对FFN的替代**3. **稀疏性与计算效率** 二、举例说明:以 N 4 N4 N4 专家、 K 2 K2 K2 为例1. **场景设定**2. **亲和度计算与专家选择**3. **输出计…...

在 Spring Boot 中实现 WebSockets

什么是 WebSockets? WebSockets 是一种基于 TCP 的全双工通信协议,允许客户端和服务器之间建立持久的双向连接,用于实时数据交换。相较于传统的 HTTP 请求-响应模型,WebSockets 提供了低延迟、高效率的通信方式,特别适…...

stone 3d v3.3.0版本发布,含时间线和连接器等新功能

1.新加了时间线(timeline)编辑器,可以类似blender一样给对象制作动画 2.新加了度量(metrics)系统,通过scene对象检测器中的useMetrics属性来启用或禁用,启用时所选物体将显示三维度量数据 新加了…...

.whl文件

本文主要介绍了.whl文件的定义,怎么安装.whl文件(离线,在线)。 怎么查看cuda的版本,以及如何安装相应版本的cuda(本地电脑,超算上) 以及如何创建.whl文件 .whl文件的定义 Document…...

Git命令行中vim的操作

Git命令行用vim打开文件,或者用其他git命令打开了文件,需要编辑和保存文件等,有些命令表情奇怪,往往容易忘记这些命令。记录下。 下面这篇比较实用和简练: gitvim编辑文件命令 • Worktile社区https://worktile.com/…...

C#初级知识总结

一、什么是CIL 1.CIL(Common Intermidate Language)是指.Net的公共中间语言,它是一种编程语言。 .Net框架的各种语言在编译时都会编译成同一种中间语言(CIL),之后程序运行的时候CIL会被JIT(Just In Time)转换为二进制语言&#xf…...

使用 AI Agent 改善师生互动的设计文档

使用 AI Agent 改善师生互动的设计文档 一、引言 1.1 研究背景 当前教育领域的师生互动存在诸多挑战,如教师负担过重、学生个体差异大导致难以满足所有人的需求,以及信息传递延迟等问题。引入AI-Agent能够有效缓解这些问题,通过自动化手段协…...

Linux学习笔记之环境变量

写这篇博客的目的主要是因为本人学习动静态库时,用到了环境变量的知识,发现略有遗忘,因此回顾复习,整理成博客。 一、环境变量是什么 Linux环境变量是存储系统或程序运行时配置信息的特殊变量,用于为程序提供配置参数…...

16:00开始面试,16:08就出来了,问的问题有点变态。。。

从小厂出来,没想到在另一家公司又寄了。 到这家公司开始上班,加班是每天必不可少的,看在钱给的比较多的份上,就不太计较了。没想到4月一纸通知,所有人不准加班,加班费不仅没有了,薪资还要降40%…...

深度解析云计算:概念、优势与分类全览

以下是对云计算概念、优点和分类更详细的介绍: 一、云计算的概念 云计算是一种通过互联网提供计算服务的模式,它基于虚拟化、分布式计算、网络存储等一系列先进技术,将计算资源进行整合和管理,形成一个庞大的资源池。这些资源包…...

私钥连接服务器(已经有服务器私钥

前言:假设我们已经有了服务器的私钥,我们怎么配置呢? 下面我会从vsc的配置角度来写 ✅ 步骤一:准备工作 安装 VS Code(如果还没装) 👉 https://code.visualstudio.com/ 安装插件:Re…...

学员答题pk知识竞赛小程序怎么做

制作学员答题PK知识竞赛小程序,主要有以下步骤: 一、规划设计 明确需求:确定小程序的使用场景是校园知识竞赛、培训机构考核还是企业内部培训等。答题功能,规定答题的具体规则,包括题目类型(单选、多选、…...

外观模式:简化复杂系统接口的设计模式

外观模式:简化复杂系统接口的设计模式 一、模式核心:为复杂子系统提供统一简单接口 当一个系统由多个复杂子系统组成时(如电商系统中的支付、物流、库存模块),客户端直接调用子系统会导致依赖关系复杂、代码难以维护…...

vue3项目中eslint.config.ts配置rules

vue3项目中eslint.config.ts配置rules 1. 使用npm create vuelatest创建vue项目 默认的eslint.config.ts如下 import { globalIgnores } from eslint/config import { defineConfigWithVueTs, vueTsConfigs } from vue/eslint-config-typescript import pluginVue from esli…...

uniapp-商城-36-shop 购物车 选好了 进行订单确认2 支付方式颜色变化和颜色滤镜filter

颜色滤镜&#xff0c;在好多网页都这样使用&#xff0c;滤掉彩色&#xff0c;显示黑白&#xff0c;这在一些关键的日子中都这样使用。 1、依然回到订单确认页面 看到支付的颜色了嘛&#xff1f; <view class"payType"><view class"box" :class&q…...

Vue3 上传后的文件智能预览(实战体会)

目录 前言1. Demo12. Demo2 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 爬虫神器&#xff0c;无代码爬取&#xff0c;就来&#xff1a;bright.cn 此处的基本知识涉及较少&#xff0c;主要以Demo的形式供大…...