苦李  

  为了充分利用计算机硬件资源,要让计算机尽可能“同时”多做一些工作。“工作”是由处理器执行某段程序代码来完成的,这段程序代码就称为进程, 一个进程可以完成一项工作。 有的时候,工作往往并不简单,它由多个“子工作”组成,因此,我们需要让进程尽可能“同时”多做一些子工作,这些子工作也得由程序代码完成,完成这些子工作的程序就是线程。

 

 并行

  过去,计算机只有 1 个处理器,在其上运行的系统也是单任务操作系统,即不管有多少个任务,任务的执行都是串行的,一个任务彻底执行完成后才能开始下一个任务。

 

  考虑这么一种情况:假如有任务 A,它的执行要耗时一天,任务 B 的执行仅需要2 分钟,如果任务 A 先上处理器上运行,会严重滞后其他的任务执行计划。

  为了解决上述问题,在处理器数量不变的情况下,多任务操作系统出现了,它采用了一种称为多道程序设计的方式,使处理器在所有任务之间来回切换,这样就给用户一种所有任务并行运行的错觉,这称为“伪并行”,毕竟在任意时刻,处理器只会执行某一个任务,处理器会落到哪些任务上执行,这是由操作系统中的任务调度器决定的。

  

  核心:任务调度器就是操作系统中用于把任务轮流调度上处理器运行的一个软件模块,它是操作系统的一部分。调度器在内核中维护一个任务表(也称进程表、线程表或调度表),然后按照一定的算法,从任务表中选择一个任务,然后把该任务放到处理器上运行,当任务运行的时间片到期后,再从任务表中找另外一个任务放到处理器上运行,周而复始,让任务表中的所有任务都有机会运行。正是因为有了调度器,多任务操作系统才能得以实现,它是多任务系统的核心,它的好坏直接影响了系统的效率 。

 

 执行流

  任务并行这是用软件来切换任务模拟出来的假象,处理器根本就不在乎切换任务这回事。因为处理器只知道加电后按照程序计数器中的地址不断地执行下去,在不断执行的过程中,我们把程序计数器中的下一条指令地址所组成的执行轨迹称为程序的控制执行流。

  执行流对应于代码,大到可以是整个程序文件,即进程,小到可以是一个功能独立的代码块,即函数,而线程本质上就是函数。

  执行流是独立的,它的独立性体现在每个执行流都有自己的栈、一套自己的寄存器映像和内存资源,这是 Intel 处理器在硬件上规定的,其实这正是执行流的上下文环境。因此,我们要想构造一个执行流,就要为其提供这一整套的资源。其实任何代码块,无论大小都可以独立成为执行流,只要在它运行的时候,我们提前准备好它所依赖的上下文环境就行,这个上下文环境就是它所使用的寄存器映像、栈、内存等资源。

  在任务调度器的眼里,只有执行流才是调度单元,即处理器上运行的每个任务都是调度器给分配的执行流,只要成为执行流就能够独立上处理器运行了,也就是说处理器会专门运行执行流中的指令。

 

  进程和线程就是最典型的执行流。

 

 线程到底是什么?

  在高级语言中,线程是运行函数的另一种方式,也就是说,构建一套线程方法,让函数在此线程中被调用,然后处理器去执行该函数,因此线程实际的功能就相当于调用了这个函数。

  线程和普通函数调用的区别在哪呢?

  在一般的函数调用中,它是随着此函数所在的调度单元(执行流)一块上处理器运行的,这个调度单元也许是整个进程,也可能是其他的线程,总之是混在更大的执行流中被“夹杂着、稍带着”执行的,甚至有可能还未执行到此函数它的时间片就到了,从而被换下了处理器,因为调度单元是被调度器安排上处理器的,此函数不是单独的调度单元。

  线程是一套机制,此机制可以为一般的代码块创造它所依赖的上下文环境,从而让代码块具有独立性,因此在原理上线程能使一段函数成为调度单元(或称为执行流),使函数能被调度器“认可”,从而能够被专门调度到处理器上执行。这样,函数就可以被加入到线程表中作为调度器的调度单元,从而有机会单独获得处理器资源,也就是说,处理器不是把线程中调用的函数和其他指令混在一块执行的,或者说不是在执行整个进程时顺便执行了该函数,而是单独、专门执行了此函数。

 

  在线程中调用函数是让所运行的函数能够以调度单元的身份独立上处理器运行,当函数可以独立运行时,可以让程序中的多个函数(执行流)以并行的方式运行,为程序提速。

 

 程序、进程、线程关系和区别

  程序是指静态的、存储在文件系统上、尚未运行的指令代码,它是实际运行时程序的映像。

  进程是指正在运行的程序,即进行中的程序,程序必须在获得运行所需要的各类资源后才能成为进程, 资源包括进程所使用的枝,使用的寄存器等。

  对于处理器来说,进程是一种控制流集合,集合中至少包含一条执行流,执行流之间是相互独立的,但它们共享进程的所有资源,它们是处理器的执行单位,或者称为调度单位,它们就是线程。

 

  其实在处理器上运行的执行流都是“人为划分的”“逻辑上独立的”程序段,本质上都是一段代码区域,只不过线程是纯粹的执行部分,它运行所需要的资源存储在进程这个大房子中,进程中包含此进程中所有线程使用的资源,因此线程依赖于进程,存在于进程之中,用表达式来表示:进程=线程+资源。

  只有线程才具备能动性,它才是处理器的执行单元,因此它是调度器眼中的调度单位。进程只是个资源整合体,它将进程中所有线程运行时用到资源收集在一起,供进程中的所有线程使用,真正上处理器上运行的其实都叫线程,进程中的线程才是一个个的执行实体、执行流,因此,经调度器送上处理器执行的程序都是线程。

 

 进程的身份证--PCB

  现代操作系统都是多任务操作系统,每个任务要被调度到处理器上分时运行,运行一段时间后再被换下来,由调度系统根据调度算法再选下一个线程上处理器,思考以下问题:

  1. 要加载一个任务上处理器运行,任务由哪来?也就是说,调度器从哪里才能找到该任务?
  2. 即使找到了任务,任务要在系统中运行,其所需要的资源从哪里获得?
  3. 即使任务已经变成进程运行了,此进程应该运行多久呢?
  4. 即使知道何时将其换下处理器,那当前进程所使用的这一套资源(寄存器内容)应该存在哪里?
  5. 进程被换下的原因是什么?下次调度器还能把它换上处理器运行吗?
  6. 进程独享地址空间,这个地址空间在哪?
  7. ........

  为解决以上问题,操作系统为每个进程提供了一个PCB,Process Control Block,即程序控制块,它就是进程的身份证,用它来记录与此进程相关的信息,比如进程状态、 PID、优先级等

 

 进程表

 

  PCB大小一般为一页。PCB中寄存器映像的位置不一定在顶部。通常情况下进程或线程被中断时,处理器自动在 TSS 中获取内核栈指针,这通常是 PCB 的顶端,因此通常情况下“寄存器映像”位于 PCB 的顶端。但有时候进程或线程的上下文,也就是寄存器映像并不是在中断发生时保存到栈中的,而是在内核态下工作时,栈指针己经发生了变化时才向栈中保存寄存器映像,比如线程主动让出处理器,这时候就得保存线程的现场,此时“寄存器映像” 必然就不在 PCB 顶端了。

 

posted on 2022-02-10 23:31  苦李  阅读(118)  评论(0)    收藏  举报