﻿# chrt —— 查看/修改进程的调度策略与实时优先级

## 这个工具是做什么的

`chrt`（change real-time attributes）用来**查看或修改一个进程/线程的调度策略和优先级**。Linux 内核有五层调度类（见 [scheduling.md](/concepts/process/scheduling.md)），默认的 `SCHED_OTHER` 只是其中一层——还有 `SCHED_FIFO`、`SCHED_RR`、`SCHED_DEADLINE` 等实时调度策略。`chrt` 就是操作这些策略的命令行接口。

## 可以回答什么问题

| 问题 | 怎么用 |
|------|--------|
| 某个进程用的是哪种调度策略？是普通还是实时？ | `chrt -p <PID>` |
| 某个进程的实时优先级是多少？ | `chrt -p <PID>` 看 `current scheduling priority` |
| 想测试把进程改成实时调度会不会改善延迟？ | `chrt -f -p 50 <PID>` |
| 应用要求必须跑在 `SCHED_FIFO` 上，设对了吗？ | `chrt -p <PID>` 确认 `scheduling policy` |
| 想给内核迁移线程/中断线程改优先级？ | 通常不需要——内核关键线程已有合适的调度类 |

## 数据来源

- **读取**：`sched_getattr(2)` / `sched_getscheduler(2)` 系统调用，直接从 `task_struct` 的 `sched_class`/`prio` 字段读取。
- **修改**：`sched_setattr(2)` 系统调用，受 `CAP_SYS_NICE` 能力限制（提升优先级需 root）。

## 一、查看：`chrt -p`

```bash
chrt -p <PID>               # 查看指定进程
chrt -p <TID>               # 查看指定线程（TID）
chrt -p $$                  # 查看当前 shell
```

输出示例：

```bash
pid 1234's current scheduling policy: SCHED_OTHER
pid 1234's current scheduling priority: 0
```

```bash
pid 5678's current scheduling policy: SCHED_FIFO
pid 5678's current scheduling priority: 50
```

| 输出项 | 含义 | 对应内核 |
|--------|------|---------|
| `scheduling policy` | 调度策略：`SCHED_OTHER`/`SCHED_FIFO`/`SCHED_RR`/`SCHED_BATCH`/`SCHED_IDLE`/`SCHED_DEADLINE` | `task_struct.policy` |
| `scheduling priority` | 优先级值 | `task_struct.rt_priority`（实时）或 0（普通） |

## 二、修改调度策略（常用选项）

```bash
# --- 实时调度 ---
chrt -f -p 50 <PID>          # 改成 SCHED_FIFO，优先级 50（0-99，越高越优先）
chrt -r -p 50 <PID>          # 改成 SCHED_RR（时间片轮转实时），优先级 50
# --- 普通调度 ---
chrt -o -p 0 <PID>           # 改回 SCHED_OTHER（普通分时调度）
chrt -b -p 0 <PID>           # 改成 SCHED_BATCH（批处理，更不爱抢占）
chrt -i -p 0 <PID>           # 改成 SCHED_IDLE（极低优先级，只在空闲时跑）
# --- 启动新进程时指定 ---
chrt -f 50 ./my_realtime_app     # 以 FIFO 实时优先级 50 启动新程序
chrt -r 10 command               # 以 RR 优先级 10 启动
```

| 选项 | 策略 | 含义 | 优先级范围 |
|------|------|------|-----------|
| `-f` / `--fifo` | `SCHED_FIFO` | 固定优先级、**不设时间片**：跑到底才让出，或被更高优先级抢占。一直占着 CPU 直到主动阻塞/yield | 1-99（默认 0 不高不低） |
| `-r` / `--rr` | `SCHED_RR` | 固定优先级但**带时间片轮转**：同优先级的多个 RR 任务轮流跑，每个跑满时间片后换下一个 | 1-99 |
| `-o` / `--other` | `SCHED_OTHER` | 默认普通分时（CFS/EEVDF），nice 值影响权重而非硬优先级 | nice -20~+19 |
| `-b` / `--batch` | `SCHED_BATCH` | 批处理：不会被唤醒抢占，比 OTHER 更不打扰交互任务 | nice -20~+19 |
| `-i` / `--idle` | `SCHED_IDLE` | 极低优先级：只在**没有任何 OTHER/BATCH/RT 任务要跑时**才得到 CPU | — |
| `-d` / `--deadline` | `SCHED_DEADLINE` | 硬实时截止期限调度，需同时指定 `--sched-runtime` `--sched-deadline` `--sched-period` 三个参数 | — |

### 实时策略的执行区别

```bash
SCHED_FIFO 优先级 99：---------一直跑--------->（不主动让出就饿死所有低优先级）
SCHED_FIFO 优先级 50：   ----被 99 抢占----   ----重新跑----
SCHED_RR 优先级 50-1：  |A|B|A|B|  （同优先级轮流，每个时间片后切）
SCHED_RR 优先级 50-2：  |C|C|C|    （不同优先级之间，高的一直跑）
SCHED_OTHER:             ........nice 加权公平........（被实时任务完全饿死）
```

> ⚠️ **`SCHED_FIFO` 非常危险**：一个 `SCHED_FIFO 99` 的任务如果在用户态死循环（没有 `sleep`/阻塞），它会**永久占住 CPU**——连内核的 per-CPU 工作队列都得不到 CPU，系统会看起来像挂了一样。内核有 `sched_rt_runtime_us` 限流兜底（默认实时任务每 1 秒最多跑 0.95 秒，留 0.05 秒给普通任务），但不能完全信赖它，**实时优先级要慎之又慎**。

## 三、`SCHED_DEADLINE` 用法（硬实时截止期限）

这是 Linux 3.14+ 引入的最严格调度策略——不给固定优先级，而是给**运行时间/截止期限/周期**三个约束，内核保证在截止期限内完成任务：

```bash
# 语法：chrt -d --sched-runtime <RT> --sched-deadline <DL> --sched-period <PR> <command>
chrt -d --sched-runtime 5000000 --sched-deadline 10000000 --sched-period 10000000 0 ./my_dl_app
#       3 个参数单位是纳秒(ns), 0 是占位符(被忽略)
#       含义：每 10ms 周期内，保证有 5ms 的 CPU 时间
```

- `--sched-runtime`：每周期内保证的运行时间
- `--sched-deadline`：任务必须在此时间前完成
- `--sched-period`：调度周期

三个参数必须满足：`runtime ≤ deadline ≤ period`。需要 `CAP_SYS_NICE`。

> 这不是性能调优的常规操作，是硬实时系统的需求——普通性能排查 99% 的场景用不到。

## 四、典型使用场景与示例

### 场景1：排查一个进程为何 CPU 利用率很高

```bash
# 先看进程的调度策略——是不是某个实时任务抢了所有 CPU
chrt -p $(pgrep cpu_demo)
# SCHED_OTHER → 正常，非实时
# SCHED_FIFO 99 → 这本身就是问题！如果不是故意设置的，谁把它提权的
```

### 场景2：测试延迟敏感性——临时提升到实时

```bash
# 把待测程序提权到 FIFO 实时优先级 50，测延迟抖动
chrt -f 50 ./my_latency_sensitive_app
# 另开终端观测调度情况
pidstat -w -p $(pgrep my_latency_sensitive_app) 1
# 看 cswch/s（自愿切换）、nvcswch/s（非自愿抢占）的变化
# 实验完记得杀掉，别让实时任务长久跑着
```

### 场景3：发现系统被实时任务"锁住"

```bash
# 症状：top 里 %Cpu sy 不高，但 SSH 反应很慢，或部分核 idle 一直是 0
# 排查：找出所有非 SCHED_OTHER 的进程
ps -eo pid,tid,class,rtprio,ni,comm | grep -v "TS"
# class=FF(FIFO)/RR 且 rtprio 高的，就是嫌疑犯
# 处理方法：
chrt -o -p 0 <PID>           # 把它改回普通调度
# 或
kill -9 <PID>                 # 杀不掉？FIFO 99 的进程优先响应中断信号——先用 chrt 降级再杀
```

## 五、常见指标判读

| 观测手段 | 指标 | 判读 |
|---------|------|------|
| `chrt -p <PID>` | `SCHED_FIFO` + 高 rtprio | 实时任务，确认是故意设置的 |
| `ps -eo class,rtprio,comm` | class=`FF`/`RR` | 系统中所有实时任务一览 |
| `pidstat -w -p <PID>` | `nvcswch/s` | FIFO 任务这个值应该很低（不被抢占）。如果高 = 有更高优先级的任务在抢 |
| `top -H` | `PR` 列出现负数 | 实时优先级：`PR = -rtprio - 1`（如 rtprio=50 显示 PR=-51） |
| `/proc/<PID>/sched` | `policy` 字段 | 0=OTHER, 1=FIFO, 2=RR, 6=DEADLINE |

## 六、常见排查方法与分析

### 6.1 排查系统是否被实时任务锁定

**症状**：`top` 里 `%Cpu sy` 不高但登录响应极慢，或部分核 idle 一直是 0，SSH 操作明显卡顿。

```bash
# 找出所有非 SCHED_OTHER 的进程
ps -eo pid,tid,class,rtprio,ni,comm | grep -v "TS"
# class=FF(FIFO)/RR 且 rtprio 高的 → 嫌疑犯
# 逐个确认是不是故意的
chrt -p <PID>
# 若是不知来历的高优先级实时任务 → 降级
chrt -o -p 0 <PID>
# 或先降级再 kill
chrt -o -p 0 <PID> && kill -9 <PID>
```

### 6.2 延迟敏感应用测试实时调度效果

```bash
# 基线：SCHED_OTHER 运行，测延迟
./my_latency_app > baseline.log
# 测试：SCHED_FIFO 优先级 50
chrt -f 50 ./my_latency_app > rt.log
# 对比观测量：
pidstat -w -p <PID> 1    # cswch/nvcswch 变化
pidstat -u -p <PID> 1    # CPU 列是否稳定不迁移
# 注意：nvcswch 应该很低（不被抢占）；如有高优先级竞争会有抢占
```

### 6.3 检查调度策略是否配置正确

```bash
# 关键进程是否用了正确的调度策略
for pid in $(pgrep -f "my_rt_app"); do
    echo "PID=$pid $(chrt -p $pid)"
done
# 对 SCHED_FIFO 任务，检查优先级是否合理
# rtprio 50 → 可能被 51-99 的任务抢占
# rtprio 99 → 最高，可能饿死一切（慎用）
```

### 6.4 实时任务饿死普通任务的急救

```bash
# 症状：普通 SSH 都连不上
# 可通过 console 或 BMC/IPMI 紧急登录
# 或提前配置好 sched_rt_runtime_us 兜底
cat /proc/sys/kernel/sched_rt_runtime_us  # 默认 950000（95%）
# 即每 1 秒实时任务最多跑 0.95 秒，留 0.05 秒给普通任务
# 极端情况可调小：echo 500000 > /proc/sys/kernel/sched_rt_runtime_us
```

## 七、交叉引用

- **调度器原理**：[scheduling.md](/concepts/process/scheduling.md) §一（五层调度类的优先级排序）
- **上下文切换**：[context-switch.md](/concepts/process/context-switch.md)（FIFO 任务为何切换少）
- **调度观测**：[scheduling-observation.md](/tools/cpu/scheduling-observation.md)（从系统层面看调度是否健康）
- **绑核**：[thread-affinity.md](/concepts/process/thread-affinity.md)（taskset 绑核 + chrt 改优先级）
- **/proc 接口**：[procfs](/tools/proc/procfs.md) §二（`/proc/<pid>/sched` 中 policy/prio 字段）

## 八、一句话总结

- **调度类分层**：[scheduling.md](/concepts/process/scheduling.md) §一——为什么 FIFO>RR>CFS，优先级怎么排序
- **优先级与 nice**：nice 是 CFS 内的权重因子，rtprio 是实时类的硬优先级，二者是不同的体系
- **上下文切换与抢占**：[context-switch.md](/concepts/process/context-switch.md)——FIFO 任务为什么上下文切换少
- **观测调度情况**：[scheduling-observation.md](/tools/cpu/scheduling-observation.md)——从系统层面看调度是否健康
- **绑核**：[thread-affinity.md](/concepts/process/thread-affinity.md)——chrt 改优先级 + taskset 绑核，是实时任务的常用组合

## 七、一句话总结

> **`chrt` 是 Linux 调度策略的命令行接口——`chrt -p` 看进程用的哪种策略，`chrt -f/-r` 改成实时。`SCHED_FIFO` 不设时间片、高优先级能饿死所有低优先级任务，用错了系统会像挂了一样。除非明确需要实时调度（如 DPDK 轮询线程、JACK 音频线程），否则保持默认的 `SCHED_OTHER` 最安全。**

