概述

        cpu 运行速度特别快,但是IO等资源比较慢,cpu多数时间都在等待,为了提高cpu 的利用率。提出了进程的概念。当一个进程阻塞后,切换到另一个进程来提高cpu的利用率。操作系统就是通过进程来管理cpu的。

进程

程序是静态的,进程是描述程序动态的程序。存放运行中的状态。cpu跑多个进程的状态,就是cpu 管理。

多进程如何组织

PCB+状态+队列

PCB

PCB:就是这个进程在操作系统中的“身份证”和“个人档案”。通常包含如下信息:

  1. 进程标识信息(我是谁?)
  2. 处理机状态信息(我现在的状态是什么?)
  3. 进程调度信息(我排在哪?优先级多高?)
  4. 进程控制信息(我有什么资源?)

进程状态

队列

上述每一个线程状态对应一种队列,这种状态的线程都在该队列里。

多进程如何交替

调度器从“就绪队列”里挑出一个进程,把它的 PCB 里的寄存器数据恢复到 CPU 中,让它开始运行。那么就绪队列中有很多就绪的进程,如何确定取哪一个进程,这就涉及到了调度算法

调度算法

在就绪队列中有多个进程,应该选取哪一个。系统中的任务 大体分为两类 一类是CPU密集型任务,另一类是IO密集型任务。因此算法既要照顾cpu密集型任务也要照顾IO密集型任务。不能让一个任务一直运行,其他任务一直等待。最好是在调用cpu密集型任务时,把IO密集型任务调度起来。下面是一些常见的算法:

FCFS

先进先出

  • 原理:就像排队买票一样,谁先提交任务,谁就先获得 CPU 资源。
  • 特点:算法极其简单、公平。但它的致命缺点是对短作业不友好。如果前面来了一个执行时间很长的任务,后面一堆只需几毫秒的短任务就只能干等(这被称为“护航效应”)。
SJF 短作业优先

提高周转时间(cpu处理时间)

  • 原理:优先挑选预计执行时间最短的任务来运行。
  • 特点:正如你提到的,它能最大限度地降低平均等待时间和平均周转时间,整体吞吐量高。但缺点是可能导致“饥饿”——如果系统里一直有短任务进来,长任务可能永远轮不到执行。
轮转调度

提高响应时间

  • 原理:给每个任务分配一个固定的时间片(比如 10ms)。时间片用完,即使任务没执行完,也会被强制暂停并放回就绪队列尾部,等下一轮。
  • 特点:它是专门为分时系统设计的,能极大地提高系统的响应时间,让每个用户都感觉系统在同时为自己服务。不过,如果时间片设置得太小,频繁的上下文切换反而会浪费 CPU 性能。
设置优先级

设置优先级,任务的优先级应该动态执行。为了避免某一个进程一直在运行,还需要加入时间片机制。

以下是liunx 0.11 调度算法,动态设置优先级,避免优先级低的任务得不到执行。

yoid
Schedule(yoid)
{ while(1){ c-1;next0;iNR TASKS;
P=&taskINR TASKS];
while(--i){ if((*p->state = TASK RUNNING&&(*p)->counter>c)
c=(*p)->counter,next=i;
if(c)break;//找到了最大的counter
for(p=&LAST TASK;P>&FIRST TASK;--p
(*p)->counter=((*p)
>Counter>>1
+(*p)->priority;)
switch to(next);}

  • counter保证了响应时间的界
  • 经过io以后, counter就会变大;10时间越长, counter越大(为什么?),照顾了io进程,变相的照顾了前台进程
  • 后台进程一直按照counter轮转,近似了sjf调度
  • 每个进程只用维护一个counter变量, 简单, 高效

进程切换

把正在执行进程中的信息放到PCB 中,把新的进程的PCB 加载到寄存器中。

多进程同时存在内存会出现两个进程同时读取同一个内存,这就是需要映射表(映射表),把内存分块。

进程的切换分为两个部分:线程的切换,和资源的切换。

用户级线程

线程

进程=资源+指令执行序列

线程=指令执行序列

和进程相比线程更轻,切换更快,一个进程通常包含多个进程。

用户级线程

本质就是切换指令而不切换资源。

用户级线程:

优点

整个过程没有系统调用,没有上下文切换的开销,也没有内存映射的切换,纯粹是在用户态内存里拷贝几个寄存器的值,速度极快

缺点

因为内核不知道它的存在,所以内核只给这个进程分配了一个 CPU 核心。这意味着,即使你的电脑有 16 核,用户级线程也无法利用多核并行。而且,只要其中一个线程发起了系统调用(比如读文件),整个进程都会被内核挂起,导致其他所有线程跟着一起阻塞。

内核级线程

内核级线程示例图:

内核级线程切换

是两套栈进行切换,内核栈切换,对应的用户栈也要切。一般切换过程:“用户态 -> 内核态 -> 用户态”

如果线程中断就启用内核栈。首先先TCB 进行切换,然后内核栈进行切换,最后用户栈进行切换。这就完成了核心级线程的切换

用户级线程和内核级线程对比

时钟中断

为每个进程分配时间片,时间片结束,进程切换。从而实现一台电脑上打开多个程序。

java中的用户线程和核心级线程

  • 从 JDK 1.2 开始,Java 放弃了早期的“绿色线程”,主流 JVM(如 HotSpot)全面采用了基于操作系统原生线程的 1:1 内核级线程模型
  • JDK 21 并没有抛弃内核级线程,而是将两者结合。虚拟线程(用户级)负责提供极致的轻量和海量并发能力,而平台线程(内核级)则作为底层基石,负责与操作系统交互并充分利用多核 CPU 的并行能力。它采用了 M:N 混合模型。即大量(M)的虚拟线程会被挂载到少量(N)的平台线程(即底层的内核级线程)上执行。

       

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐