浅谈进程优先级(下)

本文详细介绍了实时进程调度策略,包括SCHED_RR和SCHED_FIFO的区别,内核优先级与用户优先级的关系,以及nice_value对实时进程的影响。通过示例代码演示了如何定制实时进程执行时间和模拟调度器工作原理。
摘要由CSDN通过智能技术生成

实时进程调度策略 ( SCHED_RR & SCHED_FIFO )

在优先级不同的时候,先执行优先级高的进程

当实时进程优先级相同时:

  • SCHED_RR 采用的策略是时间片轮转,默认的时间片是 100ms
  • SCHED_FIFO 采用的策略是先到先得,先占有处理器的进程会持续执行

深入实时进程优先级

对于实时进程而言,内核模式的优先级与用户模式的优先级并不同

  • 对于内核模式,优先级的值越小,优先级越高 (规范优先级)
  • 对于用户模式,优先级的值越大,优先级越高 (实时优先级)

换算关系:规范优先级 = MAX_RT_PRIO - 实时优先级 - 1

  • 其中:MAX_RT_PRIO 的值为 100
  • 换算后优先级范围:[0, 99]

实时进程调度接口

因为这些 API 是给用户调用的,所有 sched_priority 指的是实时优先级,优先级范围是 [0, 99],值越大,优先级越高

SCHED_BATCH 调度策略适用于科学计算的进程,用于科学计算的进程对 cpu 的占用率会很高;使用该调度策略的进程的动态优先级会略微降低,每次调度获得的时间片会减少一些

问题:

对于实时进程,设置 nice_value 会发生什么?

不会发生什么,实时进程设置 nice_value 并不会改变进程优先级

有趣的问题

ps -al 命令中的 PRI 指什么?

实时进程

PRI = 规范优先级 - 40 = 59 - rt_priority

普通进程

PRI = 规范优先级 - 40 

实时进程优先级实验

demo.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <math.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sched.h>

static void heavy_work()
{
    int i = 0;
    
    while( 1 )
    {
        sin(i++);
    }
}

static int set_nice(int nice)
{
    return setpriority(PRIO_PROCESS, 0, nice) == 0;
}

int main()
{
    int pid = 0;
    struct sched_param sp = {0};
    
    printf("pid = %d, ppid = %d, pgid = %d\n", 
                getpid(), getppid(), getpgrp());
                
    sp.sched_priority = 2;
        
    if( sched_setscheduler(0, SCHED_RR, &sp) == -1 )
    {
        printf("set policy error ==> %d\n", __LINE__);
    }
    
    if( (pid = fork()) > 0 )
    {      
        if( set_nice(2) )
        {
            heavy_work();
        }
        else
        {
            printf("set nice_value failed ==> %d\n", __LINE__);
        }
    }
    else if( pid == 0 )
    {
        printf("pid = %d, ppid = %d, pgid = %d\n", 
                getpid(), getppid(), getpgrp());
                
        sp.sched_priority = 5;
        
        if( sched_setscheduler(0, SCHED_RR, &sp) == -1 )
        {
            printf("set policy error ==> %d\n", __LINE__);
        }
                
        if( set_nice(5) )
        {
            heavy_work();
        }
        else
        {
            printf("set nice_value failed ==> %d\n", __LINE__);
        }
    }
    else
    {
        printf("child process create failed...\n");
    }

    return 0;
}

因为该程序中需要将进程设置为实时优先级,只有 root 用户才能设置,所以在执行这个程序的时候需要使用 sudo 命令使用 root 权限去执行该程序

使用 sudo taskset -c 0 ./a.out 这个命令去运行程序,然后使用 top 命令查看进程执行状态

可以看出只有 pid 为 15796 的进程一直在执行,这个进程是子进程,父进程却没有在执行,这是因为父子进程都是实时进程,父进程的实时优先级为 2,而子进程的实时优先级为 5,子进程的优先级大于父进程的优先级,只要子进程不让出 cpu 的执行权,父进程就一直无法得到执行,程序中没有主动让出 cpu 执行权的操作

我们将子进程 kill 掉,然后再次查看进程状态

kill 掉子进程后,父进程就得到了执行

思考

如何定制实时进程的执行时间?

实现思路

模拟操作系统内核调度器的实现

  • 指定目标进程
  • 执行目标进程
  • 时间片完成,剥夺执行权
  • 选择下一个进程执行

实验设计

将父进程设置为实时进程,且实时优先级为 99 (调度器)

创建子进程,默认为普通进程

定义每个子进程的执行时间片

父进程进入循环调度,根据时间片定义改变子进程的调度策略

核心思想:

  • 利用实时进程的最高优先级 "实现" 调度器
  • 利用 sleep() 函数可使调度器主动 "放弃" 执行权
  • 利用优先执行实时进程的调度策略 "指定" 目标进程

 核心代码实现

值得注意的地方

该方法是应用层模拟实现内核中的 "时间片轮询调度"

因此:

  • 目标进程仅是获得绝大多数的处理器时间
  • 目标进程所获得的时间片不精确于预定义时间

实时进程 "调度" 实验

test.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <math.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sched.h>
#include <signal.h>

static void heavy_work()
{
    int i = 0;
    
    while( 1 )
    {
        sin(i++);
    }
}

static int set_nice(int nice)
{
    return setpriority(PRIO_PROCESS, 0, nice) == 0;
}

int main()
{
    int pid = 0;
    struct sched_param sp = {0};
    pid_t pids[3] = {0};
    int ts[3] = {5, 3, 2};
    int i = 0;
    
    printf("pid = %d, ppid = %d, pgid = %d\n", 
                getpid(), getppid(), getpgrp());
                
    sp.sched_priority = 99;
        
    if( sched_setscheduler(0, SCHED_FIFO, &sp) != -1 )
    { 
        for(i=0; i<3; i++)
        {
            int pid = fork();
            
            if( pid > 0 )
            {
                pids[i] = pid;
                
                sp.sched_priority = 0;
                
                if( sched_setscheduler(pid, SCHED_OTHER, &sp) == -1 )
                {
                    printf("set policy error ==> %d\n", __LINE__);
                    kill(pid, SIGINT);
                }
            }
            else if( pid == 0 )
            {
                printf("pid = %d, ppid = %d, pgid = %d\n", 
                getpid(), getppid(), getpgrp());
                
                heavy_work();
            }
            else
            {
                printf("fork error ==> %d\n", __LINE__);
                exit(-1);
            }
        }
    }
    else
    {
        printf("set parent policy error ==> %d\n", __LINE__);
        exit(-1);
    }
    
    i = 0;
    
    while( 1 )
    {
        sp.sched_priority = 0;
                
        if( sched_setscheduler(pids[i], SCHED_OTHER, &sp) == -1 )
        {
            printf("set process to normal policy error ==> %d\n", __LINE__);
        }
        
        i = (i + 1) % 3;
        
        sp.sched_priority = 30;
                
        if( sched_setscheduler(pids[i], SCHED_FIFO, &sp) == -1 )
        {
            printf("schedule error ==> %d\n", __LINE__);
        }
        
        sleep(ts[i]);
    }

    return 0;
}

将父进程设置为实时进程,调度策略为 SCHED_FIFO,优先级为 99,最大优先级

使用 fork() 函数,创建出三个子进程,并将这三个子进程设置为普通进程,此时父进程为实时进程,优先级最高,所以这三个子进程无法得到执行

第 80 行的 while(1) 循环中,将被调度完成的子进程设置为普通进程,下一个要被调度的子进程设置为实时进程,然后父进程通过 sleep,让出 cpu,使得下一个要被调度的子进程能够得到 cpu 的执行权,等父进程 sleep 结束后,再抢回 cpu 的执行权,这样就实现了实时进程每次执行固定的时间片

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值