关于Linux中线程优先级的问题探讨

参考:

linux 的调度策略与优先级_linux policy 1-CSDN博客

Linux调度策略和优先级机制

首先需要说明,我们常说的进程调度器是传统的说法,但是实际上进程是资源管理的单位,线程才是调度的单位.有时也会说任务调度等。

linux的调度机制由调度策略(policies)和优先级(priority)两个属性共同决定,其中调度策略又可以分为实时调度策略和通用调度策略(非实时)。其中通用调度策略的默认优先级值为0,而实时调度策略的优先级取值范围为1~99。实时调度策略的优先级总是大于通用调度策略。而优先级高线程的总是会被调度器优先调用。其中实时调度策略包含SCHED_RR,SCHED_FIFO调度策略。通用调度策略包含SCHED_OTHER、SCHED_IDLE、SCHED_BATCH调度策略。

即如下关系:

linux的调度机制由调度策略(policies)和优先级(priority)共同决定:

调度策略(policies)分如下几种:

**实时调度策略:**spriority值的取值范围为1~99.当线程间的优先级不同时,按照优先级高的先调度原则。当优先级相同时,分为如下两种方式:

SCHED_RR:优先级相同的线程按固定的时间片循环调度。下面会详细讲解。

SCHED_FIFO:与SCHED_RR相似,优先级相同的线程也是循环调度。不过没有时间片的概念。下面会详细讲解。

**通用调度策略(非实时):**spriority值为0

SCHED_OTHER:

SCHED_IDLE:

SCHED_BATCH:

所有的调度策略最后通过链表的方式组合成一个调度类。由linux内核调度。

**优先级:**确定哪个线程被优先调度。实时调度策略的优先级总是大于通用调度策略。
注意,调度策略和优先级是针对进程或者线程的,而不是说Linux只能使用某一种调度策略,Linux本身的调度算法使用的是混合调度。既然每个线程都可以指定所使用的调度策略,那么说明,Linux内核本身是实时调度策略和非实时调度策略同时存在的,从而为不同的线程提供不同的服务。例如,系统可以同时运行非实时调度策略来管理普通用户进程,并使用实时调度策略来管理实时任务。通过这种方式,Linux 能够灵活地满足各种应用场景的需求。

各种调度策略介绍

实时调度策略之SCHED_RR:Round-robin scheduling

当线程间的优先级不同时,优先级高的先调度。当优先级相同时,固定的时间片循环调度。被调用的线程满足如下条件时会让出CPU:

  • 调度期间的时间片使用完了。
  • 自动放弃CPU。如调用了阻塞相关的接口函数或调用了sched_yield()。
  • 被一个优先级更高的线程抢占了
  • 线程终止了。

如果因为时间片使用完了或自愿放弃CPU而导致线程让出CPU,此时此线程将会被放置在与其优 先级级别对应的队列的队尾。如果因为被抢占而让出CPU,则会放置到队头,等更高优先级让出cpu时,继续执行此线程。

实时调度策略之SCHED_FIFO:First in-first out scheduling

与SCHED_RR实时调度策略相似,不过它没有时间片的概念。被调用的线程让出CPU条件与SCHED_RR类似,只是没有时间片使用完的情况.

通用调度策略之SCHED_OTHER:time-sharing scheduling

分时循环调度策略。也就是我们常说的CFS(Completely Fair Scheduler)完全公平调度器。是系统的默认调度策略。按动态时间片循环调度。动态时间片由nice属性值决定。每个SCHED_OTHER策略的线程都拥有一个nice值,其取值范围为−20~19,默认值为0.nice值是一个权重因子,值越小,权重越大。CPU为其分配的动态时间片会越多。

通用调度策略之SCHED_BATCH

此策略会让频繁被唤醒的线程在调度时次数会变少。其他与SCHED_OTHER策略类似。

通用调度策略之SCHED_IDLE

可以理解为nice=19的SCHED_OTHER策略。当系统中没有其他线程需要使用CPU时才会大量使用CPU。

通用调度策略之SCHED_DEADLINE

自从3.14版本以来,Linux提供了一个截止日期调度策略(SCHED_DEADLINE)。

调度相关的用户空间接口

修改nice值相关的函数接口:

int nice(int inc)

所属头文件:<unistd.h>

描述:修改当前进程的nice值。此函数已经被更通用的setpriority()取代。

inc:在当前nice的基础上加inc.

return: 如果成功,返回新设置的nice值。

eg:nice(3)、nice(-5)

int setpriority(int which, int who, int prio)

描述: 设置通用进程的nice值。这里的priority不要与上边讲到的优先级混淆。

which:决定who传入的参数含义,which可选值:

PRIO_PROCESS:表明who为进程id.如果等于零,则为当前进程

PRIO_PGRP:表明who为进程组id.如果等于零,则为当前进程组

PRIO_USER:表明who为用户(real user)id.如果等于零,则为当前用户

prio:-20~19之间的nice值。

return: 成功返回0。失败返回-1.。

int getpriority(int which, int who)

所属头文件:<sys/time.h>,<sys/resource.h>

描述:获取进程的nice值。

return: 返回进程调度优先级(-20~19)。

eg: getpriority(PRIO_PROCESS, getpid());

修改优先级与调度策略相关的函数接口:

int sched_get_priority_max(int policy)

描述: 查看调度策略最大优先级。

policy 可指定的值如下:

SCHED_OTHER

SCHED_BATCH

SCHED_IDLE

SCHED_FIFO

SCHED_RR

return: 成功返回最大优先级值。失败返回-1.

int sched_get_priority_min(int policy)

查看调度策略最小优先级。其他同sched_get_priority_max().

int sched_setscheduler(pid_t pid, int policy,const struct sched_param *param)

描述: 设置进程的调度策略和优先级。

policy 可指定的值如下:

SCHED_OTHER

SCHED_BATCH

SCHED_IDLE 对于这三种策略,param->sched_priority的值必须为0.

SCHED_FIFO

SCHED_RR 对于这两种策略,需要指定param->sched_priority的值。

param:指定调度策略的优先级。struct sched_param的结构体如下:

struct sched_param {
    int sched_priority; //优先级1~99
};

return: 成功返回0。失败返回-1.

int sched_getscheduler(pid_t pid)

所属头文件:<sched.h>

描述: 获取进程的调度策略。

pid 进程id.如果为0,表示为本线程。

return: 成功返回调度策略值(非负整数)。失败返回-1.

SCHED_OTHER 0

SCHED_FIFO 1

SCHED_RR 2

SCHED_BATCH 3

SCHED_IDLE 5

int sched_setparam(pid_t pid, const struct sched_param *param)

描述: 设置进程的优先级。sched_setscheduler()函数的子集。

param:见sched_setscheduler()函数中的描述。

return: 成功返回0。失败返回-1.

int sched_getparam(pid_t pid, struct sched_param *param)

描述: 获取进程的优先级。

param:见sched_setscheduler()函数中的描述。用于接收要获取的优先级值。

return: 成功返回0。失败返回-1.

linux特有的函数接口,可用于修改上边提到的所有属性:

int sched_setattr(pid_t pid, struct sched_attr *attr,unsigned int flags)

描述: 设置进程的调度策略。

struct sched_attr结构体如下:

struct sched_attr {
       u32 size;              //结构体大小
       u32 sched_policy;      //调度策略
       u64 sched_flags;       //可选属性:SCHED_FLAG_RESET_ON_FORK 表示子进程创建时不继承此策略。
       s32 sched_nice;        //通用调度策略的nice值
       u32 sched_priority;    //优先级
       /* Remaining fields are for SCHED_DEADLINE */
       u64 sched_runtime;
       u64 sched_deadline;
       u64 sched_period;
   };

flags 目前为0.

return: 成功返回0。

int sched_getattr(pid_t pid, struct sched_attr *attr,unsigned int size, unsigned int flags)

描述: 获取进程的调度策略。

size: attr结构体的大小。

其他参数同sched_setattr().

其他接口:

int sched_yield(void)

描述:进程主动放弃cpu.

int sched_rr_get_interval(pid_t pid, struct timespec *tp)

获取SCHED_RR调度策略的进程在每次使用CPU时分配到时间片的长度。

tp:获取到的时间长度。

成功返回0,失败返回-1.

pthread_create的线程优先级设置

我们可以通过**POSIX Threads (pthread)**提供的一套线程管理的 API,来设置线程调度策略和优先级等参数。

比如,我们可以用pthread_create来创建一个线程,pthread_create创建线程时的优先级,可以取什么范围的值?

在Linux系统中,使用pthread_create创建线程时,线程的优先级可以通过设置线程属性来控制。具体来说,线程的优先级范围取决于所选择的调度策略。

实时调度策略(SCHED_FIFO和SCHED_RR):优先级范围:1到99。值越大表示优先级越高。

非实时调度策略(SCHED_OTHER):优先级范围:-20到19。值越低表示优先级越高。这种调度策略通常用于普通应用程序,其中所有线程的默认优先级都是0。

请注意,要设置线程的优先级,需要在创建线程之前通过pthread_attr_t结构体设置相应的调度策略和优先级参数,pthread_attr_t结构体如下所示:

typedef struct
{
       int                       detachstate;     线程的分离状态
       int                       schedpolicy;     线程调度策略
       struct sched_param        schedparam;      线程的调度参数
       int                       inheritsched;    线程的继承性
       int                       scope;           线程的作用域
       size_t                    guardsize;       线程栈末尾的警戒缓冲区大小
       int                       stackaddr_set;
       void *                    stackaddr;       线程栈的位置
       size_t                    stacksize;       线程栈的大小
}pthread_attr_t;

更具体的可参考:

多线程属性pthread_attr详解_attr线程-CSDN博客

pthread_attr_t 结构体的默认值:

在Linux系统中,pthread_attr_t结构体用于定义线程的属性。当创建一个新的线程时,可以通过设置这个结构体来指定线程的各种属性,如堆栈大小、调度策略和优先级等。如果用户没有显式地设置这些属性,系统会为它们提供默认值。以下是pthread_attr_t结构体中一些常见属性的默认值:

  1. detachstate :默认值为PTHREAD_CREATE_JOINABLE,表示线程是可连接的,即其他线程可以等待它的终止并获取其退出状态。

  2. schedpolicy :默认值为SCHED_OTHER,表示使用非实时的轮转时间片调度策略。

  3. schedparam :这是一个结构体,包含一个名为sched_priority的成员,对于SCHED_OTHER策略,默认优先级为0。对于实时策略(如SCHED_FIFOSCHED_RR),默认优先级通常为1。

  4. inheritsched :默认值为PTHREAD_INHERIT_SCHED,表示线程将继承创建它的线程的调度策略和优先级。

  5. scope :默认值为PTHREAD_SCOPE_SYSTEM,表示线程可以在系统中的所有处理器上运行。

  6. stackaddr_set:默认值为0,表示没有为线程显式设置堆栈地址。

  7. stacksize:默认值依赖于系统,但通常是足够大以容纳线程所需的堆栈空间。

  8. guardsize:默认值为系统页面大小的两倍,用于保护堆栈免受溢出。

在使用pthread_create函数创建新线程之前,通常会先调用pthread_attr_init函数来初始化pthread_attr_t结构体,然后根据需要修改其中的一些属性。如果不进行任何修改,那么上述默认值将被使用。

关于其中的优先级设置参数struct sched_param schedparam,当您获得或设置线程/进程的调度参数时,会使用sched_param结构,其内容如下:

#include <sched.h>

struct sched_param 
{ 
    int32_t  sched_priority; 
    int32_t  sched_curpriority; 
    union 
    { 
        int32_t  reserved[8]; 
        struct 
        {    
            int32_t  __ss_low_priority;  
            int32_t  __ss_max_repl;  
            struct timespec     __ss_repl_period;   
            struct timespec     __ss_init_budget;   
        }           __ss;   
    }           __ss_un;    
}

#define sched_ss_low_priority   __ss_un.__ss.__ss_low_priority
#define sched_ss_max_repl       __ss_un.__ss.__ss_max_repl
#define sched_ss_repl_period    __ss_un.__ss.__ss_repl_period
#define sched_ss_init_budget    __ss_un.__ss.__ss_init_budget

具体可参考:

struct sched_param 结构体_struct sched param-CSDN博客

我们重点关注里面的优先级变量sched_priority

  • 当获得调度参数时,该成员反映出分配给线程或进程的优先级。它不反映任何由于优先级继承而造成的临时调整。

  • 当您设置调度参数时,请将此成员设置为您想要使用的优先级。优先级必须介于sched_get_priority_min()和sched_get_priority_max()为调度策略返回的最小值和最大值之间。

更多待补充。

相关推荐
快乐飒男1 分钟前
Linux基础05
linux·笔记·学习
Who_Mr.Lin1 小时前
【虚拟机】VMWare的CentOS虚拟机断电或强制关机出现问题
linux·运维·centos
我是唐青枫1 小时前
Linux nc 命令详解
linux·运维·服务器
南东山人1 小时前
关于内核编程的一些笔记
linux·笔记
ejinxian1 小时前
Windows 系统上构建 Linux 应用
linux·运维·服务器·red hat
Java 第一深情1 小时前
详细教程-Linux上安装单机版的Hadoop
linux·运维·hadoop
MC何失眠2 小时前
泷羽sec-----shell编程(完结)
linux·学习·网络安全
Linux运维技术栈2 小时前
ELK配置索引清理策略
linux·运维·elk
44漏洞观察员2 小时前
linux实战-黑链——玄机靶场
linux·运维·服务器·web安全·网络安全
爱学嵌入式的菜鸟2 小时前
Linux应用编程(C语言编译过程)
linux·c语言·ubuntu