内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

5.硬件监控

2018-06-08 19:45 出处:清屏网 人气: 评论(0

不知道大家有没有遇到过这样的情况:我们的应用跑着跑着突然就卡了,没过一会我们的手机就变的好烫,简直可以煎鸡蛋了;应用出现一些很奇怪的现象,经过艰苦卓绝的Debug原来是因为内存爆了,好多ViewController收到了内存警告。要是我们能对我们的硬件做监控就好了,我们能实时的知道我们的iPhone系统的CPU使用率,内存的使用率,要是能知道我们自己的app使用了多少CPU,多少内存就更好了。

不过Apple的Cocoa Touch框架好像并没有提供什么面向硬件的接口给我们,不过不用担心,我们知道我们的iOS操作系统的内核是Darwin,而Darwin是一种Unix-like的操作系统,Darwin的API我们是可以直接调用的。因此本章节的代码会涉及到很多Darwin的API,但是Apple官网上对Darwin API的文档支持不是很好,很多API的文档都是空的,所以本章节中有些涉及到Darwin的API的解释是笔者通过阅读API的注释以及网上查阅和理解所得,如有出入还请指出。

本章节主要讲硬件监控的两个方面,CPU使用率监控和内存使用率监控,当然Darwin的API不仅仅可以做这些,我们还能从它那获取很多东西,比如各个设备的温度,设备的电量,设备的物理信息等。很多东西笔者也尚处于探索学习中,一旦有成熟的进展,我会第一时间更新到我们的 SystemEye 上去,并将功能接入到 GodEye 中,方便大家使用,当然我最后也会将探索的过程和产出的结果写成文章添加到本章节中。

好了,那么让我们从CPU使用率监控和内存使用率监控讲起,阅读完本章节,大家不仅可以知道我们系统的CPU使用率和内存使用率,而且可以知道我们自己的应用到底占了整个设备多少CPU,占了多少内存,是不是很好玩~

5.1 系统CPU使用率监控

虽然我们的标题是CPU使用率监控,但是我们当然不会只能拿到CPU的使用率,我们能拿到CPU的好多信息,使用率只是我们最后换算所得之一。当然,本节我们会讲两个CPU使用率,一个就是我们整个系统的CPU使用率,还有一个就是我们自己App的CPU使用率。

该章节所涉及到的代码大家可以通过GodEye的开源组件库 SystemEye 查看,也可以先将代码下载下来对照着阅读本节的内容。

Darwin接口

我们都知道CPU有好几个核,并且像 Intel Core i7 这种型号的CPU还可以通过超线程技术将物理四核整出八线程,也就是虚拟八核。不同的时刻每个CPU核的使用率也是不一而同的,而我们平时看到的CPU使用率其实是这些CPU核的平均使用率。

因此,如果我们要获取系统的CPU使用率的话我们需要拿到每个CPU核处理器的使用率,然后做一个平均值就好。

首先,我们需要来简单了解下Darwin的 host_processor_info() API:

host_processor_info(host: host_t,
                  flavor: processor_flavor_t,
     out_processor_count: UnsafeMutablePointer<natural_t>!,
      out_processor_info: UnsafeMutablePointer<processor_info_array_t?>!,
   out_processor_infoCnt: UnsafeMutablePointer<mach_msg_type_number_t>!)
                        -> kern_return_t

host_processor_info 需要传入5个参数,然后返回一个是否成功的结果,一般我们需要将返回结果和 KERN_SUCCESS 比较一下就好,我们先来看下第二个参数 processor_flavor_t :

public typealias processor_flavor_t = Int32
public var PROCESSOR_BASIC_INFO: Int32 { get } /* basic information */
public var PROCESSOR_CPU_LOAD_INFO: Int32 { get } /* cpu load information */
public var PROCESSOR_PM_REGS_INFO: Int32 { get } /* performance monitor register info */
public var PROCESSOR_TEMPERATURE: Int32 { get } /* Processor core temperature */

可见 processor_flavor_t 有4种类型, PROCESSOR_BASIC_INFO 是系统的基本信息, PROCESSOR_PM_REGS_INFO 是新能监控注册信息, PROCESSOR_TEMPERATURE 是处理器核心的温度信息,而 PROCESSOR_CPU_LOAD_INFO 就是我们要的加载信息。因此第二个参数我们可以传 PROCESSOR_CPU_LOAD_INFO

接下去我们来看第三个,第四个,第五个参数,他们都是一个可变的指针类型,说明这些穿进去的值都会被 host_processor_info 方法内部修改掉。当我们 flavorPROCESSOR_CPU_LOAD_INFO 的时候, out_processor_count 就是我们的CPU处理器的核数, out_processor_info 就是我们所有CPU处理器核的信息列表, out_processor_infoCnt 就是我们CPU处理器核的信息列表的数目。

可能你会好奇, out_processor_info 是我们所有CPU处理器核的信息列表,那有哪些信息呢,为何这里已经有了我们cpu核的数目参数?为何还需要一个信息列表的数目的参数?咱们可以带着问题先把 host_processor_info 的代码写出来,然后跑起来看一下,他们都返回了啥:

//list of cpu processor info
var cpu_processor_info : processor_info_array_t? = nil

//number of cpus
var cpu_processor_count = natural_t()

//count of cpu processor info
var cpu_processor_info_count = mach_msg_type_number_t()

let ret = host_processor_info(
    mach_host_self(),
    PROCESSOR_CPU_LOAD_INFO,
    &cpu_processor_count,
    &cpu_processor_info,
    &cpu_processor_info_count)
if(ret != KERN_SUCCESS) {
    return nil
}

当我们在iPhone5s的真机设备上跑起来的时候我们会发现,我们的CPU核数 cpu_processor_count 是2,CPU信息列表数 cpu_processor_info_count 是8,为啥呢,因为每个CPU核里面当然不可能只有一个总的使用率,每一个CPU有很多信息,当然这里只返回了4个,他们分别是 usersystemidleniceuser 就是用户态使用的CPU时间比, system 就是系统态使用的CPU时间比, idle 是空闲的CPU时间比, nice 是nice加权的进程分配的用户态cpu时间比。也就是说我们的 cpu_processor_info 里的数据差不多就是这样排列的:

[CPU核1-user,CPU核1-system,CPU核1-idle,CPU核1-nice,
 CPU核2-user,CPU核2-system,CPU核2-idle,CPU核2-nice]

由于CPU内部的信息这一块具体的东西不属于本书的内容,所以我们就不展开来讲了,这里简单介绍下什么是时间比,方便大家理解。比如一秒内有100个CPU时间片,这个cpu时间片就是cpu工作的最小单位。那么这100个cpu时间片在不同的区域和目的进行操作使用,就代表这个区域所占用的cpu时间比。也就是这里得出的cpu时间百分比。由此可知我们拿到各个核的的 usersystemidlenice 都是不同状态下的cpu时间百分比。

获取使用率

因此我们可以编写一个 SystemCPULoadModel 的类来存放我们每个CPU核的加载信息:

public class SystemCPULoadModel: NSObject {
    public var system: Int32
    public var user: Int32
    public var nice: Int32
    public var idle: Int32

    override init() {
        self.system = 0
        self.user = 0
        self.nice = 0
        self.idle = 0
    }
}

然后再在我们的CPU类中添加一个私有方法 getLoadList() 将我们通过 host_processor_info() 得到的cpu核处理器信息列表和信息列表的总数传进去,处理成一个[SystemCPULoadModel]的数组:

private enum STATE: Int {
    case user = 0
    case system = 1
    case idle = 2
    case nice = 3
    case max = 4
}

private class func getLoadList(from cpu_processor_info: processor_info_array_t?,
                                   with count:Int) -> [SystemCPULoadModel]? {

        guard let info = cpu_processor_info else {
            return nil
        }


        var loadArr = [SystemCPULoadModel](repeating: SystemCPULoadModel(), count: count)

        for i in 0 ..< count {
            var load = SystemCPULoadModel()
            let offset = STATE.max.rawValue * i
            load.system = info[offset + STATE.system.rawValue]
            load.user = info[offset + STATE.user.rawValue]
            load.nice = info[offset + STATE.nice.rawValue]
            load.idle = info[offset + STATE.idle.rawValue]

            loadArr[i] = load
        }

        return loadArr
    }

然后结合我们最初 host_processor_info 接口的代码编写一个system()接口:

open class func system() -> SystemCPUModel? {

        //list of cpu processor info
        var cpu_processor_info : processor_info_array_t? = nil

        //number of cpus
        var cpu_processor_count = natural_t()

        //count of cpu processor info
        var cpu_processor_info_count = mach_msg_type_number_t()

        let ret = host_processor_info(
            mach_host_self(),
            PROCESSOR_CPU_LOAD_INFO,
            &cpu_processor_count,
            &cpu_processor_info,
            &cpu_processor_info_count)
        if(ret != KERN_SUCCESS) {
            return nil
        }

        let loads = self.getLoadList(from: cpu_processor_info,
                                     with: Int(cpu_processor_count),
                                     and: self.previousLoad)

        let cpuInfoSize = vm_size_t(MemoryLayout<integer_t>.size) * vm_size_t(cpu_processor_count)
        vm_deallocate(mach_task_self_, unsafeBitCast(cpu_processor_info, to: vm_address_t.self), cpuInfoSize)

        return SystemCPUModel(loads: loads)
    }

以及他的返回模型:

open class SystemCPUModel: NSObject {

    open private(set) var loads: [SystemCPULoadModel]

    init?(loads:[SystemCPULoadModel]?) {
        if loads == nil {
            return nil
        }
        self.loads = loads!
    }
}

好了,我们现在能得到一个SystemCPUModel的整体模型,他的内部有一个SystemCPULoadModel的数组,那么我们需要怎么计算CPU整体的使用率,以及各个状态的使用率呢。当然我们可以计算每个Load下的使用率,然后把他们做个SUM求平均即可。我们可以在 SystemCPULoadModel 中添加一个usage方法:

public func usage() -> (system:Double,user:Double,nice:Double) {
    let system = Double(self.system) / Double(self.total())
    let user = Double(self.user) / Double(self.total())
    let nice = Double(self.nice) / Double(self.total())

    return (system,user,nice)
}

private func total() -> Int32 {
    return self.system + self.user + self.nice + self.idle
}

这个方法会返回单一Laod各个状态的使用率。然后在 SystemCPUModel 类中添加一个usage方法去把Load数组求整取平均:

open func usage() -> (system:Double,user:Double,nice:Double,total:Double)  {
    var system: Double = 0
    var user: Double = 0
    var nice: Double = 0
    var total: Double = 0

    self.loads.forEach { (load:SystemCPULoadModel) in
        let usage = load.usage()
        system += usage.system
        user += usage.user
        nice += usage.nice
    }

    system /= Double(loads.count)
    user /= Double(loads.count)
    nice /= Double(loads.count)
    total = system + user + nice

    return (system,user,nice,total)
}

这样当我们调用 CPU.system() 方法拿到我们的 SystemCPUModel 后,直接调用一下usage方法就能拿到CPU的使用率以及各个状态的使用率了。

但是这样得到的CPU使用率并不是最准确的,关键问题还是出在 host_processor_info() 方法里,这个方法返回的各个状态的使用率并不是调用刹那的使用率,而是与前一次调用的间隔时间的平均使用率,如果我们把上一次调用得到的各个状态的时间片去掉再去算使用率就会更精确了。

因此首先我们需要在CPU类中添加一个一个成员变量来记录上一次得到的Load数组:

fileprivate static var previousLoad:[SystemCPULoadModel]?

然后改造我们的 getLoadList 方法,将上一次的Load数组传入并在计算的时候剔除:

//MARK: Private func
private class func getLoadList(from cpu_processor_info: processor_info_array_t?,
                               with count:Int,
                               and previous:[SystemCPULoadModel]?) -> [SystemCPULoadModel]? {

    guard let info = cpu_processor_info else {
        return nil
    }


    var loadArr = [SystemCPULoadModel](repeating: SystemCPULoadModel(), count: count)

    for i in 0 ..< count {
        var load = SystemCPULoadModel()
        let offset = STATE.max.rawValue * i
        load.system = info[offset + STATE.system.rawValue] - (previous?[i].system ?? 0)
        load.user = info[offset + STATE.user.rawValue] - (previous?[i].user ?? 0)
        load.nice = info[offset + STATE.nice.rawValue] - (previous?[i].nice ?? 0)
        load.idle = info[offset + STATE.idle.rawValue] - (previous?[i].idle ?? 0)

        loadArr[i] = load
    }

    return loadArr
}

最后修改我们的 system 方法,每次执行完毕后,将数据记录到我们的 previousLoad 中:

open class func system() -> SystemCPUModel? {

    ....

    let loads = self.getLoadList(from: cpu_processor_info,
                                 with: Int(cpu_processor_count),
                                 and: self.previousLoad)

    ....

    //将数据记录到self.previousLoad中
    self.previousLoad = loads
    return SystemCPUModel(loads: loads)
}

好了,这样我们的监控数据就更准确了。该章节所涉及到的代码大家可以通过AppConsole的开源组件库 SystemEye 查看

5.2 应用CPU使用率监控

完成了前一节,我们已经能获取到我们手机整个操作系统的CPU使用率,但是这些CPU的消耗不一定是我们自己的App造成的。用户在使用的时候极有可能挂着好几个应用在后台来回切换。系统的CPU使用率只是他们的一个总和。因此获取自己应用的CPU使用率对于监控来说更有意义。

应用线程列表

是的,和线程相关,说的简单点,我们只需要遍历应用中每个线程的CPU消耗就能计算出整个应用占用了多少CPU。当然,这还是得用Darwin的接口,这次是谁呢? task_threads

我们来看 task_threads 的定义:

public func task_threads(_ target_task: task_t, _ act_list: UnsafeMutablePointer<thread_act_array_t?>!, _ act_listCnt: UnsafeMutablePointer<mach_msg_type_number_t>!) -> kern_return_t

target_task 我们只需要传 mach_task_self_ 即可,也就是相当于把我们应用的taskid告诉这个接口, act_listact_listCnt 都是可变指针类型,接口调用完毕后,他会让这两个指正分别指向线程信息列表和线程个数。 act_list 里面存放的都是 thread_act_tthread_act_t 里面就存放着我们后续需要分析的数据。

我们可以包装一个 threadActPointers() 方法来获取 thread_act_t 的一个数组:

```swift

private class func threadActPointers() -> [thread_act_t] {

var threads_act =

thread_act_t


分享给小伙伴们:
本文标签: iOS开发

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号