背景:前几天项目上遇到一个问题,在某些场景下cpu进不了深睡眠,导致功耗增加。由上文的分析可知,睡眠的等级是和当时系统可忍受的退出延迟相关,可cpuidle_governor_latency_req获取,这个值是通过QoS子系统计算得到的。通过调试发现在该场景下计算得到的退出延时的值很小,甚至小于idle0的exit-latency的值,导致cpu根本进不了深睡眠。为了搞清楚为啥QoS计算出的退出延迟这么小,决定好好撸一撸这个PM QoS子系统 QoS广泛应用在下面三个领域: a) […]
背景:前几天项目上遇到一个问题,在某些场景下cpu进不了深睡眠,导致功耗增加。由上文的分析可知,睡眠的等级是和当时系统可忍受的退出延迟相关,可cpuidle_governor_latency_req获取,这个值是通过QoS子系统计算得到的。通过调试发现在该场景下计算得到的退出延时的值很小,甚至小于idle0的exit-latency的值,导致cpu根本进不了深睡眠。为了搞清楚为啥QoS计算出的退出延迟这么小,决定好好撸一撸这个PM QoS子系统 QoS广泛应用在下面三个领域: a) […]
rwsem特性: a) 允许多个reader同时进入临界区; b) 只允许一个writer同时进入临界区; c) 当临界区是reader时,其他reader可以进入临界区,但是writer必须等着; d) 当临界区是writer时,其他任何线程,不管是reader还是writer,都必须等着; 本文分析基于Linux5.10.61 一、数据结构 1.1 rw_semaphore 在设计rw_semaphore数据结构时考虑到了cache的一致性,我们自己在编码时也应该考虑 [……
注意:ARMv6之前的CPU并不支持SMP,之后的ARM架构都是支持SMP的,因此,对于ARM处理器,其原子操作分成了两个阵营,一个是支持SMP的ARMv6之后的CPU,另外一个就是ARMv6之前的,只有单核架构的CPU 另外,ARMv6之前的CPU只支持swp/swpb指令,ARMv6之后的CPU只支持ldrex/strex指令 一、数据结构 1.1 atomic_t - 实际就是一个int型变量 typedef struct { int counter; [……
ARM提供了SWP或LDREX/STREX指令集,这两个指令从RTL层面保证了"在某一时刻,只能有一个CPU成功修改内存中的值"。kernel利用这个特点,实现了atomic和spin等操作,是所有同步操作的基石。 一、术语介绍 1.1 睡眠 睡眠是对线程而言的,是线程的一种特殊状态,此被"睡眠"的对象一定要有一个task_struct结构,也就是说只有线程才能睡眠。 睡眠是当前线程在等待暂时无法获得的资源时,避免当前线程一直占用着CPU,将自己放入一个等待队列中,让出CPU给别 [……
uclamp共有3个维度: system维度:任何task的uclamp值,不管是uclamp_min还是uclamp_max,都不得大于system维度指定的uclamp值(实现参见uclamp_eff_get)。system维度的uclamp值可通过procfs中的节点设置 cgroup维度:任何task的uclamp值,不管是uclamp_min还是uclamp_max,都必须落在所属group指定的uclamp_min ~ uclamp_max之间(这个和system有差异,ta [……
预估负载util_est的提出基于下面两个背景: 由上一章我们对pelt的分析,知道pelt中负载{load|runnable|util}_avg更新的周期是1ms(严格的说是1024us),负载累加还是比较慢的 cpu调频(schedutil)依赖于cpu的util值,util值小,频率低,util值变化慢,调频慢 对task的迁核依赖于util值,如果util很小,则很难被迁移到大核上去 假如我们遇到这样的场景:一个重负载的线程,因为某种原因即将进入睡眠状态,这个重载线程在睡 [……
PELT,是per-entity load tracking的缩写,也就是以entity为单位进行负载跟踪,与之相对的是per-rq load tracking,即以rq为单位进行负载跟踪 注:本文分析基于Linux 5.10.61 一、前言 注:本章摘自蜗窝科技 1.1 为何要进行负载跟踪? 对于Linux内核而言,做一款好的进程调度器是一项非常具有挑战性的任务,主要原因是在进行CPU资源分配的时候必须满足如下的需求: 它必须是公平的 快速响应 系统的throughput( [……
一、基础概念 1.1 dmips是什么 mips(Million Instructions executed Per Second):表示每秒执行了多少百万条指令,用于衡量cpu的处理能力 dmips: D是Dhrystone的缩写,Dhrystone是一种整数运算测试程序,dmips表示cpu在运行这种测试程序时的mips,即dmips表示每秒执行多少百万条整数运算指令,Linux中用该值表示cpu的算力 dmips-mhz: 表示cpu在1MHz的频率下,每秒钟可以执行多少d [……
术语约定: gse: group se grq: group cfs_rq tg: task_group 一、前言 1.1 为什么要引入组调度 Linux调度器的粒度是一个进程,这样的粒度在很多场景是不合适的,比如,希望达到用户粒度,也就是希望每个用户占有相同的cpu时间。但是,在各个用户拥有的进程数量不同的情况下,显然调度器会将cpu更多地分配给进程数量多的用户。组调度的引入,正是解决此问题的 linux的cgroup(control group)支持将进程分组,然后按 [……
一、知识点梳理 首先列出本文要点: 虚拟运行时间计算流程: 进程每降低一个nice值,将多获得10%cpu的时间 nice为0时,虚拟运行时间=物理运行时间 nice为0时,权重为1024,即下面的NICE_0_LOAD=1024 牢记做功的概念: vruntime1 * weight1 = vruntime2 * weight2 由做功的概念可知虚拟运行时间和物理运行时间的转换关系如下: vriture_runtime * weight = wall_time * NI [……
调度是一个庞大的模块,涉及的知识点很多,下面我们来简单梳理一下 X、调度器 调度器决定了将哪个进程放到CPU上执行,以及执行多长时间。操作系统进行合理的进程调度,使得资源得到最大化的利用。调度器需要合理的分配CPU时间给运行的进程,创造一种所有进程并行运行的错觉,这就对调度器提出了要求: 调度器分配给task的时间不能太长,否则会导致其他的程序响应延迟,难以保证公平性 调度器分配给task的时间也不能太短,因为每次调度会导致上下文切换,这种切换开销也很大,即频繁的上下文切换导 [……
X、sched_class - 调度类 struct sched_class { //系统中所有的调度类通过该成员链接起来,排在链表前面的优先级最高 const struct sched_class *next; //向该调度器的runqueue链表上添加/删除一个进程,即入列/出列操作 void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags); void […]
注:因为个人时间和精力的原因,本文未完结,后面有机会再补充吧! CPU提供了多种idle级别,这些idle级别的主要区别是"进入idle时的功耗"和"退出时延迟"。driver层负责定义这些idle状态(即描述每一个状态的功耗和延迟分别是多少),并实现进入和退出相关的操作。最终,driver会把这些信息告诉governor层,由governor根据具体的应用场景,决定要选用哪种idle等级?依据是什么?是考虑cpu的负载还是系统中的iowaiter的个数? 所以governor层 [……
driver层直接和硬件对接,回答了下面问题: 怎样进入指定的C state?是操作对应的寄存器,还是执行对应的指令? 什么时候退出idle?要怎样才能退出idle?是收到中断自动退出还是写相应的寄存器? 怎样理解不同等级的C state? 可见driver层是和平台强相关的一层,不同平台的cpu会有不同的driver 一、从设备树中提取C state信息 注意:这里从设备树中提取的是标准内核支持的C state属性,还有一些平台自定义的一些属性,在arm_cpu_idle中 [……
cpuidle子系统的core层,主要完成如下工作 为C state、device、driver、governor定义数据结构; 组织和管理device、driver、governor结构; 提供API接口 向上,为idle线程提供接口,这样idle线程就能通过调用这些API接口,方便的完成C state的选核和进入等流程 向下,为device、driver、governor提供注册接口 在sysfs中提供用户空间接口,用户空间可以通过这些节点完成相关信息查询和控制等功能 一、数 [……