【Linux】多线程——线程概念|进程VS线程|线程控制(上)

简介: 【Linux】多线程——线程概念|进程VS线程|线程控制(上)

> 作者:დ旧言~

> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:理解【Linux】多线程——线程概念|进程VS线程|线程控制

> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!

> 专栏选自:Linux初阶

> 望小伙伴们点赞👍收藏✨加关注哟💕💕



🌟前言

早期我们的计算机还只能单个进程运行,这样的话每个进程就只能独立存在,不可以进行每个进程交互,在这个基础上我们的先人(大佬)看看能不能多个进程同时运行,也就有了现在的多进程,那对比单个进程,多进程有什么优势呢?值不值得我们学习呢?咱们带上这两个问题来康康Linux下的多线程。


⭐主体

学习【Linux】多线程——线程概念|进程VS线程|线程控制咱们按照下面的图解:



🌙 地址空间和页表

地址空间是进程能看到的资源窗口:一个进程能看到代码区、共享区、内核区、堆栈区,大部分的资源都是在地址空间上看到的。


页表决定进程真正拥有资源的情况:当前进程认为自己有了4GB,可是实际上用了多少由页表决定最终能用多少物理资源。


合理的对地址空间与页表进行资源划分,我们就可以对一个进程所有的资源进行分类:通过地址空间分为栈区、堆区…通过页表映射到不同的物理内存。


💫 页表的映射

在32位平台下一共有2³²个地址,也就意味着有2³²个地址需要被映射:


地址空间一共有2³²个地址,每个地址单位都是1字节,而页表也得有2³²个条目,每个地址都得经过页表映射,都是页表的每个条目(包括物理地址,包括是否命中,包括RWX权限,包括U/K权限,一个条目,假设为6个字节,样例数据),所以,光保存页表所需空间为24GB(4GB约为40亿字节)。



每一个表项中除了要有虚拟地址和与其映射的物理地址以外,实际还需要有一些权限相关的信息,如用户级页表和内核级页表,实际就是通过权限进行区分的:



每个应表项中存储一个物理地址和一个虚拟地址就需要8个字节,考虑到还需要包含权限相关的各种信息,这里每一个表项就按10个字节计算:


  • 这里一共有2³²个表项,也就意味着存储这张页表需要用2³² * 10个字节,也就是40GB
  • 而在32位平台下我们的内存可能一共就只有4GB,也就是说我们根本无法存储这样的一张页表

💫 二级页表

  • 虚拟地址在被转化的过程中,不是直接转化的!而是拆分成了10 + 10 + 12
  • 以32位平台为例,其页表的映射过程如下:
  1. 选择虚拟地址的前10个比特位在页目录当中进行查找,找到对应的页表。
  2. 再选择虚拟地址的10个比特位在对应的页表当中进行查找,找到物理内存中对应页框的起始地址。
  3. 最后将虚拟地址中剩下的12个比特位作为偏移量从对应页框的起始地址处向后进行偏移,找到物理内存中某一个对应的字节数据。


  • 相关说明:
  1. 物理内存实际是被划分成一个个4KB大小的页框的,而磁盘上的程序也是被划分成一个个4KB大小的页帧的,当内存和磁盘进行数据交换时也是以4KB大小为单位进行加载和保存的。
  2. 4KB = 2¹²个字节,一个页框中有2¹²个字节,而访问内存的基本大小是1字节,因此一个页框中就有2¹²个地址,于是就可以将剩下的12个比特位作为偏移量,从页框的起始地址处开始向后进行偏移,从而找到物理内存中某一个对应字节数据。  


  • 这实际上就是所谓的二级页表,其中页目录项是一级页表,页表项是二级页表
  1. 每一个表项还是按10字节计算,页目录和页表的表项都是2¹º个,因此一个表的大小就是2¹º也就是10个字节,也就是10KB
  2. 页目录有2¹º个表项也就意味着页表有2¹º个,也就是说一级页表有1张,二级页表有2¹º张,总共算下来大概就是10MB,内存消耗并不高,因此Linux中实际就是这样映射的。  


  • **注意:**Linux中,32位平台用的是二级页表,64位平台用的是多级页表


🌙 进程基础概念

💫 线程是什么

概念:

在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列 ”一切进程至少都有一个执行线程;线程在进程内部运行,本质是在进程地址空间内运行在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流不同平台的多线程底层实现策略不一样,我们讨论Linux平台。


进程对应的模型:进程的创建实际上伴随着其进程控制块(task_struct)、进程地址空间(mm_struct)以及页表的创建,虚拟地址和物理地址就是通过页表建立映射的:



进程=内核数据结构+代码和数据,每个进程都有自己独立的进程地址空间和独立的页表,也就意味着所有进程在运行时本身就具有独立性,我们在创建“进程”时,只创建PCB,并要求创建出来的PCB不在独立创建,与父进程同享PCB,那么创建的结果就是下面这样的



因为我们可以通过虚拟地址空间+页表的方式对进程进行资源划分,单个“进程”执行力度一定要比之前的进程要细。上图中每个线程都是当前进程里的一个执行流,线程在进程内部运行,线程在进程的地址空间内运行,拥有该进程的一部分资源。


💫 如何理解线程

  • 每个进程都有自己独立的进程地址空间和独立的页表,也就意味着所有进程在运行时本身就具有独立性。
  1. 在创建进程时,它要创建PCB,页表,建立代码和数据的映射关系…
  2. 所以创建一个进程的成本非常高。


  • 如果创建"进程"时,只创建task_struct,并要求创建出来的task_struct和父task_struct共享进程地址空间和页表。
  1. 现在创建的进程不再给你独立分配地址空间和页表,而是都指向同一块地址空间,共享同一块页表。
  2. 所以这四个task_struct看到的资源都是一样的,后续可以通过某种方式把代码区拆分成4块,让这四个task_struct执行不同的代码区域。
  3. 上述的区域(数据区,堆区,栈区)也是类似处理方式。
  4. 换言之,后续创建的3个task_struct都各自有自己的一小份代码和数据,把这样的一份task_struct称之为线程。
  5. 其中每一个线程都是当前进程里面的一个执行流,也就是常说的"线程是进程内部的一个执行分支"。



  • 线程在进程内部运行,本质就是线程在进程地址空间内运行,也就是说曾经这个进程申请的所有资源,几乎都是被所有线程共享的。
  1. 线程比进程更细,是因为其执行的代码和数据更小了。
  2. 线程的调度成本更低了,是因为它将来在调度的时候,核心数据结构(地址空间和页表)均不用切换了。
  • 上述线程仅仅是在Linux下的实现,不同平台对线程管理可能不一样。
  1. 如Windows有真正的有关多线程的数据结构。
  2. 而Linux并没有真正的对线程创建对应的数据结构。
  3. Linux的线程是用进程PCB模拟的。
  4. 所以Linux并不能直接提供线程相关的接口,只能提供轻量级进程的接口。

             💦在用户层实现了一套用户层多线程方案,以库的方式提供给用户进行使用。

             💦pthread线程库。

  • CPU视角下,Linux下,PCB <= 其他OS内的PCB。
  1. Linux下的进程,统一称之为:轻量级进程。


💫 线程优点

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


💫 线程缺点

  • 性能损失:
  1. 一个很少被外部事件阻塞的计算密集型线程往往无法与其他线程共享同一个处理器。
  2. 如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失。
  3. 这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。


  • 健壮性降低:编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
  • 缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
  • 编程难度提高:编写与调试一个多线程程序比单线程程序困难得多。


💫 线程异常

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


💫 线程用途

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




【Linux】多线程——线程概念|进程VS线程|线程控制(下)     https://developer.aliyun.com/article/1565757

目录
相关文章
|
8天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
13天前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
10天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
22 1
|
15天前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
33 2
|
17天前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
25天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
16 2
|
25天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
28 2
|
22天前
|
Linux 调度
探索操作系统核心:进程与线程管理
【10月更文挑战第24天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅是计算机硬件与软件之间的桥梁,更是管理和调度资源的大管家。本文将深入探讨操作系统的两大基石——进程与线程,揭示它们如何协同工作以确保系统运行得井井有条。通过深入浅出的解释和直观的代码示例,我们将一起解锁操作系统的管理奥秘,理解其对计算任务高效执行的影响。
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
167 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)