什么是进程优先级?
进程优先级:将处理器资源分配给进程的先后顺序
Linux 中每个进程都有相应的优先级 (优先级可能动态改变)
进程优先级决定进程 何时执行 和 获得处理器的时间
进程优先级通常表现为一个整数值 (数值大小决定优先级高低)
Linux 中的进程类型 (用户观点)
交互型进程
- 表现:较多的人机交互,进程会不断进入阻塞状态,等待用户输入
- 特点:用户输入后,必须被及时唤醒执行处理逻辑
批处理型进程
- 表现:不需要人机交互,在后台执行
- 特点:对时间不敏感,常用于数据处理
Linux 中的进程类型 (内核观点)
普通进程
- 使用完全公平策略进行调度的进程 (SCHED_OTHER)
- 不能保证时间要求严格或高优先级的进程优先执行
实时进程
- 使用 SCHED_FIFO 和 SCHED_RR 进行调度的进程
- 根据进程优先级进行实时调度,在一定程度上保证实时性
进程实时性与优先级
实时性
- 硬实时:对响应时间要求非常严格,必须保证在一定时间内完成
- 软实时:硬实时的弱化形式,可接受一定程度上小概率的超时响应
进程分类
- 普通进程:优先级 [100, 139] => 默认 120,值越小优先级越高
- 实时进程:优先级 [0, 99] => sudo chrt -f 11 ./a.out 值越大优先级越高
细说进程优先级
注意:
- 规范优先级用于规范实时优先级和静态优先级,实时优先级和静态优先级可以转换为规范优先级
- 一般情况下动态优先级等于规范优先级
- 不同之处在于,动态优先级可能暂时被提高
- 对进程的处理都以动态优先级为准
静态优先级是普通进程使用的优先级,实时优先级是实时进程使用的优先级
normal_prio 函数用于将普通进程的静态优先级或实时进程的实时优先级转换为规范优先级,转换为规范优先级后,值越小,优先级越高
top 命令中的 PR 指什么?
实时进程
- PR = 规范优先级 - 100
- PR = rt => rt_priority == 99
普通进程
- PR = 规范优先级 - 100
PR 值为负数的进程为实时进程,上图中 PR 为 -51 的进程为实时进程,它的规范优先级为 -51 + 100 = 49,其余进程为普通进程
再论完全公平策略 (Completely Fair Scheduler)
"完全公平" 并非意味着所有进程获得相等时间片
"完全公平" 指的是所有进程都能获得时间片 (时间片未必相等)
问题:完全公平策略中如何确定进程的执行时间片?
- 引入权重 (weight),进程间按照权重比例分配时间片
- weight = 1024 / (1.25 ^ nice_value)
- nice_value => 取值范围 [-20, 19],默认值为 0,值越高权重越低
nice_value 对于普通的意义
假设 A 和 B 是处理相同计算任务的进程:
- A 进程:nice == 0,则:weight == 1024
- B 进程:nice == 5,则:weight == 335.5432
- 所以:A 进程获取的处理器时间大致是 B 进程的 3 倍
实验设计
父进程创建子进程,并开始复杂计算
子进程设置 nice_value 为 5,并开始复杂计算
期望:父进程占用的处理器时间是子进程 3 倍左右
普通进程 nice_value 接口
#include <sys/time.h>
#include <sys/resource.h>
int getpriority(int which, int who);
int setpriority(int which, int who, int prio);
普通进程优先级实验
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>
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 = fork();
if( pid > 0 )
{
printf("pid = %d, ppid = %d, pgid = %d\n",
getpid(), getppid(), getpgrp());
if( set_nice(0) )
{
heavy_work();
}
else
{
printf("set nice_value failed...\n");
}
}
else if( pid == 0 )
{
printf("pid = %d, ppid = %d, pgid = %d\n",
getpid(), getppid(), getpgrp());
if( set_nice(5) )
{
heavy_work();
}
else
{
printf("set nice_value failed...\n");
}
}
else
{
printf("child process create failed...\n");
}
return 0;
}
heavy_work 函数用于模拟繁重的计算
该程序创建了一个子进程,将父进程的 nice 值设置为 0,子进程的 nice 值设置为 5,然后调用 heavy_work 函数进行繁重的计算
我们使用 taskset -c 0 ./a.out 命令让这个程序只能运行在 cpu0 上,然后使用 top 命令来观察父子进程的 cpu 占用率,看父进程占用的处理器时间是否是子进程 3 倍左右
父进程的 cpu 占用率约为 75%,而子进程的 cpu 占用率约为 25%,父进程占用 cpu 的时间是子进程的 3 倍左右