您的位置:首页 > 其它

这次我们从底层把线程说清楚

2019-11-17 17:44 2381 查看

  从业两年半,零零散散学习和写过了不少多线程的代码,但对于线程的理解一直处于朦朦胧胧的状态。对于线程的概念、模型、使用场景和使用方法越来越熟悉,但对于“线程到底是什么”这一问题越来越没底,今天我将零散的点和概念聚集起来,从底层向上逐层来看一下线程到底是什么。  

  

  首先从cpu说起,我们知道,cpu执行指令分为三个阶段:取指令阶段、指令译码阶段、指令执行阶段。

  对于cpu来说,并不存在线程这一概念,cpu眼里只有指令和数据。对于内存中的信息,哪些是指令哪些是数据完全依靠cs寄存器和ip寄存器的指向来判断。我们有两个程序(假设都是单线程且没有交互),对于cpu来说它们只是内存中的两块区域的代码,相互独立没有关系。cpu在执行A程序的代码时便是在执行A任务,执行B程序的代码时便是在执行B任务。如果它们是并发执行的,那么对于cpu来说,便是一会儿在执行任务A,一会儿在执行任务B。也就是说在cpu眼中,线程是独立的任务,线程的切换是cpu在交替的执行不同的任务。

  真正实现线程这一概念,并指引cpu在不同的任务间切换的是操作系统。为了方便描述任务并以此为依据对任务进行调度,操作系统为任务维护了一个专用的数据结构TCB(Thread Control Block,线程控制块)。说到TCB,我们不得不按照时序来说一下PCB(Process Control block)。

  在早期的操作系统实现中并不存在线程的概念,与之对应的是进程。也就是说当时的操作系统粗暴的将一个任务整体的描述为一条执行线,操作系统以任务为单位进行资源分配,并以任务为单位进行运行调度。这样分配的结果便是,每次进行运行调度即进程切换时,也要同时切换资源的权限,造成非常大的开销。

  于是后来的操作系统将资源分配单位和任务调度单位分离开来,也就是我们现在所说的进程与线程。以进程为单位分配系统资源,以线程为单位进行任务调度。线程拥有所属进程的所有系统资源,这样cpu在同一进程的线程间切换时便不必同时切换资源的权限,同时一个进程也拥有了在进程内部并发甚至并行处理的能力(一个大任务有了并行或并发处理子任务的能力),大大提高了运行的效率。(与面向对象编程思路类似,在不符合单一职责原则时对任务这一对象进行了进一步的抽象,按职责进行分治。)

  回到原来的话题,让我们看看操作系统是如何使用PCB和TCB来描述进程和线程的,下面是百度百科中PCB的描述:

  

 

   我们可以看到,PCB中包含了资源分配信息和运行调度信息。其中:进程状态、CPU排班法为进行运行调度时的依据,CPU寄存器、程序计数器为运行调度时保存和恢复现场的依据,存储管理器、会计信息、输入输出状态则是对进程拥有的资源的描述。

  对于TCB来说,不同操作系统有着不同的实现,但大致都是仅包含了运行调度时所需的信息,如线程状态、调度算法、CPU寄存器、PC计数器等。PCB与TCB的关系如下图所示:

 

   操作系统通过上述两个数据结构对任务进行了管理,然后不断的以此为依据来引导cpu在不同的任务间切换,我们以时间片轮转法为例子看一下操作系统引导cpu的过程:

  cpu执行任务A------>任务A的时间片用完,OS发出时钟中断终止cpu的执行------->保存终止任务A前的任务现场(cpu寄存器、pc计数器等)--------->从所有有效的TCB中按特定算法算出下一个被执行的任务B---------->将B任务的现场恢复,开始执行B任务(cs:ip指向了B任务上次终止时执行到的指令,cpu从该处再次开始执行B任务)

  总结一下:

  1.进程和线程是操作系统层级对各个独立任务的抽象。线程的切换是cpu在不同任务的指令间切换。

  2.操作系统对线程进行调度,引导cpu不间断的执行不同任务的指令,以达到在不同任务间切换的效果。

  3.进程是资源分配的基本单位,线程是运行调度的基本单位。进程和线程的分离是对任务抽象的更加成熟的结果。

  随着操作系统的发展,为了提高运行效率人们对任务的抽象进一步细化,在线程中又剥离出了用户线程与核心线程的概念,我们留着下期再扯。

 

@Author 牛有肉,转载请注明出处

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐