news 2026/4/14 23:46:45

进程优先级和切换

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进程优先级和切换

第一部分:进程优先级 (Process Priority)

系统中的进程成百上千,但 CPU(核)可能只有几个。谁先用 CPU?这就涉及到竞争性。为了合理分配资源,必须要有优先级 。

1. PRI 与 NI:一对“相爱相杀”的数值

在 Linux 中,衡量进程优先级的指标主要有两个:PRI(Priority) 和NI(Nice)。

  • PRI (Priority)
    • 含义:这是进程真正的优先级。值越小,优先级越高(越早被执行)。
    • 现状:用户通常无法直接硬性修改这个值,它由内核动态调整。
  • NI (Nice)
    • 含义:这个名字很有趣,意为“这个进程有多‘友善’”。
    • 作用:它是优先级的修正数据
    • 取值范围-2019,共 40 个级别 。
      • 负值(如 -20):表示“我不友善”,我要抢 CPU ->优先级变高
      • 正值(如 19):表示“我很谦让”,CPU 给别人先用 ->优先级变低
2. 计算公式

内核计算最终优先级的公式非常简单:

  • 结论:调整进程优先级,在 Linux 下,本质上就是调整nice 值
    • nice为负值,PRI变小,优先级变高。
    • nice为正值,PRI变大,优先级变低。

查看命令:

输入 ps -l 可以看到:

  • UID:谁启动的。
  • PRI:最终优先级。
  • NI:nice 值。

第二部分:进程切换 (Context Switch)

既然进程有优先级,高优先级的来了,正在跑的低优先级进程就得让路。或者时间片到了,大家轮流坐庄。

问题是:进程 A 被切下去时,它跑到哪一行代码、寄存器里存的临时结果怎么办?

这就涉及到了上下文 (Context)

1. 什么是“上下文”?

CPU 内有一堆寄存器(eax,ebx,eip程序计数器,esp栈指针等)。

  • 硬件层面:CPU 里的寄存器只有一套(物理硬件)。
  • 软件层面:每个进程都认为自己独占这些寄存器。

进程的上下文:就是进程执行时,CPU 寄存器中所有的临时数据8。

2. 切换过程

当 OS 决定把进程 A 换下,把进程 B 换上时,会发生以下动作:

  1. 保存 (Save):把 CPU 寄存器里属于进程 A 的数据,统统打包保存到进程 A 的PCB (task_struct)中(具体是保存在内核栈或 TSS 结构里)。
  2. 恢复 (Restore):从进程 B 的 PCB 中,把它上次没跑完的寄存器数据读出来,填回 CPU 的寄存器里 。
  3. 执行:CPU 指针 (eip) 指向了进程 B 上次停下的地方,继续运行。

课件中的比喻:

就像你读书记笔记。你要去吃饭(被切走),必须把书签夹好(保存上下文)。等你回来(被调度回来),翻到书签页(恢复上下文),继续往下读,而不是从头读 11。


第三部分:Linux 内核的 O(1) 调度算法

问题:如果有 1000 个进程,OS 怎么快速找到优先级最高的那个去运行?如果遍历链表,时间复杂度是 O(N),进程越多越慢。

Linux 2.6 引入了 O(1) 调度算法,不管有多少进程,查找时间都是固定的常数!

1. 核心数据结构:runqueue(运行队列)

每个 CPU 都有一个运行队列。在这个队列里,有两个关键的指针数组结构:活动队列 (Active)过期队列 (Expired)

结构如下(prio_array):

  • queue[140]:这是一个数组,包含了 140 个链表。
    • 优先级映射:第 0-99 号下标对应实时进程(我们暂不关心),100-139 号下标对应普通进程的优先级
    • 哈希桶原理:优先级为 100 的进程全挂在queue[100]的链表上,优先级 101 的挂在queue[101]上。
  • bitmap[5]:位图。用来标记哪个优先级的链表不为空。查找非空队列时,不需要遍历数组,直接查位图,效率极高 。
2. 运作机制
  1. 调度谁?

调度器直接去 活动队列 (Active) 里找优先级最高的(下标最小的)非空链表,拿出第一个进程去跑。因为有位图,这个过程是 O(1) 的 。

  1. 时间片用完了怎么办?

当一个进程时间片耗尽,它会被从 Active 队列 拿下来,重新计算优先级,然后扔到 Expired 队列 里去 。

  1. 乾坤大挪移 (关键!)

随着时间推移,Active 队列里的进程越来越少(都跑完去 Expired 了),Expired 队列里的进程越来越多。

当 Active 队列空了怎么办?

OS 只需要交换两个指针:activeexpired指针互换!

    • 原来的 Expired 瞬间变成了 Active,里面装满了分配好新时间片的进程。
    • 原来的 Active 变成了空荡荡的 Expired,准备接收下一波跑累的进程。
总结
  • 优先级 (PRI/NI)决定了进程在queue[140]数组中的位置。
  • 上下文切换保证了进程可以暂停和恢复。
  • O(1) 调度器利用双队列 (Active/Expired)指针交换,保证了在海量进程下系统依然极其流畅。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!