【 linux 】进程的调度算法


目录

1.进程优先级

[2. 操作系统的调度算法](#2. 操作系统的调度算法)

[2.1 运行队列的结构](#2.1 运行队列的结构)

[2.2 active和expired的比较](#2.2 active和expired的比较)

[2.3 O(1)调度器的弊端](#2.3 O(1)调度器的弊端)


1.进程优先级

进程优先级是调度器决定下一个谁运行的核心数据,数值越小,优先级越高。

实时进程的优先级最高,数值为0-99

普通进程可以通过nice renice调整优先级。对于普通用户nice的范围是0-19,超级用户还可以设置-20至-1

cs 复制代码
创建新进程时设置优先级
nice -n -5 ./my_server      
nice -n 19 ./backup.sh      

renice用于修改已存在进程的nice值

cs 复制代码
# 按 PID 调整
renice -n -10 -p 1234       # 将 PID=1234 的进程 NI 改为 -10

# 按用户/进程组批量调整
renice -n 5 -u username     # 该用户所有进程 NI +5
renice -n 0 -g 5678         # 进程组 GID=5678 的所有进程 NI 设为 0

nice值在操作系统内部存储时还要加上默认值,通常为120,最后的范围是100-139

在 Linux 内核头文件 include/linux/sched/prio.h 中,有如下宏定义:

cs 复制代码
#define MAX_RT_PRIO       100      // 实时优先级最大值(不含)
#define MAX_PRIO          140      // 总优先级数量
#define DEFAULT_PRIO      (MAX_RT_PRIO + NICE_WIDTH / 2) // 即 100 + 40/2 = 120
#define NICE_WIDTH        (MAX_PRIO - MAX_RT_PRIO)       // 即 140 - 100 = 40

2. 操作系统的调度算法

2.1 运行队列的结构

一个CPU,一个运行队列(struct runqueue),核心有两个优先级数组 active和expired,数组的结构如下

cs 复制代码
struct prio_array {
    unsigned int nr_active;          // 该数组中的进程总数
    unsigned int bitmap[BITMAP_SIZE]; // 优先级位图
    struct list_head queue[MAX_PRIO];  // 140个优先级链表
};

unsigned int类型一共有32个比特位,数组大小通常为5,共有160个比特位,正好对应140个优先级,每个位为1代表这个位置有进程

nr_active是一个计数器,用于计算进程数,判断系统负载

struct list_head queue是140个优先级链表,每个优先级对应一个双向链表头,挂载该优先级上的所有进程

抽象图如下

通过位图的方法能够快速找到下一个等待执行的进程,从而达到了O(1)级别。

2.2 active和expired的比较

调度器永远只从 active 数组中选取下一个要运行的进程。

expired不参与调度选择,它只是一个**等待区。**存放那些本轮时间片已耗尽、但尚未获得新一轮时间片的普通进程

当nr_active变成0时,交换两个数组的指针,expired变成active继续被调度。上一轮的active变成expired自动刷新进入新一轮

nr_active变成0意味着当前 CPU 的 active 数组中没有任何处于可运行状态的进程

通常由四种场景触发

1.正常耗尽,时间片用完被移入expired

2.主动阻塞,进程进入睡眠/等待状态,直接移出运行队列

3.进程终止,调用exit()或被信号杀死,移出运行队列

4.在多核系统中负载均衡机制,进入其他cpu的运行队列

其中第二种情况最为常见

完整的生命周期

cs 复制代码
新进程 ──→ active
              │
              ├── 被调度执行 → 时间片耗尽(非交互) ──→ expired
              ├── 被调度执行 → 时间片耗尽(交互式) ──→ 重回 active
              ├── 未轮到执行 → 主动睡眠/阻塞 ──→ 移出运行队列(不进expired)
              ├── 未轮到执行 → 被迁移到其他CPU ──→ 移出本CPU active
              └── 被更高优先级抢占 → 留在 active 原位(保留剩余时间片)

当且仅当 active->nr_active == 0 时:
    swap(active, expired)
    // 原 expired 中所有进程自动获得新一轮时间片
    // 注意:这些进程可能在上轮也没全部跑过!

创建两个优先级数组也是一种空间换时间的方法。

2.3 O(1)调度器的弊端

如果一直fork创建新进程,那nr_active永远无法变为0,expired中的老进程就会被饿死。如果一直有高优先级抢占进程,且没有进程阻塞和退出,那理论上也会存在进程饥饿的问题。

为了解决,Linux 2.6.23 引入CFS时,彻底废弃了 active/expired 双数组结构,从根本上消除了"指针交换"这个前提条件:

  • 没有"批次"概念,每个进程独立按 vruntime 排序
  • 新创建的进程继承父进程的 vruntime,并加上一个小的偏移量(防止 fork 轰炸获得不公平优势)
  • 无论有多少新进程涌入,红黑树中最左节点(vruntime 最小的进程)总是会被选中
  • 老进程的 vruntime 不会因为"没轮到交换"而停滞,它们只是在树中位置相对靠右,但仍然有确定的、可计算的调度机会

相关推荐
名字还没想好☜4 小时前
用 systemd 托管后台服务:Restart 策略、日志接管、资源限制与开机自启全踩一遍
运维·docker·kubernetes
乌恩大侠5 小时前
GH200 新增 NVMe SSD 分区、格式化与挂载完整流程
运维·服务器·前端
tangwangbi5 小时前
Linux 系统配置文件:/etc/profile、~/.bashrc 和 ~/.bash_profile 三者之间的区别与作用
linux·运维·bash
蜀道山老天师5 小时前
Shell Bash变量与运算符(含条件测试与流程控制)
linux·运维·bash
Mr.朱鹏5 小时前
Linux 服务器 LVM 根分区在线动态扩容
linux·服务器·数据库
草邦设计开发团队_媒体资源平台5 小时前
GEO 信源整合一键发布软文:从内容生产到流量获客的自动化实践
运维·人工智能·自动化
zhou lily5 小时前
非标自动化管理系统ERP如何选?2026年10大ERP软件对比分析
运维·自动化
mengge.cloud5 小时前
存储技术基础小白教程
linux·运维·服务器·wpf·存储
云贝贝贝6 小时前
腾讯云 TDSQL(MySQL 版)运维高频 6 坑:代理路由、读写分离、分片键、监控、PITR
运维·mysql·腾讯云
小五传输6 小时前
FTP替代怎么做?医院文件安全传输选型与迁移指南
大数据·运维·安全