进程调度策略和进程优先级

Linux 的进程调度策略进程优先级是操作系统为保证系统响应性、公平性和高性能所设计的关键机制。

进程调度策略

Linux 支持 两大类调度策略

  • 普通调度策略(CFS: Completely Fair Scheduler), 适用于大部分用户态进程。
  • 实时调度策略(Real-Time Scheduling Policies),** **使用在时间要求严格的任务(如工业控制、音频处理)中。

普通调度策略(非实时)

  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_NORMAL</font>**(即 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_OTHER</font>**
    • 默认策略,用于普通进程(非实时进程)。
    • 使用 完全公平调度器(CFS, Completely Fair Schedule) 动态分配 CPU 时间,基于进程的 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">nice</font>** 值调整权重,把处理器时间公平地分给每个进程。
    • 示例:大多数用户进程(如浏览器、文本编辑器)。
  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_IDLE</font>**
    • 优先级最低,仅在系统空闲时运行(如后台维护任务)。

实时调度策略

  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_FIFO</font>**(First In Fisrst Out)
    • 先进先出,先调度先运行。
    • 无时间片限制,进程会一直运行直到主动让出 CPU 或更高优先级进程就绪。
    • 优先级范围:**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">1</font>**(最低)到 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">99</font>**(最高)。
  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_RR</font>**(Round Robin)
    • 类似 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_FIFO</font>**,但每个进程分配固定时间片,进程用完时间片以后加入优先级对应运行队列的尾部,把处理器让给优先级相同的其它实时进程。
    • 优先级范围同 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_FIFO</font>**(1-99)。
  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_DEADLINE</font>**
    • 基于任务的截止时间(Deadline)调度,适合实时性要求严格的任务。
    • 使用三个参数:运行时间**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">runtime</font>**、截至期限**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">deadline</font>**、周期**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">period</font>**。每个周期运行一次,在截至期限之前执行完,一次运行的时间长度是<font style="color:rgb(64, 64, 64);">runtime</font>
    • 内核需要开启 CONFIG_SCHED_DEADLINE 。

调度策略定义如下:

/*
 * Scheduling policies
 */
#define SCHED_NORMAL		0
#define SCHED_FIFO		1
#define SCHED_RR		2
#define SCHED_BATCH		3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE		5
#define SCHED_DEADLINE		6

进程优先级

  • 限期进程的优先级比实时进程要高,实时进程的优先级比普通通进程要高。
  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_DEADLINE</font>** > **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_FIFO</font>**/**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_RR</font>** > **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">SCHED_NORMAL</font>**
  • 限期进程的优先级是-1。
  • 实时进程的实时优先级是<font style="color:rgb(64, 64, 64);">1~99</font>优先级数值越大,表示优先级越高
  • 普通进程的静态优先级是<font style="color:rgb(64, 64, 64);">100~139</font>优先级数值越小,表示优先级越高。可通过修改nice值(即相对优先级,取值范围是 <font style="color:rgb(64, 64, 64);">-20~19</font>)改变普通进程的优先级,优先级等于<font style="color:rgb(64, 64, 64);">120</font>加上nice值。

进程描述符<font style="color:rgb(64, 64, 64);">task_struct</font>中,有4个成员和优先级相关:

struct task_struct {
    ...
	int				prio;
	int				static_prio;
	int				normal_prio;
	unsigned int			rt_priority;
    ...
};

相关解释如下表:

优先级限期进程实时进程普通进程
prio
动态优先级(数值越小,表示优先级越高)
大多数情况下prio等于normal_prio
特殊情况:优先级继承。如果进程a占有实时互斥锁,进程b正在等待锁,进程b的优先级比进程a的优先级高,那么把进程a的优先级临时提高到进程b的优先级,即进程a的prio值等于进程b的prio值。
static_prio
静态优先级
不适用,总是0不适用,总是0120+nice
数值越小,表示优先级越高。
初始化后通常不变。
normal_prio
进程的“标准化”优先级(数值越小,表示优先级越高),由调度类根据 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">static_prio</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">rt_priority</font>** 计算。
-1(MAX_DL_PRIO-199(MAX_RT_PRIO - 1)- rt_prioritystatic_prio
rt_priority
实时优先级
不适用,总是0实时进程的优先级,范围是1~99,数值越大,表示优先级越高。
仅对实时进程有效。
不适用,总是0
调度策略**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">static_prio</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">rt_priority</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">normal_prio</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">prio</font>**
SCHED_DEADLINE**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">-1</font>**(特殊值)优先级继承场景外,通常等于normal_prio
SCHED_FIFO/RR**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">1-99</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">100 - 1 - rt_priority</font>**
SCHED_NORMAL**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">100 + nice + 20</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">0</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">= static_prio</font>**

调度器如何使用优先级字段

  • prio:调度器根据该字段在红黑树中排序(普通任务)或链表(实时任务)中比较。
  • static_prio :用于计算普通进程的normal_prio
  • normal_prio
    • 在新建进程或任务状态切换时用于初始化 prio
    • 在进程唤醒后 prio 会从 normal_prio 恢复。
  • rt_priority:用于计算实时进程的normal_prio

内核normal_prio计算

normal_prio计算的核心代码如下:

/*
 * __normal_prio - return the priority that is based on the static prio
 */
static inline int __normal_prio(struct task_struct *p)
{
	return p->static_prio;
}

/*
 * Calculate the expected normal priority: i.e. priority
 * without taking RT-inheritance into account. Might be
 * boosted by interactivity modifiers. Changes upon fork,
 * setprio syscalls, and whenever the interactivity
 * estimator recalculates.
 */
static inline int normal_prio(struct task_struct *p)
{
	int prio;

	if (task_has_dl_policy(p)) // SCHED_DEADLINE 任务
		prio = MAX_DL_PRIO-1;
	else if (task_has_rt_policy(p)) // 实时任务
		prio = MAX_RT_PRIO-1 - p->rt_priority;
	else                            // 普通任务
		prio = __normal_prio(p);
	return prio;
}

调度策略及优先级查询方法

测试代码如下

// gcc -o policy_prio_test policy_prio_test.c -pthread
// ※一定要在root下运行 ./policy_prio_test
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/resource.h>  // for setpriority()

volatile sig_atomic_t keep_running = 1;

void signal_handler(int sig) {
    if (sig == SIGINT) {
        keep_running = 0;
    }
}

// 设置线程属性(调度策略和优先级)
void set_thread_attr(pthread_attr_t *attr, int policy, int priority) {
    // 初始化线程属性
    pthread_attr_init(attr);

    // 设置调度策略(SCHED_FIFO/SCHED_RR/SCHED_OTHER)
    pthread_attr_setschedpolicy(attr, policy);

    // 设置优先级(仅对实时策略有效)
    if (policy == SCHED_FIFO || policy == SCHED_RR) {
        struct sched_param param;
        param.sched_priority = priority;
        pthread_attr_setschedparam(attr, &param);
    }

    // 明确指定使用自定义调度参数(不继承父线程)
    pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
}

// 普通线程函数(通过nice值调整优先级)
void *normal_thread_func(void *arg) {
    int nice_val = *(int *)arg;
    printf("Normal Thread (nice=%d) started. PID=%d, TID=%ld\n",
           nice_val, getpid(), (long)pthread_self());

    // 设置当前线程的nice值
    if (setpriority(PRIO_PROCESS, 0, nice_val) == -1) {
        perror("setpriority failed");
    }

    while (keep_running) {
        sleep(1);  // 模拟工作
    }

    printf("Normal Thread (nice=%d) exiting.\n", nice_val);
    return NULL;
}

// 实时线程函数(优先级由属性设置)
void *rt_thread_func(void *arg) {
    printf("RT Thread started. PID=%d, TID=%ld\n",
           getpid(), (long)pthread_self());

    while (keep_running) {
        sleep(1);  // 模拟工作
    }

    printf("RT Thread exiting.\n");
    return NULL;
}

int main() {
    signal(SIGINT, signal_handler);
    printf("Main process PID: %d\n", getpid());

    pthread_t threads[7];
    pthread_attr_t attrs[7];

    // 线程配置:类型 + 优先级/nice值
    struct {
        int policy;
        int priority;  // 对实时线程有效
        int nice;      // 对普通线程有效
    } thread_configs[7] = {
        {SCHED_OTHER, 0, 10},   // 普通线程1 (nice=10)
        {SCHED_OTHER, 0, 0},    // 普通线程2 (nice=0)
        {SCHED_OTHER, 0, -5},   // 普通线程3 (nice=-5)
        {SCHED_FIFO,  80, 0},   // FIFO线程1 (优先级80)
        {SCHED_FIFO,  90, 0},   // FIFO线程2 (优先级90)
        {SCHED_RR,    70, 0},   // RR线程1 (优先级70)
        {SCHED_RR,    60, 0}    // RR线程2 (优先级60)
    };

    // 创建所有线程
    for (int i = 0; i < 7; i++) {
        // 设置线程属性
        set_thread_attr(&attrs[i], thread_configs[i].policy, thread_configs[i].priority);

        // 创建线程
        int ret;
        if (thread_configs[i].policy == SCHED_OTHER) {
            ret = pthread_create(&threads[i], &attrs[i], normal_thread_func, &thread_configs[i].nice);
        } else {
            ret = pthread_create(&threads[i], &attrs[i], rt_thread_func, NULL);
        }

        if (ret != 0) {
            fprintf(stderr, "Failed to create thread %d: %s\n", i, strerror(ret));
            exit(EXIT_FAILURE);
        }
    }

    // 等待Ctrl+C信号
    while (keep_running) {
        pause();
    }

    // 清理线程
    printf("Shutting down...\n");
    for (int i = 0; i < 7; i++) {
        pthread_cancel(threads[i]);
        pthread_join(threads[i], NULL);
        pthread_attr_destroy(&attrs[i]);
    }

    return 0;
}

ps命令查询

# ps  -p 44353 -Lo tid,policy,pri,nice,rtprio,cmd
    TID POL PRI  NI RTPRIO CMD
  44353 TS   19   0      - ./policy_prio_test
  44354 TS    9  10      - ./policy_prio_test
  44355 TS   19   0      - ./policy_prio_test
  44356 TS   24  -5      - ./policy_prio_test
  44357 FF  120   -     80 ./policy_prio_test
  44358 FF  130   -     90 ./policy_prio_test
  44359 RR  110   -     70 ./policy_prio_test
  44360 RR  100   -     60 ./policy_prio_test

  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pri</font>** 列的值是经过转换的(<font style="color:rgb(64, 64, 64);">139 - prio</font>),非内核的 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">prio</font>** 原始值
  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pri</font>** 值越大优先级越高

top命令查询

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  44353 root      20   0   60020    620    536 S   0.0   0.0   0:00.00 policy_prio_tes
  44354 root      30  10   60020    620    536 S   0.0   0.0   0:00.02 policy_prio_tes
  44355 root      20   0   60020    620    536 S   0.0   0.0   0:00.02 policy_prio_tes
  44356 root      15  -5   60020    620    536 S   0.0   0.0   0:00.01 policy_prio_tes
  44357 root     -81   0   60020    620    536 S   0.0   0.0   0:00.02 policy_prio_tes
  44358 root     -91   0   60020    620    536 S   0.0   0.0   0:00.02 policy_prio_tes
  44359 root     -71   0   60020    620    536 S   0.0   0.0   0:00.02 policy_prio_tes
  44360 root     -61   0   60020    620    536 S   0.0   0.0   0:00.02 policy_prio_tes

  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">PR</font>** 列(优先级)是 **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">ps</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pri</font>** 的另一种表示方式,非内核原始值
  • **<font style="color:rgb(64, 64, 64);">PR</font>** 值越小优先级越高。

<font style="color:rgb(64, 64, 64);">chrt</font>命令查询

chrt主要用来查询实时进程的调度策略和优先级。查询到的优先级是task_truct中的rt_priority,对于普通进程,该值为0。

# chrt -p 44353
pid 44353's current scheduling policy: SCHED_OTHER
pid 44353's current scheduling priority: 0 // 普通进程,rt_priority值为0

# chrt -p 44354
pid 44354's current scheduling policy: SCHED_OTHER
pid 44354's current scheduling priority: 0

# chrt -p 44355
pid 44355's current scheduling policy: SCHED_OTHER
pid 44355's current scheduling priority: 0

# chrt -p 44356
pid 44356's current scheduling policy: SCHED_OTHER
pid 44356's current scheduling priority: 0

# chrt -p 44357
pid 44357's current scheduling policy: SCHED_FIFO
pid 44357's current scheduling priority: 80

#  chrt -p 44358
pid 44358's current scheduling policy: SCHED_FIFO
pid 44358's current scheduling priority: 90

#  chrt -p 44359
pid 44359's current scheduling policy: SCHED_RR
pid 44359's current scheduling priority: 70

# chrt -p 44360
pid 44360's current scheduling policy: SCHED_RR
pid 44360's current scheduling priority: 60

/proc/<pid>/sched查询

/proc/<pid>/sched查询,查询到的值是task_struct中的prio

# cat /proc/44353/sched
policy_prio_tes (44353, #threads: 8)
-------------------------------------------------------------------
se.exec_start                                :      19758958.662391
se.vruntime                                  :        159031.419195
se.sum_exec_runtime                          :             0.509459
se.nr_migrations                             :                    0
nr_switches                                  :                    8
nr_voluntary_switches                        :                    3
nr_involuntary_switches                      :                    5
se.load.weight                               :              1048576
se.avg.load_sum                              :                46917
se.avg.runnable_sum                          :             24281477
se.avg.util_sum                              :             24076911
se.avg.load_avg                              :                 1023
se.avg.runnable_avg                          :                  515
se.avg.util_avg                              :                  513
se.avg.last_update_time                      :       19758958661632
se.avg.util_est.ewma                         :                  513
se.avg.util_est.enqueued                     :                  513
uclamp.min                                   :                    0
uclamp.max                                   :                 1024
effective uclamp.min                         :                    0
effective uclamp.max                         :                 1024
policy                                       :                    0 // SCHED_NORMAL
prio                                         :                  120
clock-delta                                  :                   14
mm->numa_scan_seq                            :                    0
numa_pages_migrated                          :                    0
numa_preferred_nid                           :                   -1
total_numa_faults                            :                    0
current_node=0, numa_group_id=0
numa_faults node=0 task_private=0 task_shared=0 group_private=0 group_shared=0

# cat /proc/44358/sched
policy_prio_tes (44358, #threads: 8)
-------------------------------------------------------------------
se.exec_start                                :      27409364.575804
se.vruntime                                  :             8.999994
se.sum_exec_runtime                          :           107.209942
se.nr_migrations                             :                    0
nr_switches                                  :                 7652
nr_voluntary_switches                        :                 7652
nr_involuntary_switches                      :                    0
se.load.weight                               :              1048576
se.avg.load_sum                              :                47055
se.avg.runnable_sum                          :             11860176
se.avg.util_sum                              :             11857104
se.avg.load_avg                              :                 1024
se.avg.runnable_avg                          :                  252
se.avg.util_avg                              :                  252
se.avg.last_update_time                      :       19758958593024
se.avg.util_est.ewma                         :                    0
se.avg.util_est.enqueued                     :                    0
uclamp.min                                   :                 1024
uclamp.max                                   :                 1024
effective uclamp.min                         :                 1024
effective uclamp.max                         :                 1024
policy                                       :                    1 // SCHED_FIFO
prio                                         :                    9
clock-delta                                  :                   21
mm->numa_scan_seq                            :                    0
numa_pages_migrated                          :                    0
numa_preferred_nid                           :                   -1
total_numa_faults                            :                    0
current_node=0, numa_group_id=0
numa_faults node=0 task_private=0 task_shared=0 group_private=0 group_shared=0


参考资料

  1. Professional Linux Kernel Architecture,Wolfgang Mauerer
  2. Linux内核深度解析,余华兵
  3. Linux设备驱动开发详解,宋宝华
  4. linux kernel 4.12


※※※下面内容暂时先保留,后续删除



系统调用

每个进程都有一个内核栈

当运行应用程序的时候,调用fork()/vfork()/clone()函数就是系统调用;系统调用就是应用程序如何进入内核空间执行任务,程序使用系统调用执行一系列的操作:如何创建进程、文件IO等;

内核线程

内核线程是直接由内核本身启动的进程;它是独立运行在内核空间的进程,与普通用户进程区别在于内核线程没有独立的进程地址空间;task_struct结构里面mm指针设置为NULL,它只能运行在内核空间。

退出进程

进程主动终止:从main()函数返回,链接程序会自动添加到exit()系统调用;主动调用exit()系统函数;

进程被动终止:进程收到自己不能处理的信号,如SIGKILL等;

  1. 什么是限期进程,实时进程,普通进程?
  2. 内核调度策略,SCHED_BATCH,怎么个批量处理;SCHED_IDLE,使task以最低优先级选择CFS调度器来调度运行,这个又是什么意思?
  3. 调度器类、调度策略,是怎么关联起来的?
  4. SCHED_OTHER, 与SCHED_NORMAL、SCHED_BATCH、SCHED_IDLE是什么关系?

不应该是数值越小,优先级越高吗?

就这么个意思

是什么呢,从什么呢,然后什么呢,对不对

SMT,超线程

查看进程状态,包括绑核信息

cat /proc/pid/status

查看进程sleep时,kernel当前运行的函数

cat /proc/pid/wchan

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值