您的位置:首页 > 其它

进程的优先级 与 CFS 进程调度

2016-06-06 17:42 267 查看
在Linux下改变进程的优先级

作者:曾老师,华清远见嵌入式学院讲师。

作为多任务的操作系统,Linux内核为每个创建的进程分配时间片并根据其优先级进行调度。当进程被创建时,其对应的task_struct里包含了四个优先级:
struct task_struct {
……
int prio, static_prio, normal_prio;
unsigned int rt_priority;
……
};
在内核头文件include/linux/sched.h中定义了如下宏
#define    MAX_USER_RT_PRIO 100
#define    MAX_RT_PRIO MAX_USER_RT_PRIO     //100
#define    MAX_PRIO (MAX_RT_PRIO + 40)      //140

内核中规定进程的优先级范围为[0, MAX_PRIO-1]
实时任务的优先级范围是[0, MAX_RT_PRIO-1]          //[0,99]
非实时任务的优先级范围是[MAX_RT_PRIO, MAX_PRIO-1] //[100,139]

优先级值越小,意味着级别越高,任务先被内核调度。
那任务的优先级又是如何确定的呢?和task_struct中的成员是什么关系?
① prio指的是任务当前的动态优先级,其值影响任务的调度顺序。
② normal_prio指的是任务的常规优先级,该值基于static_prio和调度策略计算。
③ static_prio指的是任务的静态优先级,在进程创建时分配,该值会影响分配给任务的时间片的长短和非实时任务动态优先级的计算。
④ rt_priority指的是任务的实时优先级。若为0表示是非实时任务,[1, 99]表示实时任务,值越大,优先级越高。

static_prio = MAX_RT_PRIO + 20 + nice(nice的缺省值是0,范围[20, 19])
rt_priority缺省值为0,表示非实时任务。[1,99]表示实时任务

对于实时任务,prio = normal_prio = static_prio
对于非实时任务 prio = normal_prio = MAX_RT_PRIO – 1 – rt_priority
prio的值在使用实时互斥量时会暂时提升,释放后恢复成normal_prio

下面来了解一下如何在应用程序中改变进程的优先级。
#include <sys/time.h>
#include <sys/resource.h>
int setpriority(int which, int who, int prio);
//该函数可以修改进程、进程组或用户所有进程的nice值从而影响static_prio
which : PRIO_PROCESS     // 修改某个进程
PRIO_PGRP        // 修改进程组
PRIO_USER        // 修改用户所有进程
who : 进程号(0表示当前调用进程)、进程组号或UID
prio : 新的用户态优先级(即nice值,范围[-20,19])
返回值 : 执行成功返回0,失败返回-1并设置errno

if (setpriority(PRO_PROCESS, 0, 2) <0)
{
perror(“fail to setpriority”);
exit(-1);
}

************************************************************************
#include <sched.h>
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);

struct sched_param
{
int __sched_priority;
};
// 该函数修改某个进程的调度策略和rt_priority
pid :    要修改的进程号,0表示当前调用进程
policy : 调度策略
SCHED_OTHER(针对非实时进程的调度策略)
SCHED_RR(针对实时进程的轮转调度策略)
SCHED_FIFO(针对实时进程的先进先出调度策略)

param : 指向的结构体中存放着要设置的rt_priority
返回值 : 执行成功返回0,失败返回-1并设置errno
……
struct sched_param sp = {1};
if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0)
{
perror(“fail to sched_setscheduler”);
exit(-1);
}


linux的线程调度策略,linux将进程分为下面两类
实时进程
¢ 对调度延迟的要求最高,要求立即响应并执行
¢ 调度策略:FIFO、Round Robin
普通进程
Ø交互式进程:间或处于睡眠态,对响应速度要求比较高
Ø批处理进程:在后台执行,能够忍受响应延迟

普通进程调度策略使用CFS,CFS是现在被内核采纳的调度器。它从RSDL/SD中吸取了完全公平的思想,不再跟踪进程的睡眠时间,也不再企图区分交互式进程
CFS算法中,每个进程都有一个“虚拟运行时间”表示该进程运行了“多长时间”,而调度器会选择虚拟运行时间最小的进程来运行

虚拟运行时间的计算与进程实际运行时间成正比,而与进程优先级成反比

CFS以虚拟运行时间作为键值构造一棵红黑树,从而实现了快速更新和删除完全公平调度Completely Fair Scheduler

核心思想:根据进程的优先级按比例分配运行时间

(公式1)

1.分配给进程的运行时间 = 调度周期 * 进程权重 / 所有进程权重之和

调度周期:将所有处于TASK_RUNNING态进程都调度一遍的时间
CFS对时钟做抽象,引入了虚拟运行时间vruntime的概念,每个进程有自己的vruntime

2.vruntime = 实际运行时间 * NICE_0_LOAD /进程权重

(公式2)
由公式1和2得:
vruntime = (调度周期 * 进程权重/所有进程总权重) * NICE_0_LOAD/进程权重= 调度周期 * NICE_0_LOAD/所有进程总权重

从vruntime的角度,分配给所有进程的时间是一样的
CFS算法,每次选择vruntime最小的进程运行,所有进程的vruntime增长速度宏观上看是同时推进的
CFS 维护了一个以vruntime为顺序的红黑树,可以快速高效地插入或删除任务
调度器每次选择最左侧结点的进程运行,运行结点从树中删除;进程切换时,切换下来的就绪态进程再重新插入树中,
因为换下来的进程一般vruntime比较大所以会靠近树的右侧;总体来说树的内容从右侧迁移到左侧以保持平衡。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: