【Linux】线程(一)

谈论之前需要先谈论一些线程的背景知识

其中就有进程地址空间,又是这个让我们又爱又恨的东西。

注意:全篇都是在32位的情况下进行的

目录

  • 背景知识:
    • 地址空间:
      • 内存:
      • 页表:
    • 基于以上理解文件缓冲区与虚拟地址:
      • 文件缓冲区:
      • 虚拟地址:
  • 线程:
    • linux下的线程:
      • 与进程的澄清:
    • win下的进程:
      • 与linux进行比较:
    • 代码:
  • 有了多进程为什么还要多线程?
  • 重新理解代码被划分:
  • 重新梳理一遍概念:
    • Linux线程概念:
    • 线程的优点:
    • 线程的缺点:
    • 线程异常:
    • 线程用途:
    • 关于线程资源的共享:
  • 为什么编译时要加-lpthread?

背景知识:

地址空间:

说在前边,OS通常分为4个核心模块:执行流管理,文件系统管理,文件管理与IO管理。

在这先要涉及到对内存的管理。

内存:

以下是一张内存与磁盘的形象图。
在这里插入图片描述
在没接触内存管理之前我们通常是认为内存是直接一整块的。
现在我们要知道内存其实不是一整块的,而是分成了一个大数组,数组每个大小4kb!每个4kb叫做页框或页帧。
在这里插入图片描述
当然,这个是可以进行更改的,但是需要将OS重新编译一份出来,另外这个数字是由计算机科学家研究出来的。

我们的这个4KB是不是看着很熟悉呢?
4KB也正是磁盘文件与系统进行IO的基本单位。
在OS中的巧合都是精心设计的~

结论:我们磁盘中的可执行程序也是文件,是文件就有对应的inode。我们磁盘中已经天然的按4kb,比如说我们文件存放自己的数据就在datablock[]中,这个就是以4kb进行划分的,所以在进行磁盘IO时,所谓的加载就是把数据块加载到指定的内存块。


那么现在就有一个问题了:当父子进程共享了一个全局变量int类型,进行写时拷贝时是进行4字节的拷贝还是4kb呢?

答案是4kb,这遵循了一个局部性的原则,当你本行的数据被修改,那么下一行会有90的概率也被修改,避免了太多的拷贝,OS也是很忙的。
所以其实我们C语言malloc4字节其实也是申请了4kb,只是我们只能只用4字节(很粗略的理解)
我们的共享内存所以一般也都是申请4096个字节的整倍数…


那么是怎么进行划分的呢,那么多4kb是如何管理的呢?
比如每个内存块被占用了多少,被谁占用了…

所以需要进行管理。
假设我们是4GB,那么大约是一百万个页框,更准确来说是1048576,每个页框都有自己的使用情况。

在这里插入图片描述

最底层使用数组进行管理起来,每个page都有自己对应的下标–>使用对应的下标×4kb == 页框地址

所以对内存的管理就变为了对数组的增删查改。
那么这个数组大约多大呢?
假设每一份是1byte,那么大约是1mb。
但是实际上一份大约是十几byte。那么就是十几MB就可以管理起来这个数组了。


页表:

真实的页表是什么样子?虚拟地址又是如何转化到物理地址?
在还没有真正接触页表之前,我们通常会这么认为:
在这里插入图片描述
我们先来说一下第二个问题:
我们进行地址的转化是需要与CPU进行配合的。
CPU中接收到虚拟地址+MMU–>物理地址。

再来看一下第二个问题:
我们的虚拟地址有32位,那么每个虚拟地址就有32个比特位。

在这里插入图片描述
其中前十比特位用来索引第一张页表,也就是第一张页表的下标。
在这里插入图片描述
那么第一张页表中存放的是什么?
是第二张页表的地址。

在这里插入图片描述
所以中10位也就是第二张页表的索引(下标咯)。
在这里插入图片描述
那么后12位代表着什么?
我们想到每个页框的大小为4kb->4096字节->[0, 4095],而我们的后12位正好也是[0, 4095],所以我们的低12位就是每个页框的偏移量!
在这里插入图片描述
所以我们页表的本质是进行搜索页框!

这种分配方案叫做二级页表,大大减少了内存。

口说无凭,我们计算一下:

首先第一张页表叫做页目录。
存的是第二张页表的地址。
页目录的大小 = 4字节×1024 = 4kb。

第二张页表叫做页表
页表中存的是page的起始地址,
页表大小 = 4字节 × 1024 × 1024 = 4mb。

所以总大小为4mb + 4kb就是页表了!

何况也是按照拉满的进行计算,实际是 <= 这个大小的。

注意:page这个管理内存的数据结构与页表是相辅相成,
它们之间虽然在逻辑上有间接的联系(都是用来管理物理内存的不同方面),但实际上它们的数据内容和直接操作是分离的。

但是我们现在仍然只能获得一个字节的地址,一个int都4个字节,怎么读取一个int的数据呢?
所以需要类型?
汇编会告诉CPU你的对象类型是什么。

所以现在还差一个问题。
当CPU获得虚拟地址时,MMU可以进行运算找到页框。但是这个过程中我们得有页目录的地址啊,才能进一步的运算,地址怎么获得?
在CR系列的寄存器中。
在这里插入图片描述
CR3就负责存页目录的地址。

基于以上理解文件缓冲区与虚拟地址:

文件缓冲区:

文件缓冲区实际上就是从struct page memory这个数组中挑几个page与struct file进行关联,这就是文件缓冲区,至于怎么关联的是依靠字典树,了解一下即可。

虚拟地址:

我们以正文代码段为例:在这里插入图片描述
我们的正文代码实际上是一段范围,如果有20个函数,那我们就可以将正文分成20部分。
是不是很抽象的感觉,没事,我们来好好的捋一捋。
首先我们知道函数是有地址的,是一批代码的入口地址;函数的每一行也都有地址,而我们的函数都是连续的,且编译是以平坦模式的绝对编址,所以最终每个函数都是一个一个连续的代码块。

那么正文就理所当然的可以拆分为20分,本质就是拆分页表,也就是一种资源。

线程:

那么就来看一看一下官方的线程定义:

在进程内部运行,是CPU调度的基本单位。

果然是十分抽象~

linux下的线程:

线程与进程实际上是有密不可分的关系。
所以我们先从进程开始了解线程。

这是我们以前接触的进程:
进程 = 内核数据结构 + 代码和数据
在这里插入图片描述
每一个进程都有属于自己的地址空间与页表,各自有各自的数据。

但是如果我们创建几个没有地址空间,页表与全新代码的进程呢?
在这里插入图片描述

我们将正文代码分为3部分,分别给task_structA B C。
让这3个分别执行代码的一部分,他们共享地址空间,这叫做“linux下的线程”。

所以现在我们知道了定义的第一点:线程在进程内部运行,也就是在地址空间内运行。

与进程的澄清:

进程 = 内核数据结构 + 代码和数据。

也就是说下图这个整体是一个进程。
在这里插入图片描述
而线程是其中一个task_struct与虚拟地址+页表+代码数据(绿色的不是)。
在这里插入图片描述
站在OS的视角上,我们可以给进程下一个定义:是承担分配系统资源的实体。

怎么理解?
这就像进程是一个家庭,它有着国家分配的资源;而线程是家庭成员。

爸妈执行上班的代码,孩子执行上学的代码,爷爷奶奶执行养老的代码。
他们为了一个共同的目的,那就是将日子过好。

所以我们之前理解的进程就像是家庭中只有一个成员。

win下的进程:

如果我们要设计线程,那么每个进程可能都有很多线程,有的是被创建,有的被销毁,有的被调度,所以就要被管理。win下的线程就是这样设计的。
它有着一个描述线程的tcb,还有一个将tcb组织起来的数据结构

struct tcb // thread control bolock
{// id 优先级 状态 上下文// ...
}

在这里插入图片描述

与linux进行比较:

但是linux并没有这么做,linux选择了复用pcb,因为tcb需要的pcb几乎都已经准备好了,于是用pcb统一表示执行流,这样就不需要单独设计了!

那么哪个OS的做法更优呢?
win:实现了真正的tcb。
linux:复用,用进程来模拟线程。

我们认为Linux更优秀,因为他的代码量少,成本低,健壮性高。


那么当CPU调度时,需要关注task_struct是进程还是线程吗?
答案是不需要的,CPU只关心如何执行,每个task_struct都是一个执行流,他已经包含了自己执行需要的东西。

所以CPU看到的每一个执行流都是 <= 进程的,linux中的线程叫做轻量级进程!

所以现在对于定义的第二点我们也能理解了。

代码:

说了这么久的理论,我们总归还是要回归代码的。
在这里插入图片描述

那么线程如何进行创建?
参数解释:
thread:是线程id。
attr:我们不管,设置为nullptr即可。
start_routine:是我们新线程去执行的函数。
arg:传给函数的参数。

在这里插入图片描述

现象:
在这里插入图片描述
发现只有一个pid。
利用ps | ajx也是如此:
在这里插入图片描述
原因在于线程都是在同一个进程内部。

但是我们也可以查到线程:ps -aL 即可
在这里插入图片描述
我们发现有一个叫做LWP的东西,这个其实就是轻量级进程的意思(light weight process),下面的数字是线程的唯一标识符。

我们说过CPU调度时是以线程为单位的,那么就肯定看的是LWP。
其中PID = LWD的那一个线程叫做主线程。

有了多进程为什么还要多线程?

这里主要有3个原因造成。
创建
进程创建时需要PCB,地址空间,页表,加载代码和数据,还有标准输入输出…成本很高;
但是线程的创建只需要创建一个PCB,指向地址空间即可、
调度
进程的切换要切换上下文,切换页表…
而线程由于是共享地址空间,所以页表那些东西就不要切换了
死亡
进程的死亡需要释放PCB,虚拟地址空间,页表…
线程只进行删除对应的PCB即可。

但是对于调度的理解还需要再加深一下:
我们切换页表什么的仅仅修改一下CR3寄存器即可,无非就是修改了几个寄存器,实际上并不会有很大的优势。

不过调度效率高这是事实,这其中与硬件还有密不可分的关系。
答案就在于cache。
在这里插入图片描述
我们的CPU为了调度进程时为了更快的访存效率,往往会存在cache,这其实依据了局部性原因,比如当我们访问第五行时,CPU会把围绕这行代码的高频数据预先加载到cache中,这叫做热数据,当所以PU进行访存时会先在cache中寻找,找到直接拿,找不到在切换cache。
这意味着如果是进程间的切换,对与cache中的数据肯定是用不到的,因为进程具有独立性,所以需要将cache的数据全部替换。
而线程的切换往往对于cache是用的到的,避免了大量的替换,所以效率更高!

重新理解代码被划分:

我们在背景知识的最后一段说了一段比较抽象的话,关于代码被划分的问题。

我们的程序经过编译后,所谓的线程执行的方法其实就变成了一块代码块,所以县城区执行函数本质就是拥有了正文代码的一部分—>小块虚拟地址空间的范围—>页表的一部分—>物理内存—>也就是资源。

重新梳理一遍概念:

Linux线程概念:

  • 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
  • 一切进程至少都有一个执行线程
  • 线程在进程内部运行,本质是在进程地址空间内运行
  • 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
  • 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流
    在这里插入图片描述

线程的优点:

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

这里对后两个做一下解释:

什么叫做计算密集型?
计算加密解密,压缩文件等工作。
要注意,并不是线程越多越好,如果太多的话反而增加切换的成本,需要一个合适的线程数量,那么这个合适的量一般是多少?
一般你的CPU是几核就创建几个线程,有几率一个CPU执行一个线程,做到真正的并发。

什么叫做IO密集型?
大部分时间都在等待外部设备(如硬盘、网络)操作完成的任务,这时的CPU往往处于空闲状态。
在I/O密集型任务中,通过创建多线程,可以使得在某些线程等待I/O时,其他线程能够继续执行,从而重叠I/O等待时间与计算时间,提高了处理器的利用率和任务的执行效率

线程的缺点:

  • 健壮性降低:编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

这里写一段代码便于理解。
比如我们有多个线程。
在这里插入图片描述
现象:
在这里插入图片描述
当一个线程收到信号,那么整个进程就会被终止,因为线程是进程的一部分。

  • 缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

这个应该怎么理解?
代码:在这里插入图片描述
现象:
在这里插入图片描述
由于共享地址空间,所以当其中一个线程更改了一个全局公用的数据,那么其他线程也会受到影响。

线程异常:

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
  • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。

线程用途:

  • 合理的使用多线程,能提高CPU密集型程序的执行效率
  • 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)

关于线程资源的共享:

线程共享进程数据,但也拥有自己的一部分数据:

  • 线程ID
  • 一组寄存器
  • errno
  • 信号屏蔽字
  • 调度优先级

其中最重要的是一组寄存器

一组寄存器:保存硬件的上下文–>线程是可以动态运行的。

栈:如果公用一个栈,那么每个线程的临时变量都会在一个栈中,有的线程压栈,有的出栈,最终造成混乱,所以线程在运行的时候形成的各种临时变量会被保存在自己的栈区

为什么编译时要加-lpthread?

现在我们要解决最后一个问题:
我们已经说过,linux中是没有线程的,只有轻量级进程。

虽然linux没有线程,但是他有与线程一致的轻量级进程,为了自己的纯粹性,他不会提供线程的接口。

但是用户在学习linux时只会学习线程的操作,所以中间势必会有一层软件层–>ptread库,他是对LWP的封装,按照线程的接口设计。

在这里插入图片描述
所以linux中的线程是用户级别的。
win下的线程是内核级。

本篇完~

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

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

相关文章

【学术小白成长之路】03三方演化博弈(基于复制动态方程)均衡点与稳定性分析

从本专栏开始&#xff0c;笔者正式研究演化博弈分析&#xff0c;其中涉及到双方演化博弈分析&#xff0c;三方演化博弈分析&#xff0c;复杂网络博弈分析等等。 先阅读了大量相关的博弈分析的文献&#xff0c;总结了现有的研究常用的研究流程&#xff0c;针对每个流程进行拆解。…

宁德等保测评公司有哪些?位于哪里?

据悉2024年中国百强城市就包含福建宁德。宁德市&#xff0c;福建省辖地级市&#xff0c;GDP快速增长&#xff0c;拥有众多自然风光和历史文化名镇&#xff0c;是一个生活幸福的城市。这里的小伙伴在问&#xff0c;宁德等保测评公司有哪些&#xff1f;位于哪里&#xff1f; 宁德…

想上币的项目方怎么去选择交易所

在区块链和加密货币蓬勃发展的今天&#xff0c;许多项目方都渴望通过交易所上线其代币&#xff0c;以扩大影响力、提升流动性和市场认可度。然而&#xff0c;选择合适的交易所并非易事&#xff0c;它关乎项目的未来发展和市场地位。那么&#xff0c;对于有上币意向的项目来说&a…

Thinkphp起名网宝宝起名网站源码

Thinkphp起名网宝宝起名网站源码 源码介绍 1.宝宝在线起名 2.八字起名&#xff0c;周易取名 3.一对一起名 5.支持手机wap 链接数据库地址&#xff1a;Application\Common\Conf 修改里面config.php数据库连接&#xff0c;导入sm.sql数据库文件即可 伪静态用thinkphp 后台…

接口测试的几种方法

其实无论用那种测试方法&#xff0c;接口测试的原理是通过测试程序模拟客户端向服务器发送请求报文&#xff0c;服务器接收请求报文后对相应的报文做出处理然后再把应答报文发送给客户端&#xff0c;客户端接收应答报文这一个过程。 方法一、用LoadRunner实现接口测试 大家都…

软考高级论文真题“论湖仓一体架构及其应用”

论文真题 随着5G、大数据、人工智能、物联网等技术的不断成熟&#xff0c;各行各业的业务场景日益复杂&#xff0c;企业数据呈现出大规模、多样性的特点&#xff0c;特别是非结构化数据呈现出爆发式增长趋势。在这一背景下&#xff0c;企业数据管理不再局限于传统的结构化OLTP…

持PMP证书可以免考申请CSPM-2国标证书!

一提到项目管理的专业认证&#xff0c;大家首先想到的肯定是以PMP为核心的PMI体系认证。当然也有BSI和IPMP等其他体系认证&#xff0c;但都是从国外引进的专业认证&#xff0c;我国始终缺少符合中国特色项目管理环境下的项目管理专业认证体系。 如今&#xff0c;更符合中国国情…

Novartis诺华制药社招综合能力性格动机问卷入职测评笔试题库答案及包过助攻

【华东同舟求职】由资深各行业从业者建立的一站式人才服务网络平台&#xff0c;现阶段目标是“提升全市场各行业岗位信息的流动性和透明度”。我们接受众多行业机构的直接委托发布&#xff0c;并尽力通过各种方法搜寻高价值岗位信息。事实上&#xff0c;我们以发现不为人知的优…

经验分享,xps格式转成pdf格式

XPS 是一种电子文档格式、后台打印文件格式和页面描述语言。有时候微软默认打印机保存的是xps格式&#xff0c;我们如何转换为pdf格式呢&#xff0c;这里分享一个免费好用的网站&#xff0c;可以实现。 网站&#xff1a;https://xpstopdf.com/zh/ 截图&#xff1a;

HarmoneyOS星河版 安装和启动

一、下载和安装DevEco Studio 官网链接&#xff1a;OpenAtom OpenHarmony 1.1 找到对应的操作系统进行下载 创建安装Harmony的文件夹&#xff1a; 1.2 下载后进行安装 1.3 分别安装Node、Ohpm、SDK 分别安装Node、Ohpm和SDK 二、.创建一个新项目并运行 2.1 选择[OpenHarmon…

代码解读 | Hybrid Transformers for Music Source Separation[07]

一、背景 0、Hybrid Transformer 论文解读 1、代码复现|Demucs Music Source Separation_demucs架构原理-CSDN博客 2、Hybrid Transformer 各个模块对应的代码具体在工程的哪个地方 3、Hybrid Transformer 各个模块的底层到底是个啥&#xff08;初步感受&#xff09;&#xff1…

数据安全交换系统 与网闸有什么区别?

数据安全交换系统是指用于安全地传输、共享和交换数据的一种系统。这样的系统通常包括一系列安全性和隐私保护功能&#xff0c;确保数据在传输和存储过程中不会被未经授权的用户访问、泄露或篡改。 数据安全交换系统和网闸在功能和定位上有一些区别&#xff1a; 功能&#xff…

短剧系统搭建全攻略:功能齐全,一步到位

前言 近年来&#xff0c;短剧系统以其独特魅力&#xff0c;成为大众消遣娱乐的热门选择。简单来说&#xff0c;短剧系统就是用来看短剧的小程序&#xff0c;它汇集了丰富多彩的短剧资源&#xff0c;让观众随时随地享受观影乐趣。本文将为你详细解析短剧系统的搭建全攻略&#…

ROS系统中解析通过CAN协议传输的超声波传感器数据

CAN Bus接口设置&#xff1a;确保你的ROS系统可以通过CAN Bus接口与外部设备通信。这可能需要CAN卡或CAN适配器&#xff0c;以及相应的驱动程序和库。 CAN消息接收&#xff1a;配置ROS节点来监听特定的CAN ID&#xff0c;这通常是超声波传感器的标识符。 数据解析&#xff1a…

Aeron:两个代理之间的单向IPC(One-way IPC between two agents)

一、概述 本例展示了如何通过 IPC 在调度于不同线程的两个代理之间传输缓冲区。在继续学习本示例之前&#xff0c;最好先复习一下Simplest Full Example &#xff0c;因为该示例展示的是 IPC 通信&#xff0c;没有增加代理的复杂性。读者还应熟悉Media Driver 流程构建如下&…

学习笔记——路由网络基础——路由优先级(preference)

1、路由优先级(preference) 路由优先级(preference)代表路由的优先程度。当路由器从多种不同的途径获知到达同一个目的网段的路由(这些路由的目的网络地址及网络掩码均相同)时&#xff0c;路由器会比较这些路由的优先级&#xff0c;优选优先级值最小的路由。 路由来源的优先…

编程之道:程序员必备的五大职业素养

引言 在数字化时代&#xff0c;程序员的角色变得日益重要。他们不仅是代码的编写者&#xff0c;更是技术变革的推动者。然而&#xff0c;成为一名优秀的程序员&#xff0c;除了技术能力之外&#xff0c;还需要具备一系列职业素养。本文将探讨程序员在职业生涯中应具备的五大职业…

pycharm git配置

PyCharm 是一个强大的集成开发环境(IDE),它内置了 Git 集成,使得版本控制变得非常方便。以下是 PyCharm 中配置 Git 的基本步骤: 安装Git: 在开始之前,请确保已经在您的系统上安装了 Git。您可以通过官方网站下载并安装 Git。本系统用的是Git-2.29.2.2-64-bit 。 打开PyC…

24年下半年安徽教资认定准确时间和流程

安徽教资认定准确时间 网上报名时间&#xff1a; 第一批次&#xff1a;4月8日至4月19日17时 第二批次&#xff1a;6月17日至6月28日17时 注意&#xff1a;符合安徽省申请条件的普通大中专院校2024届全日制毕业生&#xff0c;应统一选择6月17日至6月28日17时的时间段进行网上报名…

vivado在implementation时出现错误[Place 30-494] The design is empty的一个可能原因和解决方法

在查询类似帖子时我发现这一问题是由于在设计实现时vivado认为没有输出端口所以报错。 于是在.v文件中我添加了一个随意的端口&#xff0c;并且在.xdc文件中为它分配了管脚 这样做的确可以让设计实现的过程顺利进行&#xff0c;但是会发现在summary中&#xff0c;设计实现的…