4.3.1 wakelock和earlysuspend

wakelock是Android提供的一种特殊的机制,用于请求CPU资源。当持有wakelock之后,可以阻止系统进入暂停或其他低功耗状态。

wakelock和earlysuspend的头文件在include/linux/目录中,实现的内容在kernel/power/目录中,其Makefile组织如下所示:

    obj-$(CONFIG_FREEZER)       += process.o
    obj-$(CONFIG_WAKELOCK)      += wakelock.o
    obj-$(CONFIG_USER_WAKELOCK)     += userwakelock.o
    obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
    obj-$(CONFIG_CONSOLE_EARLYSUSPEND)  += consoleearlysuspend.o
    obj-$(CONFIG_FB_EARLYSUSPEND)   += fbearlysuspend.o

wakelock.c是wakelock的核心实现。userwakelock.c是在用户空间使用wakelock的接口实现。earlysuspend.c是earlysuspend机制的核心实现。consoleearlysuspend.c和fbearlysuspend.c是console和framebuffer在处理earlysuspend时的具体实现。

wakelock的接口定义的常量在wakelock.h中,内容如下所示:

    enum {
        WAKE_LOCK_SUSPEND,                /* 阻止挂起 */
        WAKE_LOCK_IDLE,                   /* 阻止低能耗的Idel */
        WAKE_LOCK_TYPE_COUNT
    };

该枚举定义了wakelock的种类。WAKE_LOCK_SUSPEND用于阻止系统进入suspend状态(CPU挂起),一般是深度休眠,Android提供了通用实现。WAKE_LOCK_IDLE用于阻止系统进入idle状态(CPU低功耗运转),一般在需要比较多的计算资源时候,申请此wakelock,各家芯片商对该wakelock的底层实现可以不一样。

wakelock的接口如下所示:

    void wake_lock_init(struct wake_lock *lock, int type, const char *name);
    void wake_lock_destroy(struct wake_lock *lock);
    void wake_lock(struct wake_lock *lock);
    void wake_lock_timeout(struct wake_lock *lock, long timeout);
    void wake_unlock(struct wake_lock *lock);

wake_lock_init对应wake_lock_destroy。用于创建和销毁一个wakelock。wake_lock和wake_unlock对应,用于持有和释放一个wakelock。wake_unlock持有一个带超时机制的wake_lock,会在超时后自动unlock。

以上接口主要用于内核空间驱动程序等使用。很多驱动程序使用wakelock来阻止CPU进入休眠,以完成关键传输。

在用户空间中,可以通过userwakelock.c中基于此实现的接口来调用。wakelock在用户空间的接口为Sys文件系统的/sys/power/wake_lock和/sys/power/wake_unlock。通过对其的写入,读出操作,完成持有,释放等操作。

Android系统电源管理功能,通过这里的接口,把wakelock暴露给用户空间的应用程序。

wakelock机制与earlysuspend(早期挂起)结合,形成Android独有的电源管理机制。后者通过在linux标准的suspend前,resume后这两个环节分别插入earlysuspend,lateresume来实现。

earlysuspend其实是一个完全挂起前中间状态。简单的来说,该状态下,屏幕和背光关闭,不响应诸如传感器和触摸屏的事件。该状态对于移动设备来说是非常常见的,比如关闭屏幕播放音乐等。而标准Linux中并没有该状态,因此Android进行了扩充。

扩充earlysuspend以后,原有的挂起操作(例如:在用户空间向/sys/power/state中写入“mem”的操作),并不直接将kernel挂起。而是进入earlysuspend,直到最后一个wakelock被释放,再进入原kernel的挂起流程。

例如,应用上述机制,音乐播放的模式就比较容易实现。只需要一直持有wakelock,即可停留在earlysuspend模式,屏幕背光可以被关闭实现省电,而CPU被占有继续进行播放工作。

earlysuspend主要提供了如下的接口:

    struct early_suspend {
    #ifdef CONFIG_HAS_EARLYSUSPEND
        struct list_head link;
        int level;
        void (*suspend)(struct early_suspend *h);
        void (*resume)(struct early_suspend *h);
    #endif
    };
    #ifdef CONFIG_HAS_EARLYSUSPEND
    void register_early_suspend(struct early_suspend *handler);
    void unregister_early_suspend(struct early_suspend *handler);
    #else
    #define register_early_suspend(handler) do { } while (0)
    #define unregister_early_suspend(handler) do { } while (0)
    #endif

以上函数用于各驱动程序实现并注册各自的发生early_suspend和late_resume时候的处理功能。

consoleearlysuspend和fbearlysuspend实现了console和framebuffer处理early_suspend和late_resume时的处理函数。并暴露了用户空间接口,以和Android系统框架进行交互,判断是否需要继续绘制屏幕。

提示:在编写传感器、触摸屏、背光等设备驱动程序时,为了能源管理的处理,可以先注册相应的early_suspend和late_resume处理函数。

从实现上来看,Android的wakelock和earlysuspend机制,在一定程度上改变了Linux原有的电源管理机制。同时,将强制占有CPU而不允许挂起的能力赋予应用程序,有可能对省电管理问题造成混乱。