欢迎光临我的博客[http://poetize.cn],前端使用Vue2,聊天室使用Vue3,后台使用Spring Boot
任务调度(并发)
大部分操作系统(如Windows、Linux)的任务调度是采用时间片轮转的抢占式调度方式,
也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行。
由于CPU的执行效率非常高,时间片非常短,在各个任务之间快速地切换,
给人的感觉就是多个任务在“同时进行”,这也就是我们所说的并发。
进程(资源管理+线程)
进程:
是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,
是操作系统进行资源分配和调度的一个独立单位,
是应用程序运行的载体。
进程一般由程序、数据集合和进程控制块三部分组成:
程序:用于描述进程要完成的功能,是控制进程执行的指令集。
数据集合:是程序在执行时所需要的数据和工作区。
程序控制块(Program Control Block,简称PCB):包含进程的描述信息和控制信息,是进程存在的唯一标志。
进程具有的特征:
动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
并发性:任何进程都可以同其他进程一起并发执行;
独立性:进程是系统进行资源分配和调度的一个独立单位;
结构性:进程由程序、数据和进程控制块三部分组成。
进程由:
内存空间(代码、数据、进程空间、打开的文件)。
一个或多个线程。
进程挂起
挂起是系统层面对进程作出的合理操作。
挂起的标志就是换出到外存,在外存的进程肯定是不能执行的,所以挂起的目的就很明显:
在内存资源不足时,需要把一些进程换出到外存,给着急运行的进程腾地方。
挂起倾向于换出阻塞态的进程,也可以是就绪态的进程。
就绪态:进程在内存中并可以执行。
阻塞态:进程在内存中并等待一个事件。
阻塞/挂起态:进程在外存中并等待一个事件。
就绪/挂起态:进程在外存中,但是只要被载入内存就可以执行。
阻塞和挂起之间的相互转换如下:
阻塞→阻塞挂起:
如果没有就绪进程,则至少一个阻塞进程被换出,为另一个没有阻塞的进程让出空间。
如果操作系统确定当前正在运行的进程,或就绪进程为了维护基本的性能要求而需要更多的内存空间,
那么,即使有可用的就绪态进程也可能出现这种转换。
阻塞挂起→就绪挂起:
如果等待的事件发生了,则处于阻塞挂起状态的进程可以转换到就绪挂起状态。
注意,这要求操作系统必须能够得到挂起进程的状态信息。
就绪挂起→就绪:
如果内存中没有就绪态进程,操作系统需要调入一个进程继续执行。
此外,当处于就绪挂起态的进程比处于就绪态的任何进程的优先级都要高时,也可以进行这种转换。
这种情况的产生是由于操作系统设计者规定调入高优先级的进程比减少交换量更重要。
就绪→就绪挂起:
通常,操作系统更倾向于挂起阻塞态进程而不是就绪态进程,因为就绪态进程可以立即执行,
而阻塞态进程占用了内存空间但不能执行。
但如果释放内存以得到足够空间的唯一方法是挂起一个就绪态进程,那么这种转换也是必需的。
并且,如果操作系统确信高优先级的阻塞态进程很快将会就绪,
那么它可能选择挂起一个低优先级的就绪态进程,而不是一个高优先级的阻塞态进程。
进程状态图
进程切换
进程切换发生情况:
时钟中断,操作系统确定当前正在运行的进程的执行时间已经超过了最大允许时间,即要切换至就绪态
IO中断,等待IO活动事件的进程在IO活动发生时,操作系统需要把相应的阻塞态或阻塞挂起态切换至就绪或就绪挂起态
内存失效,当某一个进程中处理器需要将外存的内存块调入内存中,发出IO请求时该内存将切换至阻塞态;
当调入内存中后,该进程切换至就绪态
进程切换步骤:
保存处理器上细纹,PSW和寄存器
更新当前处于运行态进程的进程控制块,状态切换以及更新记账信息和离开愿意
将进程控制块移入相应队列
选择另一个进程执行
更新选择后的进程控制块,运行状态更改及更新相关信息
更新内存管理的数据结构
恢复处理器被选择的进程前一次切换出运行状态时保存下来的上下文环境
进程间切换
线程
简介:
在早期的操作系统中并没有线程的概念,
但是进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。
于是就发明了线程,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。
一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。
一个标准的线程由以下组成:
线程ID
当前指令指针(PC)
寄存器
堆栈
线程包含:
线程执行状态
未运行时保存的线程上下文
执行栈
线程内局部变量的静态存储空间
与进程内其他线程共享的对进程内存和资源的访问
线程的优点:
一个进程中可以同时存在多个线程
各个线程之间可以并发地执行
各个线程之间可以共享地址空间和文件等资源
线程相对于进程突出的性能:
创建快
终止速度快
切换速度快
提高了执行程序间的通信效率,进程间通信介入内核,而线程之间可以直接共享内存和文件
线程模型
进程与线程的区别
进程与线程的区别:
1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的
内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)
4. 调度和切换:线程上下文切换比进程上下文切换要快得多。
进程 = 资源(包括寄存器值,PCB,内存映射表)+ TCB(栈结构)
线程 = TCB(栈结构)
线程 的切换只是切换PC,切换了TCB(栈结构)
进程 的切换不仅要切换PC,还包括切换资源,即切换内存映射表
内核线程
内核线程(Kernel Thread,KLT):
由内核完成线程的创建,管理和终止。
就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,
内核通过操作调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。
内核级线程又称轻型进程lwp(light weight process):
切换由内核控制,当线程进行切换的时候,由用户态转化为内核态。
切换完毕要从内核态返回用户态;可以很好的利用smp,即利用多核cpu。
优点:
1、内核可以同时将进程中的多个线程调度到多个处理器;
2、进程中的一个线程被堵塞,内核可以调度同一个进程的另一个线程
缺点:
1、一个线程传送至同一个进程中的线程需要内核的状态切换
用户线程
用户线程:
用户级线程的创建、撤消和调度不需要OS内核的支持,是在语言(如Java)这一级处理的。内核意识不到线程的存在。
每个用户线程都由一个内核线程支持,因此只有先支持内核线程,才能有用户线程。
通过线程库管理用户级线程:
包含创建和销毁线程代码、在线程间传递消息和数据的代码、调度线程执行的代码以及保存和恢复线程上下文的代码。
优点:
1、由于所有线程管理数据结构都在一个进程的用户地址空间,线程的切换不需要内核参与,节省了两次模式转换的开销;
2、调度由应用程序完成;
3、用户级线程可以在任何操作系统上执行而不涉及修改底层内核
缺点:
1、当用户线程执行一个系统调用时,这个线程的堵塞将造成整个进程的堵塞;
2、一个多线程应用程序不能利用多处理技术
用户级线程和内核级线程的区别
用户级线程执行系统调用指令时将导致其所属进程被中断,而内核支持线程执行系统调用指令时,只导致该线程被中断。
在只有用户级线程的系统内,CPU调度还是以进程为单位,处于运行状态的进程中的多个线程,由用户程序控制线程的轮换运行
在有内核支持线程的系统内,CPU调度则以线程为单位,由OS的线程调度程序负责线程的调度。
多线程与多核(并行)
多核处理器:
是指在一个处理器上集成多个运算核心从而提高计算能力,
也就是有多个真正并行计算的处理核心,每一个处理核心对应一个内核线程。
超线程技术:
一般一个处理核心对应一个内核线程,比如单核处理器对应一个内核线程,
双核处理器对应两个内核线程,四核处理器对应四个内核线程。
现在的电脑一般是双核四线程、四核八线程,是采用超线程技术将一个物理处理核心模拟成两个逻辑处理核心,
对应两个内核线程,所以在操作系统中看到的CPU数量是实际物理CPU数量的两倍。
超线程技术就是利用特殊的硬件指令,把一个物理芯片模拟成两个逻辑处理核心,
让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的运行效率。
用户线程与内核线程的对应关系有三种模型:一对一模型、多对一模型、多对多模型
一对一模型:
一个用户线程就唯一地对应一个内核线程
优点:一个线程因某种原因阻塞时其他线程的执行不受影响
缺点:用户线程的数量受到限制;许多操作系统内核线程调度时,上下文切换的开销较大,导致用户线程的执行效率下降。
多对一模型:
多个用户线程映射到一个内核线程上,线程之间的切换由用户态的代码来进行。
优点:线程数量没有限制。
缺点:
如果其中一个用户线程阻塞,那么其它所有线程都将无法执行,因为此时内核线程也随之阻塞了。
在多处理器系统上,处理器数量的增加对多对一模型的线程性能不会有明显的增加,因为所有的用户线程都映射到一个处理器上了。
多对多模型(常用):
多对多模型结合了一对一模型和多对一模型的优点,将多个用户线程映射到多个内核线程上。
优点:
一个用户线程的阻塞不会导致所有线程的阻塞,因为此时还有别的内核线程被调度来执行。
多对多模型对用户线程的数量没有限制。
在多处理器的操作系统中,多对多模型的线程也能得到一定的性能提升,但提升的幅度不如一对一模型的高。
现在流行的操作系统中,大都采用多对多的模型。
多对一
多对多