首页 > 系统相关 >Linux进程调度器-CPU负载

Linux进程调度器-CPU负载

时间:2024-10-29 23:34:07浏览次数:1  
标签:load HZ 负载 调度 计算 Linux CPU

1. 概述

CPU负载(cpu load)指的是某个时间点进程对系统产生的压力。来张图来类比下(参考Understanding Linux CPU Load)

  • CPU的运行能力,就如大桥的通行能力,分别有满负荷,非满负荷,超负荷等状态,这几种状态对应不同的cpu load值;
  • 单CPU满负荷运行时cpu_load为1,当多个CPU或多核时,相当于大桥有多个车道,满负荷运行时cpu_load值为CPU数或多核数;
  • CPU负载的计算(以单CPU为例),假设一分钟内执行10个任务代表满负荷,当一分钟给出30个任务时,CPU只能处理10个,剩余20个不能处理,cpu_load=3;

在实际系统中查看:

  • cat /proc/cpuinfo:查看CPU信息;
  • cat /proc/loadavg:查看cpu最近1/5/15分钟的平均负载:

计算CPU负载,可以让调度器更好的进行负载均衡处理,以便提高系统的运行效率。此外,内核中的其他子系统也可以参考这些CPU负载值来进行相应的调整,比如DVFS等。

目前内核中,有以下几种方式来跟踪CPU负载:

  1. 全局CPU平均负载;
  2. 运行队列CPU负载;
  3. PELT(per entity load tracking);

这也是本文需要探讨的内容,开始吧。

2. 全局CPU平均负载

2.1 基础概念

先来明确两个与CPU负载计算相关的概念:

  1. active task(活动任务):只有知道活动任务数量,才能计算CPU负载,而活动任务包括了TASK_RUNNINGTASK_UNINTERRUPTIBLE两类任务。包含TASK_UNINTERRUPTIBLE任务的原因是,这类任务经常是在等待I/O请求,将其包含在内也合理;
  2. NO_HZ:我们都知道Linux内核每隔固定时间发出timer interrupt,而HZ是用来定义1秒中的timer interrupts次数,HZ的倒数是tick,是系统的节拍器,每个tick会处理包括调度器、时间管理、定时器等事务。周期性的时钟中断带来的问题是,不管CPU空闲或繁忙都会触发,会带来额外的系统损耗,因此引入了NO_HZ模式,可以在CPU空闲时将周期性时钟关掉。在NO_HZ期间,活动任务数量的改变也需要考虑,而它的计算不如周期性时钟模式下直观。

2.2 流程

Linux内核中定义了三个全局变量值avenrun[3],用于存放最近1/5/15分钟的平均CPU负载。

看一下计算流程:

  • 计算活动任务数,这个包括两部分:1)周期性调度中新增加的活动任务;2)在NO_HZ期间增加的活动任务数;

  • 根据活动任务数值,再结合全局变量值avenrun[]中的old value,来计算新的CPU负载值,并最终替换掉avenrun[]中的值;

  • 系统默认每隔5秒钟会计算一次负载,如果由于NO_HZ空闲而错过了下一个CPU负载的计算周期,则需要再次进行更新。比如NO_HZ空闲20秒而无法更新CPU负载,前5秒负载已经更新,需要计算剩余的3个计算周期的负载来继续更新;

2.3 计算方法

Linux内核中,采用11位精度的定点化计算,CPU负载1.0由整数2048表示,宏定义如下:

#define FSHIFT          11		        /* nr of bits of precision */
#define FIXED_1         (1<<FSHIFT)	    /* 1.0 as fixed-point */
#define LOAD_FREQ       (5*HZ+1)	    /* 5 sec intervals */
#define EXP_1           1884		    /* 1/exp(5sec/1min) as fixed-point */
#define EXP_5           2014		    /* 1/exp(5sec/5min) */
#define EXP_15          2037		    /* 1/exp(5sec/15min) */

计算公式如下:

  • load值为旧的CPU负载值avenrun[],整个计算完成后得到新的负载值,再更新avenrun[]
  • EXP_1/EXP_5/EXP_15,分别代表最近1/5/15分钟的定点化值的指数因子;
  • active值,根据读取calc_load_tasks的值来判断,大于0则乘以FIXED_1(2048)传入;
  • 根据activeload值的大小关系来决定是否需要加1,类似于四舍五入的机制;

关键代码如下:

	active = atomic_long_read(&calc_load_tasks);
	active = active > 0 ? active * FIXED_1 : 0;

	avenrun[0] = calc_load(avenrun[0], EXP_1, active);
	avenrun[1] = calc_load(avenrun[1], EXP_5, active);
	avenrun[2] = calc_load(avenrun[2], EXP_15, active);
  • NO_HZ模式下活动任务数量更改的计算 由于NO_HZ空闲效应而更改的CPU活动任务数量,存放在全局变量calc_load_nohz[2]中,并且每5秒计算周期交替更换一次存储位置(calc_load_read_idx/calc_load_write_idx),其他程序可以去读取最近5秒内的活动任务变化的增量值。

    计算示例 假设在某个CPU上,开始计算时load=0.5,根据calc_load_tasks值获取不同的active,中间进入NO_HZ模式空闲了20秒,整个计算的值如下图:

3. 运行队列CPU负载

  • Linux系统会计算每个tick的平均CPU负载,并将其存储在运行队列中rq->cpu_load[5],用于负载均衡;

下图显示了计算运行队列的CPU负载的处理流程:

img

最终通过cpu_load_update来计算,逻辑如下:

img

  • 其中传入的this_load值,为运行队列现有的平均负载值。

上图中的衰减因子,是在NO_HZ模式下去进行计算的。在没有使用tick时,从预先计算的表中计算负载值。Linux内核中定义了两个全局变量:

#define DEGRADE_SHIFT		7

static const u8 degrade_zero_ticks[CPU_LOAD_IDX_MAX] = {0, 8, 32, 64, 128};
static const u8 degrade_factor[CPU_LOAD_IDX_MAX][DEGRADE_SHIFT + 1] = {
	{   0,   0,  0,  0,  0,  0, 0, 0 },
	{  64,  32,  8,  0,  0,  0, 0, 0 },
	{  96,  72, 40, 12,  1,  0, 0, 0 },
	{ 112,  98, 75, 43, 15,  1, 0, 0 },
	{ 120, 112, 98, 76, 45, 16, 2, 0 }
};

衰减因子的计算主要是在delay_load_missed()函数中完成,该函数会返回load * 衰减因子的值,作为上图中的old_load。计算方式如下:

img

4. PELT

PELT, Per-entity load tracking。在Linux引入PELT之前,CFS调度器在计算CPU负载时,通过跟踪每个运行队列上的负载来计算;在引入PELT之后,通过跟踪每个调度实体的负载贡献来计算。(其中,调度实体:指task或task_group

4.1 PELT计算方法

总体的计算思路:将调度实体的可运行状态时间(正在运行+等待CPU调度运行),按1024us划分成不同的周期,计算每个周期内该调度实体对系统负载的贡献,最后完成累加。其中,每个计算周期,随着时间的推移,需要乘以衰减因子y进行一次衰减操作。

先来看一下每个调度实体的负载贡献计算公式:

img

  • 当前时间点的负载贡献 = 当前时间点负载 + 上个周期负载贡献 * 衰减因子;
  • 假设一个调度实体被调度运行,运行时间段可以分成三个段d1/d2/d3,这三个段是被1024us的计算周期分割而成,period_contrib是调度实体last_update_time时在计算周期间的贡献值,;
  • 总体的贡献值,也是根据d1/d2/d3来分段计算,最终相加即可;
  • y为衰减因子,每隔1024us就乘以y来衰减一次;

计算的调用流程如下图:

img

  • 函数主要是计算时间差,再分成d1/d2/d3来分段计算处理,最终更新相应的字段;
  • decay_load函数要计算val * y^n,内核提供了一张表来避免浮点运算,值存储在runnable_avg_yN_inv数组中;
static const u32 runnable_avg_yN_inv[] = {
	0xffffffff, 0xfa83b2da, 0xf5257d14, 0xefe4b99a, 0xeac0c6e6, 0xe5b906e6,
	0xe0ccdeeb, 0xdbfbb796, 0xd744fcc9, 0xd2a81d91, 0xce248c14, 0xc9b9bd85,
	0xc5672a10, 0xc12c4cc9, 0xbd08a39e, 0xb8fbaf46, 0xb504f333, 0xb123f581,
	0xad583ee9, 0xa9a15ab4, 0xa5fed6a9, 0xa2704302, 0x9ef5325f, 0x9b8d39b9,
	0x9837f050, 0x94f4efa8, 0x91c3d373, 0x8ea4398a, 0x8b95c1e3, 0x88980e80,
	0x85aac367, 0x82cd8698,
};

Linux中使用struct sched_avg来记录调度实体和CFS运行队列的负载信息,因此struct sched_entitystruct cfs_rq结构体中,都包含了struct sched_avg,字段介绍如下:

struct sched_avg {
	u64				last_update_time;    //上一次负载更新的时间,主要用于计算时间差;
	u64				load_sum;            //可运行时间带来的负载贡献总和,包括等待调度时间和正在运行时间;
	u32				util_sum;            //正在运行时间带来的负载贡献总和;
	u32				period_contrib;     //上一次负载更新时,对1024求余的值;
	unsigned long	load_avg;           //可运行时间的平均负载贡献;
	unsigned long	util_avg;           //正在运行时间的平均负载贡献;
};

4.2 PELT计算调用

PELT计算的发生时机如下图所示:

img

  • 调度实体的相关操作,包括入列出列操作,都会进行负载贡献的计算;

PELT的算法还在持续的改进中,各个内核版本也存在差异,大体的思路已经在上文中介绍到了,细节就不再深入分析了。

标签:load,HZ,负载,调度,计算,Linux,CPU
From: https://www.cnblogs.com/linhaostudy/p/18514738

相关文章

  • 操作系统(7) (POSIX--Linux线程编程---使用多线程计算平方pthread_t/create/join应用)
    1.代码目的我们希望创建一个程序:启动多个线程,每个线程计算一个数字的平方值。每个线程将计算结果返回给主线程。主线程接收每个线程的返回值,并将结果打印出来。在这个例子中,我们通过传递不同的参数给每个线程,来让每个线程计算不同数字的平方值。2.代码实现以下是代码的......
  • 【linux网络编程】| socket套接字 | 实现UDP协议聊天室
        前言:本节内容将带友友们实现一个UDP协议的聊天室。主要原理是客户端发送数据给服务端。服务端将数据再转发给所有链接服务端的客户端。所以,我们主要就是要实现客户端以及服务端的逻辑代码。那么,接下来开始我们的学习吧。    ps:本节内容建议了解so......
  • 【最新】Kali Linux虚拟机安装与优化全攻略:必做设置让你事半功倍!
    内容预览≧∀≦ゞ【干货】KaliLinux虚拟机安装与优化全攻略简介一、从清华镜像源下载KaliLinux镜像二、配置虚拟机硬件设置三、切换KaliLinux的软件源四、解决KaliLinux与Windows之间的复制粘贴互通问题五、Kali终端优化1.调整终端窗口大小、字体大小及光标样式2.......
  • 09 高级cpu设计
    早期计算机提速方式是减少晶体管的切换时间现在有专门在硬件上设计专门除法电路,设计额外电路做专门操作,给cpu内部加上一点ram叫做缓存(ram从此可以一批批传输不用一个个,一批先存在缓存当中,cpu需要直接从缓存取数据),同时使用cpu所有部分,一次性处理多条指令,指令流水线并行处......
  • 最新宝塔安装教程:Linux 稳定版 9.0 宝塔面板+免费SSL证书解析
    在本教程中,我们将详细介绍如何在Linux服务器上安装宝塔面板的最新稳定版9.0,并为您的网站添加免费的SSL证书。此教程适合初学者和有一定经验的用户。通过SSH连接至VPS服务器后,您将能够顺利完成宝塔安装和初步设置,这篇文章将为您提供简单易懂的操作指南,帮助您在几分钟内完成服务......
  • Linux基本指令(一)
    01ls指令语法:ls[选项][目录或文件]功能:罗列当前目录下的指定文件或目录常见选项-a(显示当前目录下的所有文件) -l(显示文件详情) -d(不进入文件里面)示例:ls-a ls-l ls-a-l(命令中的选项可以传递多个)以“.”开头的为隐藏文件,在加上-a后才可见  文件=......
  • 最新宝塔安装教程:Linux 稳定版 9.0 宝塔面板+免费SSL证书解析
    在本教程中,我们将详细介绍如何在Linux服务器上安装宝塔面板的最新稳定版9.0,并为您的网站添加免费的SSL证书。此教程适合初学者和有一定经验的用户。通过SSH连接至VPS服务器后,您将能够顺利完成宝塔安装和初步设置,这篇文章将为您提供简单易懂的操作指南,帮助您在几分钟内完成服务......
  • 0基础读顶会论文—面向应用的云工作负载预测:调查和新视角
    原文链接Abstract准确的工作负载预测对于云用户和提供商来说非常有价值,因为它可以有效指导许多实践,例如性能保证、降低成本和能耗优化。然而,由于工作负载的复杂性和动态性,云工作负载预测非常具有挑战性,并且已经提出了各种解决方案来增强预测行为,与现有调查不同,我们首次从一个新......
  • 【Linux操作系统】Linux配置OpenSSH服务器步骤记录
    1.安装OpenSSH服务器软件包用指令查询,已经全部安装。编辑/etc/ssh/sshd_config文件:#      $OpenBSD:sshd_config,v1.1032018/04/0920:41:22tjExp$#Thisisthesshdserversystem-wideconfigurationfile. See#sshd_config(5)formoreinformat......
  • 如何在Linux上配置软件负载均衡(如HAProxy)
    在Linux环境中,确保应用服务的高可用性和性能是至关重要的。本文将引导你完成以下步骤:1.理解软件负载均衡的重要性;2.选择合适的负载均衡器;3.详细配置HAProxy为例;4.进行性能调优和测试;5.监控和维护。开始负载均衡配置之前,首先需要明确其作用和目标。1.理解软件负载均衡的重要性......