Linux操作系统从入门到实战(二十一)进程切换与进程调度

Linux操作系统从入门到实战(二十一)进程切换与进程调度

  • 前言
  • 一、进程切换是什么?
  • 二、进程调度
    • [1. 为什么要进程调度?](#1. 为什么要进程调度?)
    • [2. Linux里有几类"调度员"?](#2. Linux里有几类“调度员”?)
    • [3. 进程排队用什么队形?](#3. 进程排队用什么队形?)
    • [4. CFS怎么做到"公平"?](#4. CFS怎么做到“公平”?)
    • [5. 什么时候会"换进程"?](#5. 什么时候会“换进程”?)
    • [6. 调度选进程、换进程,具体怎么操作?](#6. 调度选进程、换进程,具体怎么操作?)
    • [7. 多CPU时,活会分给谁?](#7. 多CPU时,活会分给谁?)
    • [9. Idle进程是做什么?](#9. Idle进程是做什么?)
    • 总结

前言

上一篇博客中,我们介绍了进程优先级;

  • 这一篇,我们将讲解进程切换与进程调度。

我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343

我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482


一、进程切换是什么?

1. 为什么需要进程切换?

你有没有想过,电脑明明只有一个CPU(或者几个核心),却能同时运行微信、浏览器、音乐播放器?比如你一边刷网页,一边听歌,还能聊微信------这其实是CPU在"骗"你。

CPU的"本事"是快速在不同任务(进程)之间切换,快到你感觉不到卡顿,误以为它们在同时运行。

  • 而CPU在不同任务之间切换的过程,就叫进程切换(也叫任务切换)。

2. 什么是"CPU上下文"?

切换任务时,有个很重要的问题

  • 比如你正在用浏览器看视频,突然切到微信发消息,等会儿再切回浏览器,浏览器得知道"刚才看到哪一秒了""视频缓冲到哪里了",不然就会从头开始。

CPU也一样,它处理一个任务时,会把这个任务的"当前进度"存在一些叫寄存器的硬件里。

  • 寄存器就像CPU的"小笔记本",记着:
    • 下一条要执行的指令存在内存的哪个位置;
    • 刚才计算到一半的临时数据(比如加法算到哪一步);
    • 任务的状态(比如是运行中、还是暂停了)。

这些存在寄存器里的"进度信息",就叫CPU上下文 ------简单说,就是"当前任务在CPU里的所有状态记录"。

2. 进程切换的完整步骤

当CPU决定"先放下当前任务,去处理另一个任务"时,就会执行"上下文切换",步骤分两步

第一步:保存当前任务的上下文

  • CPU会把"小笔记本"(寄存器)里记录的当前任务的所有信息,存到这个任务自己的堆栈里
  • 就像你暂停看视频时,播放器会自动记录"看到第30秒",存在自己的缓存里一样。

第二步:加载下一个任务的上下文

  • 然后,CPU会从下一个要运行的任务的"专属储物柜"(堆栈)里,把它上次存的"进度信息"(上下文)取出来,重新写到自己的"小笔记本"(寄存器)里
  • 就像你打开之前暂停的视频,播放器会从缓存里读"第30秒",接着播放。

做完这两步,CPU就开始处理新任务了------这整个过程,就是"进程切换"(上下文切换)。

举个例子

假设CPU要在"浏览器(任务A)"和"音乐播放器(任务B)"之间切换:

  1. 一开始CPU在处理任务A(浏览器加载网页),寄存器里记着"浏览器下一条指令在内存地址X,临时数据是50";
  2. 到了切换时间,CPU先把寄存器里的"X地址、数据50"存到任务A的堆栈里(保存上下文);
  3. 然后从任务B的堆栈里,取出它上次存的"下一条指令在内存地址Y,临时数据是30",写到寄存器里(加载上下文);
  4. 接下来CPU就按照寄存器里的信息,开始处理任务B(播放音乐);
  5. 过一会儿又要切回任务A,重复上面的步骤:先存B的上下文,再加载A的上下文,继续处理浏览器。

这样快速切换,我们就能感觉浏览器和音乐播放器在同时运行了。

二、进程调度

  • 我们每天用电脑时,可能同时开着浏览器、音乐播放器、文档编辑器......这些程序背后都是"进程",而CPU就像一个"超级打工人",得给这些进程安排干活的顺序------这就是"进程调度"要干的事。

1. 为什么要进程调度?

CPU那么快,直接让所有进程一起跑不行吗

  • 不行
    • CPU同一时间只能给一个进程干活(即使是多核心CPU,每个核心也只能同时处理一个进程)。但我们又需要"多任务"(比如边听歌边写文档),这就需要一个"调度员"来决定:现在该让哪个进程用CPU?用多久?下次轮到谁?

这个"调度员"的目标很简单:

  • 公平:普通进程别抢着用,大家分一分CPU时间;
  • 实时:紧急任务(比如游戏里的按键响应、工业设备的控制)必须马上处理,不能卡;
  • 高效:调度本身别太费时间,别让CPU光忙着安排任务,没空干活。

2. Linux里有几类"调度员"?

问题 :所有进程都按一个规则调度吗?

  • 进程有"急"有"缓",Linux专门设计了3类"调度员"(调度类),按优先级从高到低排:
  1. 实时调度类(RT) :管"急活"

    比如工厂里的机器控制、游戏里的画面刷新,这些任务不能等,优先级最高(1-99,数字越大越急)。

    它有两种规则:

    • SCHED_FIFO(先到先得):一旦开始跑,不主动停下就一直占着CPU;
    • SCHED_RR(时间片轮转):虽然急,但也不能一直占着,给个时间片(比如10ms),到点就换另一个同优先级的实时进程。
  2. 完全公平调度类(CFS) :管"普通活"

    我们日常用的浏览器、文档、音乐播放器,大多是这类。它的目标是"大家尽量公平用CPU",优先级对应一个叫nice的值(-20到19):

    • nice=-20:最"不谦让",能多占点CPU(对应调度优先级100);
    • nice=19:最"谦让",少占点CPU(对应调度优先级139)。
  3. Idle调度类 :管"没人干活时"

    只有当所有进程都"歇着"(比如电脑没开任何程序),它才出来"值班",让CPU跑点空操作,甚至帮CPU省电,优先级最低。

3. 进程排队用什么队形?

问题 :这么多进程,调度员怎么快速找到"下一个该干活的"?

  • 每个CPU都有一个"任务队列"(rq),里面按调度类分了小队伍:

  • 实时队列(rt_rq):像个"优先级货架",有99个格子(对应优先级1-99),每个格子里是同优先级的实时进程,排成链表。调度员找实时进程时,直接从最高优先级的格子里抓第一个就行,快得很。

  • CFS队列(cfs_rq) :用"红黑树"(一种平衡的二叉树)排的队。

    • 树里的进程按"虚拟运行时间(vruntime)"排序,vruntime越小,越靠前(越先被调度)。
      为啥用红黑树?因为它增删改查都快(像查字典找单词一样),能快速找到"最该先跑"的进程。

4. CFS怎么做到"公平"?

问题:普通进程那么多,怎么保证"你用一会儿,我用一会儿"?

  • CFS的秘诀是"虚拟运行时间(vruntime)"和"权重":

  • 权重nice值决定的"抢CPU能力"。nice=-20的进程权重高(1024),nice=19的权重低(128)------权重高的,就该多占点CPU。

  • vruntime计算vruntime = 实际运行时间 × (1024 / 权重)

    比如:

    • 高权重进程(nice=-20)跑1ms,vruntime只加1ms(1×1024/1024=1);
    • 低权重进程(nice=19)跑1ms,vruntime要加8ms(1×1024/128=8)。

这样一来,高权重进程的vruntime长得慢,在红黑树里总排在前面,能更频繁地被调度(多占CPU);低权重的则相反------看似"不公平",其实是按"优先级"分配的"比例公平"。

5. 什么时候会"换进程"?

问题 :调度员啥时候会喊"停,下一个"?

分两种情况:

  1. 主动让贤 :进程自己说"我先歇会儿"。

    比如你用浏览器点了"刷新",浏览器要等网络数据,就会主动放弃CPU,去"睡觉"(阻塞)。

  2. 被动打断:调度员强制换进程。

    • 时钟中断(比如每10ms响一次):检查当前进程是不是"用太久了"(比如RT的RR策略时间片到了,或CFS的vruntime该换了);
    • 高优先级进程被唤醒:比如你按了游戏按键,对应的实时进程醒来,直接把当前的普通进程"挤下去";
    • 新进程诞生:比如你新开了一个程序,可能比当前进程优先级高,直接抢占CPU。

6. 调度选进程、换进程,具体怎么操作?

  • 核心靠一个叫schedule()的"调度函数",步骤很简单:
  1. 挑调度类:先看有没有实时进程(RT),有就优先选;没有就选CFS的普通进程;都没有就选Idle进程。
  2. 选具体进程
    • RT类:从实时队列里挑最高优先级的第一个;
    • CFS类:从红黑树里挑vruntime最小的那个。
  3. 换任务:保存当前进程的"工作进度"(寄存器、栈等),加载新进程的"进度",让新进程开始用CPU。

7. 多CPU时,活会分给谁?

  • 电脑有多个CPU核心,会不会有的忙死、有的闲死

  • 有可能!所以需要"负载均衡":

  • 为啥要均衡:比如一个CPU核心跑了10个进程,另一个核心闲着,太浪费了,得把进程挪过去点。

  • 怎么均衡

    1. 定时检查各CPU的"负载"(进程数量+权重);
    2. 把负载高的CPU上的进程,挪到负载低的CPU上(优先挪普通进程,实时进程尽量不动,怕影响响应速度)。

9. Idle进程是做什么?

当所有进程都歇着,CPU在干啥?

这时每个CPU核心会跑一个"Idle进程"------它不是摸鱼,而是:

  • 跑点空操作,让CPU有事干(不然CPU可能"懵圈");
  • 触发硬件节能(比如降低CPU频率),帮电脑省电。

总结

  • 对紧急任务(RT):让它们优先跑,保证不卡顿;
  • 对普通任务(CFS):用vruntime和红黑树,按优先级公平分配CPU;
  • 多CPU时:均衡负载,不让任何核心闲着;
  • 没事干时:让Idle进程"看场子",还能省电。

以上就是这篇博客的全部内容,下一篇我们将继续探索Linux的更多精彩内容。

我的个人主页

欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343

我的Linux知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12879535.html?spm=1001.2014.3001.5482

|--------------------|
| 非常感谢您的阅读,喜欢的话记得三连哦 |