操作系统之线程!

并发(Concurrent)

并发是一个CPU处理器同时处理多个线程任务。

宏观上是同时处理多个任务,微观上其实是CPU在多个线程之间快速的交替执行CPU把运行时间划分成若干个(微小)时间段。

  • 公平的分配给各个线程执行,在一个时间段的线程运行时,其他线程处于挂起状态,这种就称之为并发。

并行(Parallel)

并行是多个CPU处理器同时处理多个线程任务。

当一个CPU执行一个线程时,另一个CPU可以执行另一个线程。

  • 两个线程互不抢占CPU资源,可以同时进行,这就被称之为并行。

同步异步/阻塞非阻塞

当一个request发送出去以后,会得到一个response,这整个过程就是一个同步调用的过程。

  • 哪怕response为空,或者response的返回特别快,但是针对这一次请求而言就是一个同步的调用。

当一个request发送出去以后,没有得到想要的response,而是通过后面的callback、状态或者通知的方式获得结果。

异步请求分两步:

  • 调用方发送request没有返回对应的response(可能是一个空的response)。
  • 服务提供方将response处理完成以后通过callback的方式通知调用方。

阻塞和非阻塞:

  • 阻塞和非阻塞就看调用方在发送请求后是否block住了。

协程

协程是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理。

  • 而完全是由程序所控制(也就是在用户态执行)。

这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

image-20231011095850049

用户态和内核态

Kernel 运行在超级权限模式(Supervisor Mode)下,所以拥有很高的权限。

按照权限管理的原则,多数应用程序应该运行在最小权限下。

因此,很多操作系统,将内存分成了两个区域:

  • 内核空间(Kernal Space),这个空间只有内核程序可以访问。
  • 用户空间(User Space),这部分内存专门给应用程序使用。

用户空间中的代码被限制了只能使用一个局部的内存空间,我们说这些程序在用户态(User Mode) 执行。

内核空间中的代码可以访问所有内存,我们称这些程序在内核态(Kernal Mode) 执行。

什么情况会导致用户态到内核态切换:

系统调用:

用户态进程主动切换到内核态的方式,用户态进程通过系统调用向操作系统申请资源完成工作。

  • 例如 fork()就是一个创建新进程的系统调用。

异常:

当 CPU 在执行用户态的进程时,发生了一些没有预知的异常。

这时当前运行进程会切换到处理此异常的内核相关进程中,也就是切换到了内核态,如缺页异常。

中断:

当 CPU 在执行用户态的进程时,外围设备完成用户请求的操作后,会向 CPU 发出相应的中断信号。

  • 这时 CPU 会暂停执行下一条即将要执行的指令,转到与中断信号对应的处理程序去执行,也就是切换到了内核态。

如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后边的操作等。

用户态和内核态切换的开销

保留用户态现场(上下文、寄存器、用户栈等)

复制用户态参数,用户栈切到内核栈,进入内核态

额外的检查(因为内核代码对用户不信任)

执行内核态代码

复制内核态代码执行结果,回到用户态

恢复用户态现场(上下文、寄存器、用户栈等)

异常和中断的区别

中断是由硬件设备产生的,而它们从物理上说就是电信号,之后它们通过中断控制器发送给CPU。

接着CPU判断收到的中断来自于哪个硬件设备(这定义在内核中)。

  • 最后,由CPU发送给内核,有内核处理中断。

异常是由CPU产生的,同时,它会发送给内核,要求内核处理这些异常。

系统调用过程:

如果用户态程序需要执行系统调用,就需要切换到内核态执行。

内核程序执行在内核态(Kernal Mode),用户程序执行在用户态(User Mode)。

当发生系统调用时,用户态的程序发起系统调用。

因为系统调用中牵扯特权指令,用户态程序权限不足,因此会中断执行,也就是 Trap(Trap 是一种中断)。

发生中断后,当前 CPU 执行的程序会中断,跳转到中断处理程序。

内核程序开始执行,也就是开始处理系统调用。

内核处理完成后,主动触发 Trap,这样会再次发生中断,切换回用户态工作。

线程调度算法

先到先服务(First Come First Service,FCFS)

就是先到的作业先被计算,后到的作业,排队进行。

最短作业优先

最短作业优先(Shortest Job First, SJF)调度算法通常会同时考虑到来顺序和作业预估时间的长短。

优先级队列(PriorityQueue)

优先级队列可以给队列中每个元素一个优先级,优先级越高的任务就会被先执行。

抢占(Preemption)

抢占就是把执行能力分时,分成时间片段。

让每个任务都执行一个时间片段。

如果在时间片段内,任务完成,那么就调度下一个任务。

  • 如果任务没有执行完成,则中断任务,让任务重新排队,调度下一个任务。

多级队列模型:

多级队列,就是多个队列执行调度。

image-20231011212323295

紧急任务仍然走高优队列,非抢占执行。

普通任务先放到优先级仅次于高优任务的队列中,并且只分配很小的时间片。

如果没有执行完成,说明任务不是很短,就将任务下调一层。

下面一层,最低优先级的队列中时间片很大,长任务就有更大的时间片可以用。

通过这种方式,短任务会在更高优先级的队列中执行完成,长任务优先级会下调,也就类似实现了最短作业优先的问题。