ios 如何查看cpu使用率 苹果手机怎么看cpu使用率 您所在的位置:网站首页 iphone怎么看cpu使用情况 ios 如何查看cpu使用率 苹果手机怎么看cpu使用率

ios 如何查看cpu使用率 苹果手机怎么看cpu使用率

2024-07-04 10:17| 来源: 网络整理| 查看: 265

在 iOS/Mac 上开发 App,当我们需要性能监控能力的时候,往往需要 CPU 信息来辅助追查:比如当前时刻是否 CPU 高占导致 App 卡到掉渣之类。

一、iOS 的 CPU 占用率实现

iOS 由于系统的限制,在不越狱的情况下无法获知整个系统的 CPU 信息,只能拿到自己 App 的所有线程信息,然后把 CPU 时间全部加起来得到一个大概的数值以供参考。代码太长我们只看核心部分:

// 取当前进程基础信息,其实不取也没有关系 kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count); // 取当前进程的所有线程 kr = task_threads(mach_task_self(), &thread_list, &thread_count); // 遍历所有线程,取一波 CPU 时间 for (j = 0; j < thread_count; j++) { // 取一下线程信息 thread_info_count = THREAD_INFO_MAX; kr = thread_info(thread_list[j], THREAD_BASIC_INFO, (thread_info_t) thinfo, &thread_info_count); basic_info_th = (thread_basic_info_t) thinfo; // 计算一下时间和 CPU Usage,需要除以一个 TH_USAGE_SCALE 的 scale factor if (!(basic_info_th->flags & TH_FLAGS_IDLE)) { tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds; tot_usec = tot_usec + basic_info_th->system_time.microseconds + basic_info_th->system_time.microseconds; tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float) TH_USAGE_SCALE * 100.0; } } // 最后释放一下 kr = vm_deallocate(mach_task_self(), (vm_offset_t) thread_list, thread_count * sizeof(thread_t));

或者滴滴开源的 DoraemonKit 的实现,跟上面的实现基本是一样的,只是省略了task_info()和user_time, system_time的计算。

留意到我们需要把 cpu_usage 取得的值除以 TH_USAGE_SCALE 后才能获得一个准确的值。为啥?这个东西用来干啥子的?

1.1 TH_USAGE_SCALE 是什么

我们直接看看 darwin-xnu 对 thread_info() 的实现。这个函数只是简单地加了个锁,真正的实现在 thread_info_internal()。位置在 osfmk/kern/thread.c。

如果参数为 THREAD_BASIC_INFO 则走 retrieve_thread_basic_info()。这个函数先取了一波系统 timer 的数据给 user_time 和 system_time,然后就是重头戏了:

#define TH_USAGE_SCALE 1000 /* * To calculate cpu_usage, first correct for timer rate, * then for 5/8 ageing. The correction factor [3/5] is * (1/(5/8) - 1). */ basic_info->cpu_usage = 0; #if defined(CONFIG_SCHED_TIMESHARE_CORE) if (sched_tick_interval) { basic_info->cpu_usage = (integer_t)(((uint64_t)thread->cpu_usage * TH_USAGE_SCALE) / sched_tick_interval); basic_info->cpu_usage = (basic_info->cpu_usage * 3) / 5; } #endif if (basic_info->cpu_usage > TH_USAGE_SCALE) basic_info->cpu_usage = TH_USAGE_SCALE;

CONFIG_SCHED_TIMESHARE_CORE 这个宏应该是分时调度线程的意思,sched_tick_interval 则是定义在 osfmk/kern/sched.h 的一个全局变量。在分时调度逻辑初始化的时候,这个值被赋值:

// void sched_timeshare_timebase_init(void) /* scheduler tick interval */ // #define USEC_PER_SEC 1000000ull /* microseconds per second */ // #define SCHED_TICK_SHIFT 3 clock_interval_to_absolutetime_interval(USEC_PER_SEC >> SCHED_TICK_SHIFT, NSEC_PER_USEC, &abstime); assert((abstime >> 32) == 0 && (uint32_t)abstime != 0); sched_tick_interval = (uint32_t)abstime;

这个值就是分时调度时(Time)每次 tick 的时间间隔,关于 FreeBSD 的分时模型(Time-sharing) 

void clock_interval_to_absolutetime_interval(uint32_t interval, uint32_t scale_factor, uint64_t * result) { uint64_t nanosecs = (uint64_t) interval * scale_factor; uint64_t t64; *result = (t64 = nanosecs / NSEC_PER_SEC) * rtclock_sec_divisor; nanosecs -= (t64 * NSEC_PER_SEC); *result += (nanosecs * rtclock_sec_divisor) / NSEC_PER_SEC; }

NSEC_PER_SEC 是每一秒中有多少的纳秒(参考这里)。nanosecs / NSEC_PER_SEC 就得到秒了。

rtclock_sec_divisor 比较有意思。首先是 RTC,Real-time clock,中文翻译为实时时钟,是一个小小的时钟芯片,一般装在主板上,使用 CMOS 电池。读者朋友如果有装过 PC 的话应该会在主板上看到一个纽扣电池的卡槽,这个东西可以给 RTC 模块供电。

rtclock_sec_divisor 这个数值来自于以下函数:

static void timebase_callback(struct timebase_freq_t * freq)

其中 freq 这个参数不同的平台有不同的实现。在时钟模块初始化的时候,内核会注册一个回调 PE_register_timebase_callback(timebase_callback); arm 架构的是是持有这个 callback 然后从硬件读取到相关信息后通过 callback 函数传回去:

void PE_call_timebase_callback(void) { struct timebase_freq_t timebase_freq; timebase_freq.timebase_num = gPEClockFrequencyInfo.timebase_frequency_hz; timebase_freq.timebase_den = 1; if (gTimebaseCallback) gTimebaseCallback(&timebase_freq); }

timebase_freq_t 结构体的定义如下:

struct timebase_freq_t { unsigned long timebase_num; // numerator 分子 unsigned long timebase_den; // denominator 分母 };

这种表示时间的方法叫做 Time Base,中文翻译为“时基”(注意这里所谓的时基和示波器的稍有不同,这里主要用作一个计时单位)。上面说到整个计算机的时序系统是建立在 RTC 模块上的,这个东西最重要的核心是一个时钟振荡器。目前多采用频率为 32.768 kHz (2^15) 的石英晶体制作。

在 arm 架构(iPhone)的实现中,timebase_freq 的分母被 hardcode 为 1。

i386(Mac)则取了总线频率做了如下运算:

void PE_call_timebase_callback(void) { struct timebase_freq_t timebase_freq; unsigned long num, den, cnt; num = gPEClockFrequencyInfo.bus_clock_rate_num * gPEClockFrequencyInfo.bus_to_dec_rate_num; den = gPEClockFrequencyInfo.bus_clock_rate_den * gPEClockFrequencyInfo.bus_to_dec_rate_den; cnt = 2; while (cnt


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有