1. 前言

在计算机系统中,CPU的功能是执行程序,总结起来就是我们在教科书上学到的:取指、译码、执行。那么问题来了,如果没有程序要执行,CPU要怎么办?也许您会说,停掉就是了啊。确实,是要停掉,但何时停、怎么停,却要仔细斟酌,因为实际的软硬件环境是非常复杂的。

我们回到Linux kernel上,Linux系统中,CPU被两类程序占用:一类是进程(或线程),也称进程上下文;另一类是各种中断、异常的处理程序,也称中断上下文。

进程的存在,是用来处理事务的,如读取用户输入并显示在屏幕上。而事务总有处理完的时候,如用户不再输入,也没有新的内容需要在屏幕上显示。此时这个进程就可以让出CPU,但会随时准备回来(如用户突然有按键动作)。同理,如果系统没有中断、异常事件,CPU就不会花时间在中断上下文。

在Linux kernel中,这种CPU的无所事事的状态,被称作idle状态,而cpuidle framework,就是为了管理这种状态。

注:cpuidle framework系列文章会以ARM64作为示例平台,由于ARM64刚刚发布不久,较早版本的kernel没有相关的代码,因此选用了最新的3.18-rc4版本的kernel。

2. 功能概述

曾经有过一段时间,Linux kernel的cpu idle框架是非常简单的,简单到driver工程师只需要在“include\asm-arm\arch-xxx\system.h”中定义一个名字为arch_idle的inline函数,并在该函数中调用kernel提供的cpu_do_idle接口,就Okay了,剩下的实现kernel全部帮我们做了,如下:

   1: static inline void arch_idle(void)
2: {
3: cpu_do_idle();
4: }

以蜗蜗之前使用过的一个ARM926的单核CPU为例(内核版本为Linux2.6.23),cpuidle的处理过程是:

B start_kernel(arch\arm\kernel\head-common.S)

start_kernel->rest_init(init\main.c)

;系统初始化完成后,将第一个进程(init)变为idle进程,

;以下都是在进程的循环中,周而复始…

cpu_idle->default_idle(arch\arm\kernel\process.c)

arch_idle(include\asm-arm\arch-xxx\system.h)

cpu_do_idle(include/asm-arm/cpu-single.h)

cpu_arm926_do_idle(arch/arm/mm/proc-arm926.S)

mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt ;WFI指令

虽然简单,却包含了idle处理的两个重点:

1)idle进程

idle进程的存在,是为了解决“何时idle”的问题。

我们知道,Linux系统运行的基础是进程调度,而所有进程都不再运行时,称作cpu idle。但是,怎么判断这种状态呢?kernel采用了一个比较简单的方法:在init进程(系统的第一个进程)完成初始化任务之后,将其转变为idle进程,由于该进程的优先级是最低的,所以当idle进程被调度到时,则说明系统的其它进程不再运行了,也即CPU idle了。最终,由idle进程调用idle指令(这里为WFI),让CPU进入idle状态。

“ARM WFI和WFE指令”中介绍过,WFI Wakeup events会把CPU从WFI状态唤醒,通常情况下,这些events是一些中断事件,因此CPU唤醒后会执行中断handler,在handler中会wakeup某些进程,在handler返回的时候进行调度,当没有其他进程需要调度执行的时候,调度器会恢复idle进程的执行,当然,idle进程不做什么,继续进入idle状态,等待下一次的wakeup。

2)WFI

WFI用于解决“怎么idle”的问题。

一般情况下,ARM CPU idle时,可以使用WFI指令,把CPU置为Wait for interrupt状态。该状态下,至少(和具体ARM core的实现有关,可参考“ARM WFI和WFE指令”)会把ARM core的clock关闭,以节省功耗。

也许您会觉得,上面的过程挺好了,为什么还要开发cpuide framework?蜗蜗的理解是:

ARM CPU的设计越来越复杂,对省电的要求也越来越苛刻,因而很多CPU会从“退出时的延迟”和“idle状态下的功耗”两个方面考虑,设计多种idle级别。对延迟较敏感的场合,可以使用低延迟、高功耗的idle;对延迟不敏感的场合,可以使用高延迟、低功耗的idle。

而软件则需要根据应用场景,在恰当的时候,选择一个合适的idle状态。而选择的策略是什么,就不是那么简单了。这就是cpuidle framework的存在意义(我们可以根据下面cpuidle framework的软件架构,佐证这一点)。

3. 软件架构

Linux kernel中,cpuidle framework位于“drivers/cpuidle”文件夹中,包含cpuidle core、cpuidle governors和cpuidle drivers三个模块,再结合位于kernel sched中的cpuidle entry,共同完成cpu的idle管理。软件架构如下图:

1)kernel schedule模块

位于kernel\sched\idle.c中,负责实现idle线程的通用入口(cpuidle entry)逻辑,包括idle模式的选择、idle的进入等等。

2)cpuidle core

cpuidle core负责实现cpuidle framework的整体框架,主要功能包括:

根据cpuidle的应用场景,抽象出cpuidle device、cpuidle driver、cpuidle governor三个实体;

以sysfs的形式,向用户空间提供接口;

向下层的cpuidle drivers模块,提供统一的driver注册和管理接口;

向下层的governors模块,提供统一的governor注册和管理接口。

cpuidle core的代码主要包括:cpuidle.c、driver.c、governor.c、sysfs.c。

3)cpuidle drivers

不同的architecture、不同的CPU core,会有不同的cpuidle driver,平台驱动的开发者,可以在cpuidle core提供的框架之下,开发自己的cpuidle driver。代码主要包括:cpuidle-xxx.c。

4)cpuidle governors

Linux kernel的framework有两种比较固定的抽象模式:

模式1,provider/consumer模式,interrupt、clock、timer、regulator等大多数的framework是这种模式。它的特点是,这个硬件模块是为其它一个或多个模块服务的,因而framework需要从对上(consumer)和对下(provider)两个角度进行软件抽象;

模式2,driver/governor模式,本文所描述的cpuidle framework即是这种模式。它的特点是:硬件(或者该硬件所对应的驱动软件)可以提供多种可选“方案”(这里即idle level),“方案”的实现(即机制),由driver负责,但是到底选择哪一种“方案”(即策略),则由另一个模块负责(即这里所说的governor)。

模式2的解释可能有点抽象,把它放到cpuidle的场景里面,就很容易理解了:

前面讲过,很多CPU提供了多种idle级别(即上面所说的“方案”),这些idle 级别的主要区别是“idle时的功耗”和“退出时延迟”。cpuidle driver(机制)负责定义这些idle状态(每一个状态的功耗和延迟分别是多少),并实现进入和退出相关的操作。最终,cpuidle driver会把这些信息告诉governor,由governor根据具体的应用场景,决定要选用哪种idle状态(策略)。

kernel中,cpuidle governor都位于governors/目录下。

4. 软件流程

在阅读本章之前,还请读者先阅读如下三篇文章:

Linux cpuidle framework(2)_cpuidle core

Linux cpuidle framework(3)_ARM64 generic CPU idle driver

Linux cpuidle framework(4)_menu governor

前面提到过,kernel会在系统启动完成后,在init进程(或线程)中,处理cpuidle相关的事情。大致的过程是这样的(kernel启动相关的分析,会在其它文章中详细介绍):

首先需要说明的是,在SMP(多核)系统中,CPU启动的过程是:

1)先启动主CPU,启动过程和传统的单核系统类似:stext-->start_kernel-->rest_init-->cpu_startup_entry

2)启动其它CPU,可以有多种方式,例如CPU hotplug等,启动过程为:secondary_startup-->__secondary_switched-->secondary_start_kernel-->cpu_startup_entry

上面的代码位于./arch/arm64/kernel/head.S、init/main.c等等,感兴趣的读者可以自行参考。最终都会殊途同归,运行至cpu_startup_entry接口,该接口位于kernel/sched/idle.c中,负责处理CPU idle的事情,流程如下(暂时忽略一些比较难理解的分支,如cpu idle poll等)。

cpu_startup_entry流程:

cpu_startup_entry

arch_cpu_idle_prepare,进行idle前的准备工作,ARM64中没有实现

cpu_idle_loop,进入cpuidle的主循环

如果系统当前不需要调度(!need_resched()),执行后续的动作

local_irq_disable,关闭irq中断

arch_cpu_idle_enter,arch相关的cpuidle enter,ARM64中没有实现

cpuidle_idle_call,main idle function

cpuidle_select,通过cpuidle governor,选择一个cpuidle state

cpuidle_enter,通过cpuidle state,进入该idle状态



中断产生,idle返回(注意,此时irq是被禁止的,因此CPU不能响应产生中断的事件)

cpuidle_reflect,通知cpuidle governor,更新状态

local_irq_enable,使能中断,响应中断事件,跳转到对应的中断处理函数



arch_cpu_idle_exit,和enter类似,ARM64没有实现

具体的代码比较简单,不再分析了,但有一点,还需要着重说明一下:

使用cpuidle framework进入idle状态时,本地irq是处于关闭的状态,因此从idle返回时,只能接着往下执行,直到irq被打开,才能执行相应的中断handler,这和之前传统的cpuidle不同。同时也间接证实了“Linux cpuidle framework(4)_menu governor”中所提及的,为什么menu governor在reflect接口中只是简单的置一个标志。因为reflect是在关中断时被调用的,需要尽快返回,以便处理中断事件。

Linux cpuidle framework(1)_概述和软件架构的更多相关文章

  1. 【原创】Linux cpuidle framework

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  2. Linux Regulator Framework(2)_regulator driver

    转自蜗窝科技:http://www.wowotech.net/pm_subsystem/regulator_driver.html 说实话,这篇好难懂啊... 1. 前言 本文从regulator d ...

  3. Linux regulator framework(1) - 概述【转】

    转自蜗窝科技:http://www.wowotech.net/pm_subsystem/regulator_framework_overview.html 1. 前言 Regulator,中文名翻译为 ...

  4. Linux电源管理-Linux regulator framework概述

    前言 1.  什么是regulator?      regulator翻译为"调节器",分为voltage regulator(电压调节器)和current(电流调节器).一般电源 ...

  5. C++框架_之Qt的开始部分_概述_安装_创建项目_快捷键等一系列注意细节

    C++框架_之Qt的开始部分_概述_安装_创建项目_快捷键等一系列注意细节 1.Qt概述 1.1 什么是Qt Qt是一个跨平台的C++图形用户界面应用程序框架.它为应用程序开发者提供建立艺术级图形界面 ...

  6. 【原创】Linux cpufreq framework

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  7. Robot Framework自动化_环境搭建以及第一个用例

    Robot Framework自动化_环境搭建以及第一个脚本 培训老师:肖能尤 2016/06/07 课程目的 一.Robot framework 环境搭建以及第一个脚本 课程内容 1    安装前准 ...

  8. Linux PWM framework简介和API描述【转】

    本文转载自:https://blog.csdn.net/mike8825/article/details/51656400 1. 前言 PWM是Pulse Width Modulation(脉冲宽度调 ...

  9. Linux下c语言环境概述

    Linux下C语言环境概述 主要涉及编辑器.编译链接器.调试器.项目管理工具 编辑器 Linux中常用的编辑器有vi和emacs 查看vim配置文件并编辑 编译链接器 在Linux中,最常用的编译器是 ...

  10. 深入Linux内核架构——简介与概述

    一.内核的任务 纯技术层面上,内核是硬件与软件的之间的一个中间层.作用是将应用程序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址. 从应用程序视角上看,内核可以被认为是一台增强 ...

随机推荐

  1. web3 产品介绍:硬件钱包Ledger 离线管理私钥更安全

    Ledger是一款硬件钱包,可以安全地存储用户的加密资产,并在需要时进行交易.作为一种离线存储设备,Ledger钱包比在线钱包更加安全,因为它能够保护用户的私钥和交易信息,使其免受黑客攻击和网络病毒的 ...

  2. Label Studio数据标注--通过源码安装

    Label Studio是Heartex公司开发的一款在线数据标注工具,分为社区版(开源)和企业版(云服务,收费),企业版提供了增强的安全性(单点登录.角色基于访问控制.SOC2).团队管理.分析和报 ...

  3. 第九讲: MySQL为什么有时候会选错索引?

    第九讲: MySQL为什么有时候会选错索引? ​ 前面我们介绍过索引,你已经知道了在 MySQL 中一张表其实是可以支持多个索引的. ​ 但是,你写 SQL 语句的时候,并没有主动指定使用哪个索引.也 ...

  4. 【Java】Spring注入静态Bean的几种写法

    单例模式在Spring注解上的一种拓展用法 写法一,先配置自身Bean,作为静态成员,然后目标Bean作为自身Bean的实例成员' Spring初始化自身Bean时自动装配数据源Bean,从而附属到静 ...

  5. Blazor Web 应用如何实现Auto模式

    本文介绍Blazor Web应用Auto交互呈现模式的实现方案,如下示例是基于 Known 框架来实现的,该解决方案共有3个项目,具体实现步骤如下: 1. 前后端共用项目 创建前后端共用类库项目Sam ...

  6. 【转载】 arXiv论文提交流程

    原文地址: https://blog.csdn.net/u010705932/article/details/105834469 =================================== ...

  7. 国产CPU——兆芯(先开)KX-6640MA 使用感受

    上半年买了个兆芯CPU的小mini电脑,一直没有换Windows系统,这两天想着就换了过来,具体配置如下: 1.  使用Python死循环代码烧机,性能和我14年买的i5-4200M的Intel CP ...

  8. 支付宝支付java版实战(含视频讲解)

    1.背景 实际开发中用到支付宝支付的概念非常大...... 这里重点分析一下支付宝支付实际生产必须要实现的功能 1.获取支付链接(统一下单) 2.支付回调(异步通知) 3.统一下单交易查询 4.退款 ...

  9. 清除 Nuxt 状态缓存:clearNuxtState

    title: 清除 Nuxt 状态缓存:clearNuxtState date: 2024/8/7 updated: 2024/8/7 author: cmdragon excerpt: 摘要:本文介 ...

  10. git push --recurse-submodules = on-demand 递归push

    I have the following project structure: root-project | |-- A | | | |-- C | |-- B A和B是根项目的子模块. C又是项目A ...