苹果在iOS13的时候,在内核中退出了一个新的性能掂量指标wakeup,同时因为这个指标而被零碎杀死的利用不可胜数,其中也包含咱们罕用的微信淘宝等。而这个指标齐全是由 XNU 内核统计的,所以咱们很难通过日志等一般伎俩去精确的定位问题,所以这里通过另一种思路去解决这个问题。

为什么要统计 wakeup

要定位这个问题,首先咱们须要晓得这个指标的目标是什么。

XNU 中,对性能的指标有CPU、内存、IO,而wakeup属于 CPU 的性能指标,同时属于 CPU 指标的还有 CPU 使用率,上面是XNU中对其限度的定义。

/** Default parameters for CPU usage monitor.** Default setting is 50% over 3 minutes.*/
#define         DEFAULT_CPUMON_PERCENTAGE 50
#define         DEFAULT_CPUMON_INTERVAL   (3 * 60)
#define TASK_WAKEUPS_MONITOR_DEFAULT_LIMIT              150 /* wakeups per second */
#define TASK_WAKEUPS_MONITOR_DEFAULT_INTERVAL   300 /* in seconds. *//** Level (in terms of percentage of the limit) at which the wakeups monitor triggers telemetry.** (ie when the task's wakeups rate exceeds 70% of the limit, start taking user*  stacktraces, aka micro-stackshots)*/
#define TASK_WAKEUPS_MONITOR_DEFAULT_USTACKSHOTS_TRIGGER        70

总结来说,当 CPU 使用率在3分钟内均值超过50%,就认为适度应用CPU,当wakeup在300秒内均值超过150次,则认为唤起次数过多,同时在阈值的70%水位内核会开启监控。

CPU 使用率咱们很容易了解,使用率越高,电池寿命越低,而且并不是线性减少的。那么wakeup又是如何影响电池寿命的呢?

首先咱们须要看看ARM架构中对于 CPU 功耗问题的形容:

Many ARM systems are mobile devices and powered by batteries. In such systems, optimization of power use, and total energy use, is a key design constraint. Programmers often spend significant amounts of time trying to save battery life in such systems.
因为ARM被大量应用于低功耗设施,而这些设施往往会由电池来作为驱动,所以 ARM 在硬件层面就对功耗这个问题进行了优化设计。

Energy use can be divided into two components:

  • Static
    Static power consumption, also often called leakage, occurs whenever the core logic or RAM blocks have power applied to them. In general terms, the leakage currents are proportional to the total silicon area, meaning that the bigger the chip, the higher the leakage. The proportion of power consumption from leakage gets significantly higher as you move to smaller fabrication geometries.

  • Dynamic
    Dynamic power consumption occurs because of transistor switching and is a function of the core clock speed and the numbers of transistors that change state per cycle. Clearly, higher clock speeds and more complex cores consume more power.
    功耗能够分为2种类型,即动态功耗与动静功耗。

动态功耗指的是只有 CPU 通上电,因为芯片无奈保障相对绝缘,所以会存在“漏电”的状况,而且越大的芯片这种问题越重大,这也是芯片厂家为什么拼命的钻研更小尺寸芯片的起因。这部分功耗因为是硬件自身决定的,所以咱们无奈去管制,而这种类型功耗占比不大。

动静功耗指的是 CPU 运行期间,接通时钟后,执行指令所带来的额定开销,而这个开销会和时钟周期频率相干,频率越高,耗电量越大。这也就阐明了苹果为什么会管制 CPU 使用率,而相干钻研(Facebook 也做过)也表明,CPU 在20以下和20以上的能耗简直是成倍的减少。

CPU 使用率曾经可能从肯定水平上限度电池损耗问题了,那么wakeup又是什么指标呢?

wakeup 是什么
要理解wakeup是什么,首先要晓得ARM低功耗模式的2个重要指令WFI和WFE。

ARM assembly language includes instructions that can be used to place the core in a low-power state. The architecture defines these instructions as hints, meaning that the core is not required to take any specific action when it executes them. In the Cortex-A processor family, however, these instructions are implemented in a way that shuts down the clock to almost all parts of the core. This means that the power consumption of the core is significantly reduced so that only static leakage currents are drawn, and there is no dynamic power consumption.

通过这2个指令进入低功耗模式后,时钟将会被敞开,这个 CPU 将不会再执行任何指令,这样这个 CPU 的动静能耗就没有了。这个能力的实现是由和 CPU 外围强绑定的空转线程idle thread实现的,有意思的是XNU中的实现较为简单,而Zircon中则十分间接暴力:

__NO_RETURN int arch_idle_thread_routine(void*) {for (;;) {__asm__ volatile(“wfi”);}
}

在 XNU 中,一个 CPU 外围的工作流程被概括为如下状态机:

/*

  •       -------------------- SHUTDOWN
    
  •      /                     ^     ^
    
  •    _/                      |      \
    
  • OFF_LINE —> START —> RUNNING —> IDLE —> DISPATCHING
  •     \_________________^   ^ ^______/           /
    
  •                            \__________________/
    

*/
而wakeup则示意的是,从低功耗模式唤起进入运行模式的次数。

wakeup 如何统计的
ARM异样零碎
CPU 时钟被敞开了,那么又要怎么唤起呢?这就波及到 CPU 的异样零碎。

在 ARM 中,异样和中断的概念比拟含糊,他把所有会引起 CPU 执行状态变更的事件都称为异样,其中包含软中断,debug 中断,硬件中断等。

从触发机会上能够辨别为同步异样与异步异样。这里指的同步异步并不是应用程序的概念,这里同步指的是领有明确的触发机会,比方零碎调用,缺页中断等,都会产生在明确的机会,而异步中断,则齐全忽视指令的逻辑,会强行打断指令执行,比方 FIQ 和 IRQ,这里比拟典型的是定时器中断。

异样零碎有很多能力,其中一个重要的能力就是内核态与用户态切换。ARM的执行权限分为4个等级,EL0,EL1,EL2,EL3。其中 EL0 代表用户态,而 EL1 代表内核态,当用户态想要切换至内核态的时候,必须通过异样零碎进行切换,而且异样零碎只能向等同或更高等级权限进行切换。那么这么多类型的异样,又是如何响应的呢?这里就波及到一个异样处理表(exception table),在系统启动的时候,须要首先就去注册这个表,在XNU中,这个表如下:

.section __DATA_CONST,__const.align 3.globl EXT(exc_vectors_table)
LEXT(exc_vectors_table)/* Table of exception handlers.* These handlers sometimes contain deadloops. * It's nice to have symbols for them when debugging. */.quad el1_sp0_synchronous_vector_long.quad el1_sp0_irq_vector_long.quad el1_sp0_fiq_vector_long.quad el1_sp0_serror_vector_long.quad el1_sp1_synchronous_vector_long.quad el1_sp1_irq_vector_long.quad el1_sp1_fiq_vector_long.quad el1_sp1_serror_vector_long.quad el0_synchronous_vector_64_long.quad el0_irq_vector_64_long.quad el0_fiq_vector_64_long.quad el0_serror_vector_64_long

wakeup 计数
那么咱们回过头来看看wakeup计数的中央:

/**  thread_unblock:**  Unblock thread on wake up.*  Returns TRUE if the thread should now be placed on the runqueue.*  Thread must be locked.*  Called at splsched().*/
boolean_t
thread_unblock(thread_t                thread,wait_result_t   wresult)
{// . . .boolean_t aticontext, pidle;ml_get_power_state(&aticontext, &pidle);/* Obtain power-relevant interrupt and “platform-idle exit" statistics.* We also account for “double hop” thread signaling via* the thread callout infrastructure.* DRK: consider removing the callout wakeup counters in the future* they’re present for verification at the moment.*/if (__improbable(aticontext /* . . . */)) {// wakeup ++}// . . .
}

而这里的aticontext则是通过ml_at_interrupt_context获取的,其含意则是是否处于中断上下文中。

/**  Routine:        ml_at_interrupt_context*  Function:   Check if running at interrupt context*/
boolean_t
ml_at_interrupt_context(void)
{/* Do not use a stack-based check here, as the top-level exception handler* is free to use some other stack besides the per-CPU interrupt stack.* Interrupts should always be disabled if we’re at interrupt context.* Check that first, as we may be in a preemptible non-interrupt context, in* which case we could be migrated to a different CPU between obtaining* the per-cpu data pointer and loading cpu_int_state.  We then might end* up checking the interrupt state of a different CPU, resulting in a false* positive.  But if interrupts are disabled, we also know we cannot be* preempted. */return !ml_get_interrupts_enabled() && (getCpuDatap()->cpu_int_state != NULL);
}

那么cpu_int_state标记又是在什么时候设置下来的呢?只有在locore.S中,才会更新该标记:

str x0, [x23, CPU_INT_STATE] // Saved context in cpu_int_state
同时发现如下几个办法会配置这个标记:

el1_sp0_irq_vector_long
el1_sp1_irq_vector_long
el0_irq_vector_64_long
el1_sp0_fiq_vector_long
el0_fiq_vector_64_long

联合上述的异样处理表的注册地位,与ARM官网文档的地位进行比照,能够发现:

[点击可获取资料大全]

这几个中断类型均为 FIQ 或者 IRQ,也就是硬中断。由此咱们能够判断,wakeup必然是由硬中断引起的,而像零碎调用,线程切换,缺页中断这种并不会引起wakeup。

过程统计
由上能够看出,wakeup其实是对CPU外围唤起次数的统计,和应用层的线程与过程仿佛毫不相干。但从程序执行的角度思考,如果一个程序始终在运行,就不会进入期待状态,而从期待状态唤醒,必定是因为某些异常中断,比方网络,vsync 等。

在 CPU 外围被唤醒后,在以后 CPU 外围执行的线程会进行wakeup++,而零碎统计维度是利用维度,也就是过程维度,所以会累计该过程上面的所有线程的wakeup计数。

queue_iterate(&task->threads, thread, thread_t, task_threads) {info->task_timer_wakeups_bin_1 += thread->thread_timer_wakeups_bin_1;info->task_timer_wakeups_bin_2 += thread->thread_timer_wakeups_bin_2;
}

所以在咱们代码中,如果在2个不同线程启用用同样的定时器,wakeup是同一个线程起2个定时器的2倍(同样的定时器在底层其实是一颗树,注册同样的定时器理论只注册了一个)。

用户层获取该统计值则能够通过如下形式:

#include <mach/task.h>
#include <mach/mach.h>BOOL GetSystemWakeup(NSInteger *interrupt_wakeup, NSInteger *timer_wakeup) {struct task_power_info info = {0};mach_msg_type_number_t count = TASK_POWER_INFO_COUNT;kern_return_t ret = task_info(current_task(), TASK_POWER_INFO, (task_info_t)&info, &count);if (ret == KERN_SUCCESS) {if (interrupt_wakeup) {*interrupt_wakeup = info.task_interrupt_wakeups;}if (timer_wakeup) {*timer_wakeup = info.task_timer_wakeups_bin_1 + info.task_timer_wakeups_bin_2;}return true;}else {if (interrupt_wakeup) {*interrupt_wakeup = 0;}if (timer_wakeup) {*timer_wakeup = 0;}return false;}
}

wakeup 治理
从以上剖析来看,咱们只须要排查各种硬件相干事件即可。

从理论排查后果来看,目前只有定时器或者领有定时能力的类型是最广泛的场景。

比方NSTimer,CADisplayLink,dispatch_semaphore_wait,pthread_cond_timedwait等。

对于定时器,咱们尽量复用其能力,防止在不同线程去创立同样的定时能力,同时在回到后盾的时候,敞开不须要的定时器,因为大部分定时器都是UI相干的,敞开定时器也是一种规范的做法。

对于 wait 类型的能力,从计划抉择上防止轮询的计划,或者减少轮询间隔时间,比方能够通过 try_wait,runloop或者 EventKit 等能力进行优化。

ps;iOS开发交流技术:[欢迎你的加入],不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长

监控与防劣化
一旦咱们晓得了问题起因,那么对问题的治理比较简单,而后续咱们须要建设继续的管控等长效措施才能够。

在此咱们能够简略的定义一些规定,并且嵌入线下监控能力中:

定时器工夫周期小于1s的,在进入后盾须要进行暂停
wait 类型提早小于1s,并且继续应用10次以上的状况须要进行优化
总结
wakeup因为是 XNU 内核统计数据,所以在问题定位排查方面特地艰难,所以从另一个角度去解决这个问题反而是一种更好的形式。

同时从 XNU 中对 CPU 功耗的管制粒度能够看出,苹果在极致的优化方面做的很好,在本身的软件生态中要求也比拟高。电量问题在短时间内应该不会有技术上的冲破,所以咱们本身也须要多思考如何缩小电池损耗。

iOS系列—wakeup in XNU相关推荐

  1. 【iOS系列】-程序开启后台运行

    [iOS系列]-程序开启后台运行 iOS程序是伪后台的运行,可是有时候我们需要让其在后台也要进行一些操作,我们可以让其伪装成音乐的APP,这样就可以让程序后台进行相关操作了,具体做法如下: 1:在Ap ...

  2. iOS系列教程 目录 (持续更新...)

      前言: 听说搞iOS的都是高富帅,身边妹子无数.咱也来玩玩.哈哈. 本篇所有内容使用的是XCode工具.Swift语言进行开发. 我现在也是学习阶段,每一篇内容都是经过自己实际编写完一遍之后,发现 ...

  3. 【iOS系列】-xib封装使用

    [iOS系列]-xib封装使用 Xib文件可以用来描述某一块局部的UI界面 Xib文件的加载 修改xib文件的大小size(Freeform) 第一: NSArray *objs = [[NSBund ...

  4. iOS系列开发-版本控制工具Git的使用

    iOS系列开发-版本控制工具Git的使用 作为一个开发者,与团队之间默契的配合是很重要的,我们所写的代码在无论是在公司还是在个人来说都是一份不可随意丢弃的东西,但是如果只是单纯的开发,我们很难做到今天 ...

  5. 网易云音乐大前端团队:Wakeup in XNU

    苹果在 iOS 13 的时候,在内核中加入了一个新的性能衡量指标wakeup,同时由于这个指标而被系统杀死的应用数不胜数,其中也包括我们常用的微信淘宝等.而这个指标完全是由 XNU 内核统计的,所以我 ...

  6. 【iOS系列】-iOS中内存管理

    iOS中创建对象的步骤: 1,分配内存空间,存储对象 2,初始化成员变量 3,返回对象的指针地址 第一:非ARC机制: 1,对象在创建完成的同时,内部会自动创建一个引用计数器,是系统用来判断是否回收对 ...

  7. iOS系列教程之常见开发Tips(转自阿峰的博客)

    开发技巧和常见错误汇总,不定期更新中,也欢迎大家总结跟帖 1:ARC下NSNotificationCenter需要remove - (void) dealloc { // [super dealloc ...

  8. 格物致知iOS系列之类与对象

    欲诚其意者,先致其知:致知在格物.物格而后知至,知至而后意诚.现代汉语词典中将格物致知解释为: "推究事物的原理,从而获得知识". 在编程中我们接触最多的也是最基本的就是类和对象, ...

  9. 【MAC/IOS系列】之苹果电脑 MacOS 安装根证书和开发流程

    DATE: 2019-3-1 1.参考 https://www.cnblogs.com/chuanwei-zhang/p/8058254.html https://blog.51cto.com/vim ...

  10. 【iOS系列】聊聊 -ObjC 的故事

    "民之失德,乾糇以愆:他山之石,可以攻玉."   - <诗经> 在开发 iOS 应用时,可能遇见这样的情况:你想实现的某种功能(比如崩溃收集),已经有成熟的产品提供,可 ...

最新文章

  1. c语言结构体反射解析,C语言结构体解析
  2. 【运筹学】匈牙利法 ( 匈牙利法示例 2 | 第一步 : 变换系数矩阵 | 第二步 : 试指派 | 行列打√ | 直线覆盖 | 第二轮试指派 )
  3. 在html中选项卡怎么做,纯css3制作选项卡
  4. Mac终端建立替身 并置于桌面或Finder中
  5. WPF 获取屏幕分辨率(获取最大宽高)等
  6. [机械]“重工业面临两大危机”——向文波(三一重工股份有限公司执行总裁)
  7. py文件转exe时包含paramiko模块出错解决方法
  8. Arthas 思维导图笔记
  9. mysql数据库优化语句_【MySQL】10条SQL优化语句,让你的MySQL数据库跑得更快!
  10. 【Git入门之十四】Git GUI
  11. python编写脚本方法_python写dnf脚本录制自己点击
  12. 读书笔记 | 国富论(卷一)
  13. cmd“不是内部或外部命令,也不是可运行的程序或批处理文件”
  14. nltk文件下载以及word_tokenize运行问题解决
  15. 使用 requireJS 的shim参数 解决插件jequery 插件问题
  16. git加速 用谷歌浏览器插件
  17. 计算机辅助设计中图元单位,CAD图形单位快捷键
  18. 亚洲销售女神徐鹤宁经典语录——太过精辟!
  19. opencv自己生成标定板
  20. 微型计算机原理...,微型计算机原理

热门文章

  1. Enterprise Architect安装使用
  2. 网络受限是个什么东东?
  3. Unity场景优化工具:Mesh Baker 基础教程(贴图篇)
  4. 【C语言进阶】预定义详解
  5. 纯css实现三角原理,兼容IE
  6. 基于ThinkPHP的图书馆管理系统 毕业设计-附源码311833
  7. UOJ #449.【集训队作业2018】喂鸽子 min-max容斥
  8. 401. 二进制手表
  9. Android天天飞车游戏辅助系统
  10. 家庭单台计算机连接宽带步骤,两台win7电脑共用一个宽带账号上网的设置方法...