标准linux休眠和唤醒机制分析说明:1. Based on linux2.6.32, only for mem(SDR)2. 有兴趣请先参考阅读:电源管理方案APM和ACPI比较.docLinux系统的休眠与唤醒简介.doc3. 本文先研究标准linux的休眠与唤醒,android对这部分的增改在另一篇文章中讨论4. 基于手上的一个项目来讨论,这里只讨论共性的地方虽然linux支持三种省电模式:standby、suspend to ram、suspend to disk,但是在使用电池供电的手持设备上,几乎所有的方案都只支持STR模式(也有同时支持standby模式的),因为STD模式需要有交换分区的支持,但是像手机类的嵌入式设备,他们普遍使用nand 来存储数据和代码,而且其上使用的文件系统yaffs一般都没有划分交换分区,所以手机类设备上的linux都没有支持STD省电模式。
一、项目power相关的配置目前我手上的项目的linux电源管理方案配置如下,.config文件的截图,当然也可以通过make menuconfig使用图形化来配置:## CPU Power Management## CONFIG_CPU_IDLE is not set## Power management options#CONFIG_PM=y# CONFIG_PM_DEBUG is not setCONFIG_PM_SLEEP=yCONFIG_SUSPEND=yCONFIG_SUSPEND_FREEZER=yCONFIG_HAS_WAKELOCK=yCONFIG_HAS_EARLYSUSPEND=yCONFIG_WAKELOCK=yCONFIG_WAKELOCK_STAT=yCONFIG_USER_WAKELOCK=yCONFIG_EARLYSUSPEND=y# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set# CONFIG_CONSOLE_EARLYSUSPEND is not setCONFIG_FB_EARLYSUSPEND=y# CONFIG_APM_EMULATION is not set# CONFIG_PM_RUNTIME is not setCONFIG_ARCH_SUSPEND_POSSIBLE=yCONFIG_NET=y上面的配置对应下图中的下半部分图形化配置。
,看来是直接在Kconfig文件中删除了配置STD模式的选项。
使用上面的配置编译出来的系统,跑起来之后,进入sys目录可以看到相关的接口:# pwd/sys/power# lsstate wake_lock wake_unlock wait_for_fb_sleep wait_for_fb_wake# cat statemem如果配置了宏CONFIG_PM_DEBUG,那么在power目录下会多出一个pm_test文件,cat pm_test后,列出的测试选项有:[none] core processors platform devices freezer。
关于这个test模式的使用,可以参考kernel文档:/kernel/documentation/power/Basic-pm-debugging.txt这个文档我也有详细的阅读和分析。
二、sys/power和相关属性文件创建系统bootup时候在sys下新建power和相关属性文件,相关源码位置:kernel/kernel/power/main.cstatic int __init pm_init(void){int error = pm_start_workqueue();// CONFIG_PM_RUNTIME not set, so this fun is nullif (error)return error;power_kobj = kobject_create_and_add("power", NULL);// 建立power对应的kobject和sysfs_dirent对象,同时建立联系:kobject.sd =// &sysfs_dirent,sysfs_dirent.s_dir->kobj = &kobject。
if (!power_kobj)return -ENOMEM;return sysfs_create_group(power_kobj, &attr_group);// 建立一组属性文件,可以在power下建立一个子目录来存放这些属性文件,// 不过需要在结构体attr_group中指定name,否则直接将这些属性文件放在// power_kobj对应的目录下。
}core_initcall(pm_init); // 看的出来,该函数是很早就被调用,initcall等级为1static struct attribute_group attr_group = {.attrs = g,};struct attribute_group {const char *name;mode_t (*is_visible)(struct kobject *,struct attribute *, int);struct attribute **attrs;};// 属性文件都是以最基本得属性结构struct attribute来建立的static struct attribute * g[] = {&state_attr.attr,#ifdef CONFIG_PM_TRACE // not set&pm_trace_attr.attr,#endif#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG) // not set&pm_test_attr.attr,#endif#ifdef CONFIG_USER_WAKELOCK // set &wake_lock_attr.attr,&wake_unlock_attr.attr,#endifNULL,};#ifdef CONFIG_PM_SLEEP#ifdef CONFIG_PM_DEBUGpower_attr(pm_test);#endif#endifpower_attr(state);#ifdef CONFIG_PM_TRACEpower_attr(pm_trace);#endif#ifdef CONFIG_USER_WAKELOCK power_attr(wake_lock);power_attr(wake_unlock);#endif#define power_attr(_name) \static struct kobj_attribute _name##_attr = { \.attr = { \.name = __stringify(_name), \.mode = 0644, \}, \.show = _name##_show, \.store = _name##_store, \}// 而这些被封装过的属性结构体,将来会使用kobject的ktype.sysfs_ops->show(store)这两个通用函数通过container_of()宏找到实际的属性结构体中的show和store函数来调用。
关于更多sysfs的内容,请查看其他关于这部分内容的详细解析文档。
三、pm_test属性文件读写int pm_test_level = TEST_NONE;static const char * const pm_tests[__TEST_AFTER_LAST] = {[TEST_NONE] = "none",[TEST_CORE] = "core",[TEST_CPUS] = "processors",[TEST_PLATFORM] = "platform",[TEST_DEVICES] = "devices",[TEST_FREEZER] = "freezer",// core >> processors >> platform >> devices >> freezer,控制范围示意cat pm_test的时候最终会调用函数pm_test_show(),在终端上打印出上面数组中的字符串,当前的模式用[]表示出来。
echo devices > pm_test的时候会最终调用到函数pm_test_store()中去,该函数中设置全局变量pm_test_level的值,可以是0-5,分别代表上none ~ freezer。
该全局变量会在后面的suspend和resume中被引用到。
memchr函数说明:原型:extern void *memchr(void *buf, char ch, unsigned int count);用法:#include <string.h>功能:从buf所指内存区域的前count个字节查找字符ch。
说明:当第一次遇到字符ch时停止查找。
如果成功,返回指向字符ch的指针;否则返回NULL。
四、state属性文件power_attr(state)宏定义了一个struct kobj_attribute结构体state_attr:static struct kobj_attribute state_attr = {.attr = {.name = __stringify(state),.mode = 0644,},.show = state_show,.store = state_store,kobj_attribute结构体封装了struct attribute结构体,新建属性文件是依据struct attribute结构体。
最终通过函数kobj_attr_show和kobj_attr_store回调到实际的show和store函数(kobject.c)。
state_show()函数主要是显示当前系统支持哪几种省电模式。
static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){char *s = buf;#ifdef CONFIG_SUSPEND //defint i;for (i = 0; i < PM_SUSPEND_MAX; i++) {if (pm_states[i] && valid_state(i))s += sprintf(s,"%s ", pm_states[i]);}#endif#ifdef CONFIG_HIBERNATION // undef, don't support STD modes += sprintf(s, "%s\n", "disk");#elseif (s != buf)/* convert the last space to a newline */*(s-1) = '\n';#endifreturn (s - buf);}@ kernel/include/linux/suspend.h#define PM_SUSPEND_ON ((__force suspend_state_t) 0)#define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1)#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)#define PM_SUSPEND_DISK ((__force suspend_state_t) 4)#define PM_SUSPEND_MAX ((__force suspend_state_t) 5)@ kernel/kernel/power/suspend.cconst char *const pm_states[PM_SUSPEND_MAX] = {#ifdef CONFIG_EARLYSUSPEND // android修改了标准linux的休眠唤醒机制,增加了eraly suspend和late resume机制,如果是android内核,则这个宏是需要定义的。