Linux 多线程 ( 多线程概念 )

2023-11-13

Linux线程概念

什么是线程?

  1. 在一个程序里的一个执行路线叫做线程 thread ),更准确的定义为:“线程是一个进程内部的控制序列"。
  2. 一切进程至少有一个执行线程。
  3. 线程在进程内部运行,本质上是在进程地址空间中运行。
  4. 在linux系统中,CPU看到的PCB比传统的进程更加轻量化。
  5. 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。

我们在以前所学习的进程知识中,一个进程由进程控制块(task_struct),进程地址空间( mm_struct ) ,页表,页表与进程地址空间,物理内存的映射为关系构成。

在这里插入图片描述

每一个进程都有自己独有的进程地址空间和页表,对应的映射关系,所以我们在创建进程时需要耗费大量的时间,空间。

但是,对于Linux系统,如果我们在创建一个进程后只创建task_struct,并且这些task_struct共享进程地址空间,页表等相关资源,图示如下。
在这里插入图片描述
由于这些task_struct指向同一块进程地址空间和页表,所以它们所看到的资源都是一样的,我们可以让这四个task_struct执行不同的代码区域(栈区,堆区等以上区域),换句话说,我们之后创建出来的3个task_struct都可以同时执行自己的代码,从而完成”并发“。像这样,我们把这样的一份task_struct称这为”线程“。

  • 其中每一个线程都是当前进程里面的一个执行流,也就是我们常说的”线程是进程内部的一个执行分支“。
  • 线程在进程内部运行,本质就是线程在进程地址空间内运行,也就是说曾经这个进程申请的所有资源,几乎都是被所有线程共享的。
  • 线程比进程粒度更细,因为执行的代码和数据也更小了。
  • 线程调度的成本更低了,因为它在调度的时候,核心数据结构(进程地址空间和页表都不用切换了)

windows下的线程和Linux下的线程区别

Linux其实并没有真正对线程创建特定的数据结构

  • 线程本身是在进程内部运行的,操作系统中存在大量的进程,一个进程内又存在一个或多个线程,因此线程的数量一定比进程的数量多(线程 : 进程 一定是n : 1),当线程的数量足够多的时候,很明显线程的执行粒度要比进程更细。
  • 对于这么多的线程我们OS需要对其做管理(先描述,再组织),在大部分的OS中,线程都有一个tcb。如果我们的系统实现的是真线程,比如说windows平台,它就要分别对进程和线程设计各自的描述的数据块(结构体),并且很多线程在一个进程内部,所以还要维护线程tcb和进程pcb之间的关系。所以这样写出的代码,其tcb和pcb两个数据结构之间的耦合度非常复杂。但是对于Linux来说,在概念上并没有进程和线程的区分,只将task_struct叫做一个执行流,所以对于Linux来说,PCB和TCB为同一种。

Linux中使用pcb模拟tcb的优势:

  • 不需要单独设计tcb了。
  • 不用维护tcb和pcb之间的关系。
  • 不用打再编写调度算法了。

在Linux中,CPU是否能够识别当前调度的task_struct是进程还是线程?

不能,因为CPU只关心一个一个独立的task_struct(执行流),无论进程内部只有一个执行流还是有多个执行流,CPU都是以task_struct为单位进行调度的。只是这里的task_struct只执行一部分代码和数据,但也并不妨碍CPU执行其他执行流。

图示如下:
在这里插入图片描述

理解修改常量区

字符串常量区在代码区和已初始化数据区之间的,如果它不可被修改,那它是如何加载到物理内存呢?如何保证它不可被修改的?

比如当我们尝试修改字符串,字符串常量区经过页表的映射到物理内存,当它从虚拟地址到物理地址转换的时候,它是只读的,所以RWX权限为R,所以尝试在修改的时候直接在页表进行拦截,并结合MMU硬件转换,识别到只读但尝试修改的异常,发出信号,随后OS把此进程直接干掉。

如今,有了线程的引入,如何重新理解之前的进程?

我们红色方框框起来的内容,我们把这个整体叫做进程。
在这里插入图片描述

曾经我们理解的进程 = 内核数据结构 + 进程对应的代码和数据,现在的进程,站在内核角度上看就是:承担分配系统资源的基本实体。所有进程最大的意义是向系统申请资源的基本单位

因此,所谓的进程并不是通过task_struct衡量的,还需要创建地址空间、维护页表,然后在物理内存当中开辟空间、构建映射,打开进程默认打开的相关文件、注册信号对应的处理方案等等。

我们之前接触的进程内部只有一个task_struct,说明该进程内部只有一个执行流,也称之为”单执行流进程“。
在这里插入图片描述
如果进程内部有多个执行流,我们称之为"多执行流进程"。
在这里插入图片描述

  • Linux中,CPU实际上看到的task_struct实际上要比传统的task_struct更加轻量化,当进程只有一个执行流,那就说明等于OS内的进程,但是如果进程有多个执行流,那就说明该线程<其他OS内传统的PCB,CPU拿到的是进程中多执行流中某一个PCB,这某一个PCB并没有单独创建特定的数据结构,从宏观上,所以Linux下的进程统一称之为: 轻量级进程。
  • 线程(一个执行流)是CPU调度的基本单位。

原生线程库pthread

在Linux中,站在内核角度没有真正意义上线程相关的接口,但是站在用户角度,如果想创建线程,而不是只能用fork函数,所以提供了pthread原生线程库。

二级页表

二级页表引入

以32位平台为例,在32个比特位中一共可以存在2^32个地址,如果由虚拟地址空间通过页表映射到物理内存中,那么一个页表就需要2 ^32个表项存储地址。
在这里插入图片描述
并且,每一个表项除了要保存的虚拟地址以及物理地址这里就大概需要8字节,并且还需要保存一些权限信息,那么一个表项大概需要10个字节,那么最后意味着存储一张页表OS需要2^32 * 10个字节,那么也就是40GB。
在这里插入图片描述
可是,这远远超过了在32位平台下4GB的内存。

二级页表

实际上,OS在64位平台上是多级页表,在32位平台上是二级页表。

这里以32位平台为例,虚拟地址通过二级页表映射关系如下:

  1. 选择虚拟地址的前10个比特位在页目录中查找,通过映射关系找到对应的页表。
  2. 再选择虚拟地址的10个比特位在对应的页表中查找,通过映射关系找到对应的页框的起始地址。
  3. 最后将虚拟地址的剩下12个比特位作为偏移量从对应页框的起始位置后进行偏移,页框起始地址 + 偏移量 = 物理内存中对应的字节数据。

在这里插入图片描述
说明一下:

  • 物理内存是按照4KB单位进行划分的(每一个4KB单位叫做页框),可执行程序上也是被划分为4KB为一个单位为页帧,当可执行程序和加载到内存中时也是以4KB进行加载和保存的。
  • 如果一级页表有一张,那就说明有2^10个表项,进而表示有2 ^ 10个二级页表,并且1个表项 大概10个字节,那么总字节数便为2 ^ 10 * 10个字节,也就是10MB,这极大节省了空间。
  • 上面所述的映射过程,总共由页表和MMU硬件完成,其中页表为软件映射,MMU是一种硬件映射,OS由虚拟地址转换为物理地址采用的是软硬链接结合的方式。

上述页表的优势:

  • 进程虚拟地址管理和内存管理,通过页表 + page进行了解耦。
  • 页表分离了,可以实现页表的按需获取,没有用到的就不创建,进而节省了空间。

线程的优点

  • 创建一个新线程的代价要比创建一个新进程小得多。
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多,这里有两个核心原因:
  1. 线程切换是不需要切换页表和进程地址空间的,而进程与进程调度之间需要切换页表,进程地址空间
  2. CPU内部具有硬件L1~L3 cache缓存,在进程进行访问的时候,CPU提前就将目标代码和数据加载到CPU的缓冲区中,一个进程内部的执行流访问时就可以可以直接从缓冲区中读取,提高调度效率。但是如果进程间切换,因为进程具有独立性,cache立即失效,新进程执行的时候,只能重新对该进程预计执行的代码数据缓存。
  • 线程占用的资源要比进程少很多。
  • 能充分利用多处理器的可并行数量。
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务。
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

那么,一个进程中线程是不是越多越好?

不是,即便线程切换搜耗费的成本较低。但是如果线程过多,那么会造成线程之间的调度成本过大。

线程的缺点

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

线程异常

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃,因为在Linux中线程就是进程的一部分。
  • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 多线程 ( 多线程概念 ) 的相关文章

  • 来自守护程序的错误响应:加入会话密钥环:创建会话密钥:超出磁盘配额

    我尝试在我的服务器上安装 docker 使用本教程 https docs docker com install linux docker ce ubuntu 我想远程运行 docker 镜像并使用 portainer Web 界面来管理一切
  • Ubuntu Python shebang 线不工作

    无法让 shebang 线在 Ubuntu 中为 python 脚本工作 我每次只收到命令未找到错误 test py usr bin env python print Ran which python usr bin python 在 sh
  • 如何在不使用 IDE 的情况下在 Linux 上运行 Java 项目

    我是 Java 新手 基本上 我开发了一个java项目 其中包含Eclipse中的多个Java包 该项目在我安装了 redhat Linux 的桌面上运行正常 然而 我需要在一个更强大的没有安装X11的Linux服务器 redhat ent
  • ssh 连接超时

    我无法在 git 中 ssh 到 github bitbucket 或 gitlab 我通常会收到以下错误消息 如何避免它 输出 ssh T email protected cdn cgi l email protection i ssh
  • Linux 中 m 标志和 o 标志将存储在哪里

    我想知道最近收到的路由器通告的 m 标志和 o 标志的值 从内核源代码中我知道存储了 m 标志和 o 标志 Remember the managed otherconf flags from most recently received R
  • FileOutputStream.close() 中的设备 ioctl 不合适

    我有一些代码可以使用以下命令将一些首选项保存到文件中FileOutputStream 这是我已经写了一千遍的标准代码 FileOutputStream out new FileOutputStream file try BufferedOu
  • 在 C 中使用单个消息队列是否可以实现双向通信

    我希望服务器向客户端发送一些消息 并让客户端确认它 我被分配了这个任务 我可以在 C linux 中使用单个消息队列来完成它还是我需要创建两个 谢谢 是的 可以使用 sysV 消息队列来做到这一点 从您之前的问题来看 您正在使用该队列 您可
  • 在 .gitconfig 中隐藏 GitHub 令牌

    我想将所有点文件存储在 GitHub 上 包括 gitconfig 这需要我将 GitHub 令牌隐藏在 gitconfig 中 为此 我有一个 gitconfig hidden token 文件 这是我打算编辑并放在隐藏令牌的 git 下
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • 如何阻止ubuntu在使用apt安装或更新软件包时弹出“Daemons using outdatedlibraries”? [关闭]

    Closed 这个问题是与编程或软件开发无关 help closed questions 目前不接受答案 我最近新安装了 Ubuntu 22 04 LTS 我发现每次使用 apt 安装或更新软件包时 它都会询问我有关Which servic
  • 如何获取 (Linux) 机器的 IP 地址?

    这个问题和之前问的几乎一样如何获取本地计算机的IP地址 https stackoverflow com questions 122208 get the ip address of local computer 问题 但是我需要找到一个的I
  • 并行运行 shell 脚本

    我有一个 shell 脚本 打乱大型文本文件 600 万行和 6 列 根据第一列对文件进行排序 输出 1000 个文件 所以伪代码看起来像这样 file1 sh bin bash for i in seq 1 1000 do Generat
  • CMake 链接 glfw3 lib 错误

    我正在使用 CLion 并且正在使用 glfw3 库编写一个程序 http www glfw org docs latest http www glfw org docs latest 我安装并正确执行了库中的所有操作 我有 a 和 h 文
  • 使用 shell 脚本将行附加到 /etc/hosts 文件

    我有一个新的 Ubuntu 12 04 VPS 我正在尝试编写一个安装脚本来完成整个 LAMP 安装 我遇到问题的地方是在 etc hosts文件 我当前的主机文件如下所示 127 0 0 1 localhost Venus The fol
  • 如何让R使用所有处理器?

    我有一台运行 Windows XP 的四核笔记本电脑 但查看任务管理器 R 似乎一次只使用一个处理器 如何让 R 使用全部四个处理器并加速我的 R 程序 我有一个基本系统 我使用它在 for 循环上并行化我的程序 一旦您了解需要做什么 此方
  • .net-core:ILDASM / ILASM 的等效项

    net core 是否有相当于 ILDASM ILASM 的功能 具体来说 我正在寻找在 Linux 上运行的东西 因此为什么是 net core ildasm 和 ilasm 工具都是使用此存储库中的 CoreCLR 构建的 https
  • Bash - 在与当前终端分开的另一个终端中启动命令的新实例

    我有一个简单的 bash 脚本 test sh 设置如下 bin bash args if args 0 check capture then watch n 1 ls lag home user capture0 watch n 1 ls
  • 如何使用waf构建共享库?

    我想使用构建一个共享库waf http code google com p waf 因为它看起来比 GNU 自动工具更容易 更简洁 到目前为止 我实际上有几个与我开始编写的 wscript 有关的问题 VERSION 0 0 1 APPNA
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 错误:“rjags”的包或命名空间加载失败

    在终端的 conda 环境之一中 我能够成功安装包 rjags 但是 当我在该环境中运行 R 并运行库 rjags 时 出现以下错误 加载所需的包 coda 错误 rjags 的包或命名空间加载失败 rjags 的 loadNamespac

随机推荐

  • 10个 解放双手的 IDEA 插件,让你少写冤枉代码

    公众号关注 GitHubDaily 设为 星标 每天带你逛 GitHub 友情提示 插件虽好 可不要贪装哦 装多了会 卡 卡 卡 正经干活用的 分享一点自己工作中得心应手的 IDEA 插件 可不是在插件商店随随便便搜的 都经过实战检验 用过
  • 两种通过aop设置重试机制的方式

    注意 1 不要在同一个类中调用自定义的注解 如果controller调用 注解要放在service层 其他类 2 如果有配置aop扫描的包 不能只设置扫描control下的文件 方式一 controller层 RequestMapping
  • Map

    1 说明 系统根据C Reference学习下STL gt Map 2 Map Maps are associative containers that store elements formed by a combination of a
  • 攻略:Windwos 系统中应用程序访问权限

    在Windwos XP系统中 存在着两种类型的账户 即隶属于Administrators组中的管理员账户和隶属于Users组中受限制的用户 默认情况下 管理员账户可以无限制地运行计算机中的所有程序 包括XP系统内建的以及自行安装的应用程序
  • Vue常用指令详解分析

    Vue入门 Vue是一个MVVM Model View ViewModel 的前端框架 相对于Angular来说简单 易学上手快 近两年也也别流行 发展速度较快 已经超越Angular了 比较适用于移动端 轻量级的框架 文件小 运行速度快
  • 2023AI会议时间表(持续更新)

    本文已参与 新人创作礼 活动 一起开启掘金创作之路 这篇文我会持续更新 小伙伴们可以收藏 尤其是有发论文需求的同学 当然了 如果你上网方便 有耐心的话 也可以直接上这个网站去查看 只不过要筛选一下 我这里主要是把机器学习还有我自己比较相关的
  • 【牛客网 - 华为机试】HJ59 找出字符串只出现一次的字符

    HJ59 找出字符串只出现一次的字符 题目描述 找出字符串中第一个只出现一次的字符 输入描述 输入几个非空字符串 输出描述 输出第一个只出现一次的字符 如果不存在输出 1 示例1 输入 asdfasdfo aabb 输出 o 1 impor
  • C++去掉字符串前后的多余空格

    C 中std string 没有可以直接去掉字符串前后多余空格的接口 所以自己实现了一个 借鉴Qt中QString的trimmed 函数源码实现的版本 去掉std string 字符串前后的空格 void string trimmed st
  • vscode连接服务器

    使用VScode连接服务器的好处在于 vscode可以当成一个网页端的IDE 而使用Pycharm连接服务器则存在文件的同步问题 而且很慢 此处保险起见 加一个前提条件 to the best of our knowledge 步骤一 安装
  • Vue3的fragment

    vue2时 组件的模板结构中出现多个标签时 需要使用根标签 vue3时 组件的模板结构中出现多个标签时 可以不用根标签 这是因为vue3会自动将多个标签用fragment包裹 举个例子 main js import createApp fr
  • 华为h22h05服务器装系统_在新服务器装12年前的系统,如何实现

    2020年1月14日 微软正式终止支持Windows 7 包括不再提供任何技术支持 软件更新和安全更新及修复 这意味着 如果用户不想让自己的电脑 裸奔 就得升级到更新的操作系统版本 也许有些人觉得更新操作系统并没有什么 但对那些古董软件使用
  • 社会学讲义

    文章目录 一 社会学的观点和视角 1 世界为什么不是看起来的那个样子 为什么我们需要通过社会学的视角来看世界 2 社会学的想象力 赖特 米尔斯 C Wright Mills 3 为何要培养 社会学的想象力 4 为什么说人的世界观和人的属性是
  • 关于蓝牙测试蓝牙协议(应用层)

    1 HFP 让蓝牙设备可以控制电话 如接听 挂断 拒接 语音拨号等 拒接 语音拨号要看蓝牙设备和手机是否支持 2 HSP 描述了蓝牙耳机如何与计算机或者其他蓝牙设备 手机 通信 连接配置好后耳机可以作为远程设备的音频输入和输出接口 这个是最
  • torch.bmm()函数详解

    去掉batch size这一维度 按照矩阵乘法运算即可 自行体会 class PAM Module nn Module Position attention module def init self in dim gt None super
  • vue-cli4.0下antd(ant-design-vue)按需引入(附带完整过程)

    ant design vue 踩坑记录 官网没给vue cli4的例子 于是试试按照官网vue cli3的步骤操作 遇到了一些问题 这里记录下 后文给了完整过程 我亲自重新跑过一遍 应该是没有问题的 坑1 Failed to resolve
  • [vue报错]has been blocked by CORS policy

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 8080 setMealDetail goodsId 36363684365 1 Access to XMLHttpRequest at http 132 121 80 2
  • react hook

    一 hook的定义 Hook 是 React 16 8 的新增特性 它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性 二 使用react hook的好处 1 使用类组件 相关逻辑的代码会被分割到不同生命
  • Java-多线程-给线程命名

    Java 多线程 给线程命名 在Java中 通过继承Thread创建的线程 有以下两种方式可以给线程命名 通过构造器命名 因为线程类继承自Thread类 所有也继承了Thread的name属性 可以通过super的方法调用父类构造器 将na
  • 关于xxxxxxRepository.search()方法一个分页的小陷阱

    背景介绍 我有20几万的数据 现在要推送给别人 这样我势必要从自己的数据库中先查出来 然后一点一点的传给对方 所以我的思路是这样的 20万数据 Repository自带的search方法有分页功能 我只需要传一个pageable给它 告诉它
  • Linux 多线程 ( 多线程概念 )

    文章目录 Linux线程概念 什么是线程 二级页表 线程的优点 线程的缺点 线程异常 Linux线程概念 什么是线程 在一个程序里的一个执行路线叫做线程 thread 更准确的定义为 线程是一个进程内部的控制序列 一切进程至少有一个执行线程