实时进程调度策略 ( 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 的执行权,这样就实现了实时进程每次执行固定的时间片