这次我们从底层把线程说清楚
由于硬件昂贵,多人分享一个终端,也就是多任务并行的需求引出了进程的概念。
随着硬件的发展和为了提高单个任务的效率,进程内部并发的需求引出了线程。
首先从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任务)
线程有三种基本状态:就绪、阻塞、运行。操作系统层面的阻塞函数(如I/O操作)便是在调用时将所在线程的状态改为了阻塞状态后让出CPU,操作系统的线程调度程序在选择要执行的线程时只会选择就绪态的线程,直到阻塞线程所调用的阻塞方法发起中断并返回后阻塞线程才可以继续向下执行。
上面说的线程的三种状态只是线程的基本状态,事实上不同的操作系统为了达到线程调度时不同的精度,可能会为线程定义更多的状态。只要可以方便线程的管理,状态的分类可以无穷多= =。但相同是,目前流行的操作系统中,都会包含上述三种状态作为线程的基本状态。
进程与线程是计算机科学中最深刻、最成功的概念之一。进程的经典定义便是一个运行中的实例,每个应用程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态组成的,这个状态包括内存中的代码和数据、栈、通用目的寄存器的内容、程序计数器、环境变量和打开文件描述符的集合。进程给应用程序提供了关键的两个抽象:
一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占的使用处理器。
一个独立的地址空间,它提供一个假象,好像我们的程序独占的使用内存系统。
总结一下:
1.进程和线程是操作系统层级对各个独立任务的抽象。线程的切换是cpu在不同任务的任务上下文间切换。
2.操作系统对线程进行调度(时钟中断),引导cpu不间断的执行不同任务的指令,以达到在不同任务间切换的效果。
3.进程是资源分配的基本单位,线程是运行调度的基本单位。进程和线程的分离是对任务抽象的更加成熟的结果,是对进程内部并发需求的实现。
@Author 牛有肉,转载请注明出处