1、标准linux休眠和唤醒机制分析标准linux休眠和唤醒机制分析标准linux休眠和唤醒机制分析(一)说明:1. Based on , only for mem(SDR)2. 有兴趣请先参考阅读: 电源管理方案APM和ACPI比较.docLinux系统的休眠与唤醒简介.doc3. 本文先研究标准linux的休眠与唤醒,android对这部分的增改在另一篇文章中讨论4. 基于手上的一个项目来讨论,这里只讨论共性的地方虽然linux支持三种省电模式:standby、suspend to ram、suspend to disk,但是在使用电池供电的手持设备上,几乎所有的方案都只支持STR模式(也有同
2、时支持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_
3、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 setC
4、ONFIG_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#
5、 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/m
6、ain.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 (
7、!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 attrib
8、ute_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(CONFI
9、G_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_WAK
10、ELOCKpower_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()
11、宏找到实际的属性结构体中的show和store函数来调用。关于更多sysfs的内容,请查看其他关于这部分内容的详细解析文档。标准linux休眠和唤醒机制分析(二)三、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 = f
12、reezer,;/ 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, cha
13、r ch, unsigned int count); 用法:#include 功能:从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, .stor
14、e = 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_
15、SUSPEND /defint i;for (i = 0; i valid & suspend_ops-valid(state);而实际平台的platform_suspend_ops结构体一般都是在文件arch/arm/mach-xxxx/pm.c中进行定义,对于mtk的平台是文件mtkpm.c,如下: kernel/include/linux/suspend.hstruct platform_suspend_ops int (*valid)(suspend_state_t state);int (*begin)(suspend_state_t state);int (*prepare)(vo
16、id);int (*prepare_late)(void);int (*enter)(suspend_state_t state);void (*wake)(void);void (*finish)(void);void (*end)(void);void (*recover)(void);经过后面的代码分析,得出了如下结论:休眠唤醒过程依次会执行的函数是:begin,prepare,prepare_late,enter,wake, finish, end。同颜色的函数执行了恰好相反的工作。休眠的时候代码执行是停留在函数enter中,wake之后也是从suspend的时候停留的地方继续运行。至
17、于recover函数貌似只有在pm_test处于devices的模式下,才会被调用到。 kernel/arch/arm/mach-mt6516/mtkpm.cstatic struct platform_suspend_ops mtk_pm_ops = .valid = mtk_pm_state_valid,.begin = mtk_pm_begin,.prepare = mtk_pm_prepare,.enter = mtk_pm_enter,.finish = mtk_pm_finish,.end = mtk_pm_end,;static int mtk_pm_state_valid(su
18、spend_state_t pm_state)return pm_state = PM_SUSPEND_MEM ;void mtk_pm_init(void)_Chip_PM_init();/* Register and set suspend operation */suspend_set_ops(&mtk_pm_ops); 而函数mtk_pm_init()是在函数mt6516_init_irq()中调用。可以看出该平台只支持mem的省电模式。state_store()函数:static ssize_t state_store(struct kobject *kobj, struct kob
19、j_attribute *attr,const char *buf, size_t n)#ifdef CONFIG_SUSPEND / set#ifdef CONFIG_EARLYSUSPEND /对标准linux而言,这个宏不存在suspend_state_t state = PM_SUSPEND_ON;#elsesuspend_state_t state = PM_SUSPEND_STANDBY;#endifconst char * const *s;#endifchar *p;int len;int error = -EINVAL;p = memchr(buf, n, n);len =
20、p ? p - buf : n;/* First, check if we are requested to hibernate */if (len = 4 & !strncmp(buf, disk, len) error = hibernate(); / 如果值是disk,那么进入STD模式,该模式暂不讨论goto Exit;#ifdef CONFIG_SUSPEND / deffor (s = &pm_statesstate; state PM_SUSPEND_MAX; s+, state+) if (*s & len = strlen(*s) & !strncmp(buf, *s, le
21、n)break;if (state /sys/power/pm_test/ echo mem /sys/power/state/ 是的话,延时5s后,然后做一些解冻进程等工作就返回goto Finish;pr_debug(PM: Entering %s sleepn, pm_statesstate);error = suspend_devices_and_enter(state); / 休眠外设Finish:pr_debug(PM: Finishing wakeup.n);suspend_finish(); / 解冻进程,发广播通知等Unlock:mutex_unlock(&pm_mutex)
22、;return error;static int suspend_prepare(void)int error;if (!suspend_ops | !suspend_ops-enter) / mtk_pm_enter() in mtkpm.creturn -EPERM;pm_prepare_console(); /给suspend分配一个虚拟终端来输出信息error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);/ 广播一个通知给通知链pm_chain_head,该通知链上都是用函数register_pm_notifier()注册的pm通知项(也可以用宏pm_notifier定义和注册),这里按照注册时候的定义的优先级来调用该通知链上注册的回调函数。/ PM_SUSPEND_PREPARE是事件值,表示将要进入suspendif (error)goto Finish;error = usermodehelper_disable(); / 关闭用户态的helper进程if (error)goto Finish;error = suspend_freeze_processes();/ 冻结所有的进程, 这里会保存所有进程当前的状态。if (!error)return 0;
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1