Linux学习笔记(05-25)进程优先级设置和进程调度

常把进程分为以下三类:
交互式进程(Interactive process)
批处理进程(Batch process)
实时进程(Real-time process)


Linux2.6 内核是抢占式的,这意味着进程无论是处于内核态还是用户态,都可能被抢占。
调度程序总能成功地找到要执行的进程,事实上,总是至少有一个可运行进程:即swapper 空闲进程,它的PID等于0,而且它只有在CPU不能执行其他进程时才执行。

每个Linux进程总是按照下面的调度类型被调度:
SCHED_FIFO
先进先出的实时进程,此特性只有在具备相同优先级的实时进程间体现。当调度程序把CPU分配给进程的时候,它把该进程描述符保留在运行队列链表的当前位置。如果没有其他可运行的高优先权实时进程,进程就继续使用CPU, 想用多久就用多久,即使还有其他具有相同优先权的实时进程处于可运行状态。(sched_rr 为2)
SCHED_RR
时间片轮转的实时进程。当调度程序把CPU分配给进程的时候,把该进程的描述符放在运行队列链表的末尾。这种策略保证对所有具有相同优先权的SCHED_RR实时进程公平地分配CPU 时间。(sched_fifo 为1)
SCHED_NORMAL
普通的分时进程。(sched_normal 或者 sched_other是Linux默认的调度策略,其值为0)


其中后两个为实时进程的调度策略,第一个是非实时进程的调度策略。

(1)与调度相关的系统调用

nice( ) 改变一个普通进程的静态优先级
getpriority( ) 获得一组普通进程的最大优先级
setpriority( ) 设置一组普通进程的静态优先级
sched_getscheduler( ) 获得一个进程的调度策略
sched_setscheduler( ) 设置一个进程的调度策略和实时优先级
sched_getparam( ) 获得一个进程的实时优先级
sched_setparam( ) 设置一个进程的实时优先级
sched_yield( ) 自愿放弃处理器而不阻塞
sched_get_ priority_min( ) 获得一种策略的最小实时优先级
sched_get_ priority_max( ) 获得一种策略的最大实时优先级
sched_rr_get_interval( ) 获得时间片轮转策略的时间片值Linux内核为每个创建的进程分配时间片并根据其优先级进行调度。


(二)进程优先级

linux2.6内核将任务优先级进行了一个划分:
    0——99  实时进程 
    100——139   非实时进程


当进程被创建时,其对应的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
        #define    MAX_PRIO (MAX_RT_PRIO + 40)


内核中规定进程的优先级范围为[0, MAX_PRIO-1]。其中实时任务的优先级范围是[0, MAX_RT_PRIO-1],非实时任务的优先级范围是[MAX_RT_PRIO, MAX_PRIO-1]。优先级值越小,意味着级别越高,任务先被内核调度。


那任务的优先级又是如何确定的呢?和task_struct中的成员是什么关系?
1. prio指的是任务当前的动态优先级,其值影响任务的调度顺序。
        2. normal_prio指的是任务的常规优先级,该值基于static_prio和调度策略计算。

        3. static_prio指的是任务的静态优先级,在进程创建时分配,该值会影响分配给任务的时间片的长短和非实时任务动态优先级的计算。

静态优先权本质上决定了进程的基本时间片因此静态优先权越高,基本时间片就越长

动态优先权是调度程序在选择新进程来运行的时候使用的数。它与静态优先权的关系用下面的经验公式表示。
动态优先权=max(100,min (静态优先权-bonus +5,139))
Bonus 是范围从0到10的值,bonus的值小于5表示降低动态优先权以示惩罚,bonus的值大于5表示增加动态优先权以示额外奖赏。Bonus的值依赖于进程过去的情况,说得更准确一些是与进程的平均睡眠时间相关。
平均睡眠时间也被调度程序用来确定一个给定进程是交互进程还是批处理进程。高优先权进程比低优先权进程更容易成为交互进程。

static_prio = MAX_RT_PRIO + 20 + nice(nice的缺省值是0,范围[20, 19])

        4. rt_priority指的是任务的实时优先级。若为0表示是非实时任务,[1, 99]表示实时任务,值越大,优先级越高。

rt_priority缺省值为0,表示非实时任务。[1,99]表示实时任务;

对于一个实时进程,有两个参数来表明优先级:prio 和 rt_priority。
    prio才是调度所用的最终优先级数值,这个值越小,优先级越高;
    rt_priority 被称作实时进程优先级,它要经过转化prio=MAX_RT_PRIO - 1- p->rt_priority; 
    MAX_RT_PRIO = 99;

这样意味着rt_priority值越大,优先级越高;
    内核提供的修改优先级的函数,是修改rt_priority的值,所以越大,优先级越高。

调度策略也是根据进程的优先级对它们进行分类:
在Linux 中,进程的优先级是动态的,但对于实时进程,其优先级固定,保证其有固定的时间响应。调度程序跟踪进程正在做什么,并周期性地调整它们的优先级。在这种方式下,在较长的时间间隔内没有使用CPU 的进程,通过动态地增加它们的优先级来提升它们。相应地,对于已经在CPU 上运行了较长时间的进程,通过减少它们的优先级来处罚它们。普通进程的优先级是由动态优先权和静态优先权组合的,而动态优先权会随之运行时间的增加而减少,所以整体上其优先级会下降,以便让其他非实时进程运行。

(3)进程调度时机

Linux调度时机主要有:
1、进程状态转换的时刻:进程终止、进程睡眠;
2、当前进程的时间片用完时(current->counter=0);
3、设备驱动程序主动调用schedule;
4、进程从中断、异常及系统调用返回到用户态时;

(4)改变进程的优先级

进程调度的依据,在每个进程的task_struct结构中有这么五项:
need_resched、nice、counter、policy 及rt_priority
need_resched: 在调度时机到来时,检测这个域的值,如果为1,则调用schedule() 。
counter: 进程处于运行状态时所剩余的时钟滴答数,每次时钟中断到来时,这个值就减1。当这个域的值变得越来越小,直至为0时,就把need_resched 域置1,因此,也把这个域叫做进程的“动态优先级”。
nice: 进程的“静态优先级”,这个域决定counter 的初值。只有通过nice(), sched_setparam()系统调用才能改变进程的静态优先级。
rt_priority: 实时进程的优先级
policy: 从整体上区分实时进程和普通进程,

实时进程应该先于普通进程而运行。

可以通过系统调用sched_setscheduler( )来改变调度的策略。对于同一类型的不同进程,采用不同的标准来选择进程。
对于普通进程,选择进程的主要依据为counter和nice 。
对于实时进程,Linux采用了两种调度策略,即FIFO(先来先服务调度)和RR(时间片轮转调度)

#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学习笔记(05-25)进程优先级设置和进程调度)