linux任务优先级

news2024/11/18 9:26:10

这篇笔记记录了linux任务(指线程而非进程)优先级相关的概念,以及用户态可以用来操作这些优先级的系统调用。

基本概念

调度策略

linux内核中的调度器为任务定义了调度策略,也叫调度类,每个任务同一时刻都有唯一的调度策略,这些调度策略按照优先级从高到低依次为:

  • SCHED_DEADLINE

内核在3.14引入了Deadline调度策略,适用于那些需要周期性执行,并且必须在指定时间内完成的任务,其优先级最高。Deadline任务的使用场景较少,不是我们关注的重点。

  • SCHED_FIFO、SCHED_RR

采用这两种调度策略的任务叫做RT任务,这两种调度策略的优先级相同。

SCHED_FIFO任务一旦就绪,它会立即抢占比自己优先级低的任务,它一旦开始运行,除非被更高优先级的RT任务抢占,或者自己主动让出CPU,否则它会一直运行,这类任务没有时间片的限制。

SCHED_RR在SCHED_FIFO的基础上增加了时间片约束,它每次至多运行一段时间,之后如果还没有运行完也会让出CPU,继续下一次轮转,所以将Round-robin。

  • SCHED_OTHER、SCHED_BATCH、SCHED_IDLE

虽然这是三种调度策略,但是调度器基本上对它们不做区分,采用这三种调度策略的任务叫做普通任务。系统中大多数任务的调度策略都是这一类。这类任务共享CPU时间,由内核大名鼎鼎的CFS算法调度运行。

任务优先级

上面的调度策略决定了任务的第一级优先级,其概念是很清晰的,但是linux对于相同或同一类调度策略下各个任务之间的优先级概念就比较混乱,原因是用户态和内核态对这些优先级的叫法不统一。

用户态角度

对于普通任务,用户态是用nice值来表述它们的优先级的,nice值取值[-20, 19]。nice值越大,表示任务对CPU约nice,其优先级最低。

对于RT任务,用户态称其优先级为调度优先级,其值越大,优先级越高,取值范围为[sched_get_priority_min(2), sched_get_priority_min(2)],在linux上总是返回[1, 99]。

内核态角度

task_struct中定义了如下和优先级有关的字段,下面会介绍这些字段的含义。

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

对于普通任务,其用户态的nice值到了内核被转换为静态优先级保存到task_struct的static_prio字段中。下面的NICE_TO_PRIO宏会将用户态的nice值范围从[-20, 19]线性映射到[100, 139]范围,依然是值越小优先级越高。

#define MAX_USER_RT_PRIO    100
#define MAX_RT_PRIO        MAX_USER_RT_PRIO

// NICE_WIDTH为40,表示nice的等级
#define MAX_PRIO        (MAX_RT_PRIO + NICE_WIDTH) // 139
#define DEFAULT_PRIO        (MAX_RT_PRIO + NICE_WIDTH / 2) // 120

/*
 * Convert user-nice values [ -20 ... 0 ... 19 ]
 * to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ],
 * and back.
 */
#define NICE_TO_PRIO(nice)    ((nice) + DEFAULT_PRIO)
#define PRIO_TO_NICE(prio)    ((prio) - DEFAULT_PRIO)

void set_user_nice(struct task_struct *p, long nice)
{
...
    p->static_prio = NICE_TO_PRIO(nice);
}

对于RT任务,其用户态的调度优先级到了内核会被原封不动的保存到task_struct的rt_priority字段中,我们可以称之为实时优先级

static void __setscheduler_params(struct task_struct *p,
        const struct sched_attr *attr)
{
...
    p->rt_priority = attr->sched_priority;
}

内核态归一化优先级

任务的调度策略和优先级上述几种类型,内核态调度器在处理时将这些不同类型的调度策略和优先级进行了归一化处理,将它们映射到了一个线性区间,并使得它们单调性保持一致。归一化后的任务优先级保存在task_struct的normal_prio字段中。

static inline int __normal_prio(struct task_struct *p)
{
    return p->static_prio;
}

static inline int normal_prio(struct task_struct *p)
{
    int prio;

    if (task_has_dl_policy(p))
        prio = MAX_DL_PRIO-1;
    else if (task_has_rt_policy(p))
        prio = MAX_RT_PRIO-1 - p->rt_priority; // MAX_RT_PRIO值为100,将RT任务的调度优先级单调性进行反转
    else
        prio = __normal_prio(p);
    return prio;
}

归一化处理过程实现的效果如下图所示:

Deadline任务的normal_prio字段为-1,RT任务的normal_prio字段范围为[0, 100), 普通任务的normal_prio字段的范围为[100, 139),这样归一化优先级可以统一表达所有任务的优先级,并且规定归一化优先级值越小优先级越高。

内核态动态优先级

归一化优先级也不是调度器最终用于调度的优先级,这是因为调度器有时候会针对RT任务临时性的调整其优先级,因此又引入了动态优先级。动态优先级被保存在了task_struct的prio字段中。动态优先级通过effective_prio()函数获取。

/*
 * Calculate the current priority, i.e. the priority
 * taken into account by the scheduler. This value might
 * be boosted by RT tasks, or might be boosted by
 * interactivity modifiers. Will be RT if the task got
 * RT-boosted. If not then it returns p->normal_prio.
 */
static int effective_prio(struct task_struct *p)
{
    p->normal_prio = normal_prio(p);
    /*
     * If we are RT tasks or we were boosted to RT priority,
     * keep the priority unchanged. Otherwise, update priority
     * to the normal priority:
     */
    if (!rt_prio(p->prio))
        return p->normal_prio;
    return p->prio;
}

用户态接口

从系统手册sched(7)中可以看到,linux共提供了如下接口供用户态获取和调整任务的优先级。从下面的介绍中可以看到,不同的接口有其适用范围。

接口

描述

nice(2)

调整调用线程的nice值

getpriority(2)、setpriority(2)

操作进程、进程组或用户的所有进程的nice值

sched_setscheduler(2)、sched_getscheduler(2)

获取线程的调度策略,设置RT任务的调度策略和调度优先级

sched_setparam(2)、 sched_getparam(2)

sched_setscheduler(2)、sched_getscheduler(2)的变体

sched_setattr(2)、sched_getattr(2)

同时支持RT任务和普通任务调度策略和优先级的接口,该接口在实现上述接口的所有功能外还有额外的扩展功能

nice(2)

将调用线程的nice值加上参数inc,所以正的inc可以降低调用线程的优先级,负的inc可以提高调用线程的优先级。

int nice(int inc);

线程的nice值不是可以随意调整的,其可设置的上限受getrlimit(2)中的RLIMIT_NICE值限制。RLIMIT_NICE的取值范围为[1, 40],假设RLIMIT_NICE的配置为rlimit_cur,那么可设置的nice值上限为20 - rlimit_cur。

getpriority(2)、setpriority(2)

这两个接口用于获取和设置任务的nice值。which和who指定了要操作的任务范围,具体有:

  • which=PRIO_PROCESS,who=PID。任务范围为进程中所有线程;
  • which=PRIO_PGRP,who=进程组长PID。任务范围为进程组中所有线程;
  • which=PRIO_USER,who=UID。任务范围为该用户的所有线程;

可以看到,这两个接口的功能比nice(2)要更加的强大和灵活。

int getpriority(int which, id_t who);
int setpriority(int which, id_t who, int prio);

同样的,nice值的可设置上限受getrlimit(2)中的RLIMIT_NICE值限制。

sched_setscheduler(2)、sched_getscheduler(2)

当用sched_setscheduler(2)将线程的调度策略修改为普通任务时,其param->sched_priority必须为0,即该接口不能设置普通任务的nice值。当用它将线程的调度策略修改为RT任务时,可以用param->sched_priority为RT任务指定调度优先级。

sched_getscheduler(2)可以用来获取线程的调度策略。

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

struct sched_param {
   ...
   int sched_priority;
   ...
};

这两个接口的pid为0时,表示操作的时调用线程。

sched_setparam(2)、 sched_getparam(2)

这组接口就是sched_setscheduler(2)、sched_getscheduler(2)的变体。

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

sched_setattr(2)、sched_getattr(2)

这两个接口是linux特有的,并非POSIX接口,它们支持所有类型任务的调度策略和优先级调整。

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

struct sched_attr {
   u32 size;              /* Size of this structure */
   u32 sched_policy;      /* Policy (SCHED_*) */
   u64 sched_flags;       /* Flags */
   s32 sched_nice;        /* Nice value (SCHED_OTHER, SCHED_BATCH) */
   u32 sched_priority;    /* Static priority (SCHED_FIFO, SCHED_RR) */
   /* Remaining fields are for SCHED_DEADLINE */
   u64 sched_runtime;
   u64 sched_deadline;
   u64 sched_period;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1118359.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Framework系列---输入法服务

Android Framework系列之输入法服务 本文基于Android R(11),从Framework角度介绍Android输入法框架流程及常用调试方法。 写在前面 车载项目需要定制输入法,也有一些POC演示的项目使用原生比如LatinIME(源码路径为/packages/inputmethods…

CVE-2019-9766漏洞实战

1.利用msf生成反向连接的shellcode 2.构造具有反弹shell的MP3文件 将上一步标记的部分替换脚本中的shellcode 3.运行脚本,生成恶意mp3文件 4.msf设置监听并运行exploit 5.打开恶意文件 6.攻击机已经获得shell 文笔生疏,措辞浅薄,望各位大佬不吝赐教…

运行原理:eBPF 是一个新的虚拟机吗?

目录 背景 eBPF 虚拟机是如何工作的? BPF 指令是什么样的? eBPF 程序是什么时候执行的? 小结 背景 前面,我们从最简单的 Hello World 开始,带你借助 BCC 库从零开发了一个跟踪 openat() 系统调用的 eBPF 程序。…

Leetcode1839. 所有元音按顺序排布的最长子字符串

Every day a Leetcode 题目来源:1839. 所有元音按顺序排布的最长子字符串 解法1:滑动窗口 要找的是最长美丽子字符串的长度,我们可以用滑动窗口解决。 设窗口内的子字符串为 window,每当 word[right] > window.back() 时&…

喜讯!持安科技入选2023年北京市知识产权试点单位!

近日,北京市知识产权局发布了“2023年度北京市知识产权试点示范单位及2020年度北京市知识产权试点示范单位复审通过名单”名单。 经过严格的初审、形式审核和专家评审,北京持安科技有限公司入选“2023年北京市知识产权试点单位”。 北京市知识产权试点示…

A预测蛋白质结构

基于AlphaFold2进行蛋白质结构预测的文章解析 RoseTTAFold: Tunyasuvunakool, K., Adler, J., Wu, Z. et al. Highly accurate protein structure prediction for the human proteome. Nature 596, 590–596 (2021) AlphaFold2: Accurate prediction of protein structures a…

git commit报错:running pre-commit hook: lint-staged

报错截图: 报错信息: running pre-commit hook: lint-staged 解决方式: 在项目(vue)的package.json文件中,查找 “husky” 部分,并确认其下的 “pre-commit” 钩子是否正确地引用了 lint-staged。 其中配置示例如下&a…

2023年中国自动排气阀产业链、市场规模及存在问题分析]图[

自动排气阀是一种用于排除管道、容器或设备中累积的空气或气体的装置。在液体流动系统中,气体或空气可能会积聚在管道或容器中,影响流体流动、导致气锁和能效降低。自动排气阀的作用是在系统中的气体达到一定压力时,自动地释放气体&#xff0…

LeetCode_并查集_DFS_中等_2316.统计无向图中无法互相到达点对数

目录 1.题目2.思路3.代码实现(Java) 1.题目 给你一个整数 n ,表示一张 无向图 中有 n 个节点,编号为 0 到 n - 1 。同时给你一个二维整数数组 edges ,其中 edges[i] [ai, bi] 表示节点 ai 和 bi 之间有一条无向边。请…

如何解决电脑出现msvcp140.dll丢失问题,msvcp140.dll丢失的最全解决方法

首先,我们需要了解什么是“msvcp140.dll”。这是一个动态链接库文件,它是Microsoft Visual C 2015 Redistributable的一部分。当计算机运行某些程序时,这个文件会被调用,以支持程序的正常运行。因此,当这个文件丢失时&…

3dmax中导出模型到unity注意事项

从3dmax中导出 1. 注意单位,根据需要,选英寸还是选厘米 2. 不能导出有错误的骨骼,否则导入后模型网格里出现 Skinned Mesh Renderer ,对网格变换移动有影响,正常情况下都应该是 Mesh Renderer 3. 导出一般不带光源和…

【LeetCode刷题】:仅仅反转字母(双指针+字符串)

给你一个字符串 s ,根据下述规则反转字符串: 所有非英文字母保留在原有位置 所有英文字母(小写或大写)位置反转 返回反转后的 s 示例 1: 输入:s “ab-cd” 输出:“dc-ba” 示例 2: …

【sqlserver】配置管理器打不开

问题描述 无法连接到 WMI 提供程序。您没有权限或者该服务器无法访问。请注意,您只能使用SQL Server 配置管理器来管理 SQL Server 2005 和更高版本的服务 器。无效类[0x80041010] 解决方式: 命令提示符-右键-以管理员身份运行,再把以下代码执行一遍&…

案例分析真题--架构师

案例分析真题--架构师 试题1 质量属性架构风格 软件架构设计 系统开发基础 数据库系统 其他嵌入式 试题1 质量属性架构风格

bug记录(feign)——如何给feign加上请求头相关的参数,让生产者获得请求头中参数

问题描述 在微服务的调用中,如果消费者通过feign调用生产者,然后生产者方法中需要用到请求头,从请求头中获得token,但是feign默认是不携带请求头的,因此即使消费者请求feign的时候请求头中携带了token,也没…

QGIS003:【03捕捉工具栏】-点、线、面图层的捕捉操作

引言:QGIS捕捉工具栏包括启用捕捉、捕捉图层、捕捉位置、捕捉容差、启用拓扑编辑、拓扑规则、交叉处使用捕捉、启用追踪、自动捕捉等选项,本文介绍各选项的基本操作。 实验数据: 链接:https://pan.baidu.com/s/1GTeBNQqQJlJPj6Yu…

堆排序;大顶堆、小顶堆

堆排序 基本介绍 堆排序基本思想 堆排序步骤图解 在第二个步骤中,将节点6和它的两个左右节点比较大小,发现右节点最大,所以将节点6和节点9进行交换,如图所示,数组相应位置的值也交换 总结 代码实现 """…

Ubuntu 17.10的超震撼声音权限

从GNOME GUADEC 2017开发者大会归来之后,Canonical的Didier Roche就开始了一个日更博客系列,主要讲述即将带来的Ubuntu 17.10(Artful Aardvark)发行版将如何从Unity到GNOME Shell的转变。有趣的是,Ubuntu Unity桌面环境…

c语言练习91:合并两个有序链表

合并两个有序链表 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 代码1: /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/typedef struct ListNode ListNode; struct Li…

基于springboot实现学生综合成绩测评系统项目【项目源码】

基于springboot实现学生合成绩测评系统演示 开发技术与环境配置 以Java语言为开发工具,利用了当前先进的springboot框架,以MyEclipse10为系统开发工具,MySQL为后台数据库,开发的一个学生综合测评系统。 SpringBoot框架 SpringBo…