关于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()为调度策略返回的最小值和最大值之间。

更多待补充。

相关推荐
xuanzdhc28 分钟前
Linux 基础IO
linux·运维·服务器
愚润求学33 分钟前
【Linux】网络基础
linux·运维·网络
bantinghy1 小时前
Linux进程单例模式运行
linux·服务器·单例模式
小和尚同志2 小时前
29.4k!使用 1Panel 来管理你的服务器吧
linux·运维
帽儿山的枪手2 小时前
为什么Linux需要3种NAT地址转换?一探究竟
linux·网络协议·安全
shadon1789 天前
回答 如何通过inode client的SSLVPN登录之后,访问需要通过域名才能打开的服务
linux
小米里的大麦9 天前
014 Linux 2.6内核进程调度队列(了解)
linux·运维·驱动开发
算法练习生9 天前
Linux文件元信息完全指南:权限、链接与时间属性
linux·运维·服务器
忘了ʷºᵇₐ9 天前
Linux系统能ping通ip但无法ping通域名的解决方法
linux·服务器·tcp/ip
浩浩测试一下9 天前
渗透测试指南(CS&&MSF):Windows 与 Linux 系统中的日志与文件痕迹清理
linux·运维·windows·安全·web安全·网络安全·系统安全