> For the complete documentation index, see [llms.txt](https://vernon2gb.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://vernon2gb.gitbook.io/notes/process_thread_scheduler/scheduler03.md).

# scheduler03

## 吞吐率 vs. 响应

* 响应：最小化某个任务的响应时间，那怕牺牲其他的任务为代价
* 吞吐：全局视野，整个系统的workload被最大化处理

因为响应与吞吐率是一个完全相反的步骤，吞吐率好--响应差，吞吐率差--响应好，不存在完美的解决方法，所以存在server ubuntu、desktop ubuntu。其中主要通过linux kernel指定响应与吞吐策略，如下：

```bash
# linux-linux2.6.34
$ make x86_64_defconfig
$ make menuconfig
Processor type and features  --->
    Preemption Model ()  --->
        ( ) No Forced Preemption (Server)
        (X) Voluntary Kernel Preemption (Desktop)
        ( ) Preemptible Kernel (Low-Latency Desktop)
```

## CPU消耗型 vs IO消耗型

* IO bound : CPU利用率低，进程的运行效率主要受限于I/O速度
* CPU bound : 多数时间花在CPU上面（做运算）
* IO消耗型进程对CPU性能 不敏感，但是对及时抢到CPU 敏感。

比如：一个IO消耗型进程执行一次操作，用时100ms，其中CPU只占1ms，99ms用在IO读写操作。

当CPU性能降低一半，CPU执行时间从原来1ms变成2ms而已，全部时间是101ms。

当进程不能及时抢到CPU时，需要等待40ms，全部时间是140ms。

因为IO消耗型进程对及时抢到CPU 敏感，所以 IO消耗型优先级 > CPU消耗型优先级。这样做的好处，IO消耗型进程能够及时抢到CPU。

因为IO消耗型进程对CPU性能 不敏感， 所以目前 ARM 一般采用big.little core模式，让IO消耗型进程在little core工作，这样做的好处，可以降低CPU功耗。

## 优先级数组和bitmaps

* 优先级

总范围：\[0, 139]。值越小，优先级越高；值越大，优先级越小；

范围：\[0, 99]是实时进程的优先级

范围：\[100, 139]是（非实时）普通进程的优先级

* 如果某个优先级有TASK\_RUNNING进程，对应的bit设置为1
* 调度第一个bitmap设置为1的进程

## 实时进程调度

* SCHED\_FIFO ：不同优先级，高优先级先跑到睡眠，低优先级再跑；同等优先级 先进先出
* SCHED\_RR ：不同优先级，高优先级先跑到睡眠，低优先级再跑；同等优先级 轮转
* RT门限

  在`sched_rt_period_us`时间内，实时进程最多只能跑`sched_rt_runtime_us`时间，剩下`sched_rt_period_us-sched_rt_runtime_us`时间给（非实时）普通进程运行。

  ```bash
  $ cd /proc/sys/kernel/
  $ cat sched_rt_period_us 
  1000000
  $ cat sched_rt_runtime_us 
  950000
  ```

## （非实时）普通进程的调度和动态优先级

* SCHED\_NORMAL：不同优先级 轮转
* nice范围：\[-20, +19]，对应优先级\[100, 139]。nice值越小，优先级越高；nice值越大，优先级越小；
* 2.6早期调度算法（**已废弃**）：根据睡眠情况，动态奖励和惩罚

  如果分配100ms时间片给进程A，进程A立刻将时间片用光，说明进程A是CPU消耗型进程，即 将进程A nice值升高，同时再分配新100ms时间片给进程A。

  如果分配100ms时间片给进程B，进程B一直没有用，说明进程B是IO消耗型进程，即 将进程B nice值降低。

  这样动态调节nice值，让IO消耗型进程优先级高， 一旦被唤醒后，可以立刻抢占CPU。
* 现在的调度算法：CFS（完全公平调度）

  采用红黑树把所有可执行进程组织而成，红黑树的左边节点`vruntime`小于右边节点的`vruntime`，CFS会运行最小`vruntime`对应的进程

  ```c
  # dela        : 进程实际运行时间
  # NICE_0_LOAD : 1024
  # se.weight   : 不同nice值，对应的weight，在sched_prio_to_weight[40]中查找
  vruntime += dela * NICE_0_LOAD / se.weight;

  const int sched_prio_to_weight[40] = {
   /* -20 */     88761,     71755,     56483,     46273,     36291,
   /* -15 */     29154,     23254,     18705,     14949,     11916,
   /* -10 */      9548,      7620,      6100,      4904,      3906,
   /*  -5 */      3121,      2501,      1991,      1586,      1277,
   /*   0 */      1024,       820,       655,       526,       423,
   /*   5 */       335,       272,       215,       172,       137,
   /*  10 */       110,        87,        70,        56,        45,
   /*  15 */        36,        29,        23,        18,        15,
  };
  ```

## 调度相关的系统调用

| System Call                 | Description                         |
| --------------------------- | ----------------------------------- |
| nice()                      | Sets a process's nice value         |
| sched\_setscheduler()       | Sets a process's scheduling policy  |
| sched\_getscheduler()       | Gets a process's scheduling policy  |
| sched\_setparam()           | Sets a process's real-time priority |
| sched\_getparam()           | Gets a process's real-time priority |
| sched\_get\_priority\_max() | Gets the maximum real-time priority |
| sched\_get\_priority\_min() | Gets the minimum real-time priority |
| sched\_rr\_get\_interval()  | Gets a process's timeslice value    |
| sched\_setaffinity()        | Sets a process's processor affinity |
| sched\_getaffinity()        | Gets a process's processor affinity |
| sched\_yield()              | Temporarily yields the processor    |

1. 代码例子：

```c
# 将进程设置为SCHED_FIFO，RT优先级为50
struct sched_param the_priority;

the_priority.sched_priority = 50;
pthread_setschedparam(pthread_self(), SCHED_FIFO, &the_priority);
```

1. 命令行工具：

```bash
$ chrt -f -a -p 50 10576 # 将pid＝10576进程中所有线程，设置为SCHED_FIFO，RT优先级为50

$ renice -n -5 -g 9394   # 将pid＝9394进程中所有线程，对应nice值设置为 -5
or
$ nice -n -5 ./a.out     # 运行./a.out时，同时将对应nice值设置为 -5
```

## 实例

1. 源码

```c
$ cat two-loops.c 
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>

void *thread_fun(void *param)
{
    printf("thread pid:%d, tid:%lu\n", getpid(), pthread_self());
    while (1) ;
    return NULL;
}

int main(void)
{
    pthread_t tid1, tid2;
    int ret;

    printf("main pid:%d, tid:%lu\n", getpid(), pthread_self());

    ret = pthread_create(&tid1, NULL, thread_fun, NULL);
    if (ret == -1) {
        perror("cannot create new thread");
        return 1;
    }

    ret = pthread_create(&tid2, NULL, thread_fun, NULL);
    if (ret == -1) {
        perror("cannot create new thread");
        return 1;
    }

    if (pthread_join(tid1, NULL) != 0) {
        perror("call pthread_join function fail");
        return 1;
    }

    if (pthread_join(tid2, NULL) != 0) {
        perror("call pthread_join function fail");
        return 1;
    }

    return 0;
}
$ gcc two-loops.c -pthread
```

1. 运行2个高CPU利用率程序，调整他们的nice

前提：测试电脑是2个CPU核，所以最大％CPU是200％

```bash
$ ./a.out &
main pid:2856, tid:139933770024704
thread pid:2856, tid:139933753579280
thread pid:2856, tid:139933761971984
$ ./a.out &
main pid:2859, tid:140378753423104
thread pid:2859, tid:140378745370384
thread pid:2859, tid:140378736977680
$ top
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 2856 vernon    20   0 22544  580  460 S  102  0.0   0:56.17 a.out
 2859 vernon    20   0 22544  584  460 S   98  0.0   0:52.89 a.out
$ sudo renice -n -5 -g 2856 # 将pid＝2856进程中所有线程，对应nice值设置为 -5
$ top
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 2856 vernon    15  -5 22544  580  460 S  151  0.0   3:20.69 a.out
 2859 vernon    20   0 22544  584  460 S   49  0.0   2:41.34 a.out

$ killall a.out
```

1. 用chrt把一个死循环程序调整为SCHED\_FIFO

```bash
$ ./a.out &
$ top
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
3283 vernon    20   0 22544  584  460 S  200  0.0   0:10.28 a.out
$ sudo su - root
$ chrt -f -p 50 3283 # 将pid＝3283进程，设置为SCHED_FIFO，RT优先级为50
$ top
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 3283 vernon   -51   0 22544  584  460 S  197  0.0  19:15.88 a.out
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://vernon2gb.gitbook.io/notes/process_thread_scheduler/scheduler03.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
