linux内核有一套电源管理的机制,休眠/唤醒在嵌入式Linux中是非常重要的部分,而android是基于linux内核的,它在此机制基本上作了改进。
本文只是针对参考代码总结的具体性的知识,其它有关系统知识网上一大堆,可自己搜索学习。
关于这部分我曾经写了份PPT作为内部技术交流稿,可下载参考下:
http://download.csdn.net/detail/yunjinwang/4698512
一,设备文件的生成:/sys/power/state
内核中的相关文件:
Kernel/power/main.c
Kernel/power/suspend.c
注册pm的流程:
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n);
static struct attribute * g[] = {&state_attr.attr, ……
static struct attribute_group attr_group = {.attrs = g,};
static int __init pm_init(void);
core_initcall(pm_init);
注册后生成/sys /power/state 文件,用户可以通过读写sys文件/sys /power/state 是实现控制系统休眠唤酲
echo on/mem > /sys/power/state
enter_state 是负责处理进入何种状态的函数。在linux里,电源管理基本都经过如下几步:
1. suspend_prepare():分配console终端、广播notify、冻结所有进程、保存当前状态
2. suspend_enter():休眠所有外设、关闭irq、休眠系统设备和总线、cpu省电状态
3. suspend_finish():恢复所有的进程
休眠的三个主要步骤:
a,冻结用户态进程和内核态任务
b,调用注册设备的suspend回调函数
c,停止内核态所有进程,保存上下文,使CPU进入休眠。
唤醒条件:中断或事件唤醒系统
唤醒顺序:
系统设备与总线最先唤醒
恢复内核态进程
重启各个设备
Suspend_finish()解冻进程和任务并Notify状态唤醒终端
二,suspend_prepare和suspend_devices_and_enter详解
1,suspend_prepare详解
suspend_prepare - Do prep work before entering low-power state.that is called for that we're entering.Run suspend notifiers, allocate a console and stop all processes.
static int suspend_prepare(void)
{ pm_prepare_console(); // 创建虚拟console以便导出输出打印信息,防止阻塞
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
error = usermodehelper_disable();
error = suspend_freeze_processes();
suspend_thaw_processes(); //前面出现错误,下面是恢复操作
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
pm_notifier_call_chain(PM_SUSPEND_PREPARE);
调用Linux系统的notifier_call_chain,通知相关的子系统正在进行suspend 系统的准备工作,提示相关子系统做出相应的操作准备。
Notifier chain是Linux的一种内核态子系统通知机制,当某一个子系统发生一些事件,会通知相关的子系统,实现信息共享,前提是其它子系统事先注册了对应的通知回调函数。
Notification chain由notifier block组成,其结构类型如下(include/linux/notifier.h):
struct notifier_block{
int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
struct notifier_block *next;
int priority;};
blocking_notifier_chain_register(struct notifier_block*); //注册函数
blocking_notifier_call_chain(); //处理函数
实例参考:/arch/arm/mach-s5pv210/cpu-freq.c
http://blog.chinaunix.net/link.php?url=http://www.linuxdiyf.com%2Fbbs%2Fviewthread.php%3Ftid%3D76138
/ * usermodehelper_disable - prevent new helpers from being started
usermodehelper_disable();
一般都是用户态直接通知同核态做相应处理,但有的时候需要从内核通知用户态来处理事件,这时候就用到user mode helper
参考资料:
http://news.dayoo.com/tech/201005/21/10000612_102055495.htm
/**tell processes to enter the refrigerator
suspend_freeze_processes();
冻结所有进程,并保存当前进程状态。
linux是用它的freeze框架加上信号处理来实现进程冻结的,在freeze所有进程的时候并没有将之挂起或者说使其不再运行,而是在信号处理的开始挂了一个类似于钩子的东西,把挂起进程交给信号处理来做,freeze框架要做的就是设置一些标志位来指示信号处理要冻结它了,然后设置此进程的信号附着位,这样该进程在返回用户空间的时候就会进入到冻结状态了。
/kernel/power/process.c freeae_process();
/arch/arm/kernel/signal.c do_signal();
参考资料:
http://blog.csdn.net/dog250/article/details/5303442
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
以上的处理是在前面的处理过程失败后,恢复到此前状态的操作
2,suspend_devices_and_enter详解
/*suspend_devices_and_enter - suspend devices and enter the desired system
int suspend_devices_and_enter(suspend_state_t state)
{
suspend_console(); //真正挂起console
dpm_suspend_start(PMSG_SUSPEND);
suspend_enter(state);
Resume_devices: //当挂起出现错误或恢复系统时运行下面部分
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
resume_console();
}
//****Prepare devices for PM transition and suspend them.
dpm_suspend_start(PMSG_SUSPEND)
{
error = dpm_prepare(state);// Prepare all non-sysdev devices for a system PM transition.
if (!error)
error = dpm_suspend(state);// Execute "suspend" callbacks for all non-sysdev devices.
}
//**enter the desired system sleep state. should be called after devices have been suspended.
static int suspend_enter(suspend_state_t state)
{
dpm_suspend_noirq(PMSG_SUSPEND); //Execute "late suspend" callbacks for non-sysdev devices
error = disable_nonboot_cpus();//关闭非启动的CPU
arch_suspend_disable_irqs(); //关闭所有的中断
error = sysdev_suspend(PMSG_SUSPEND);//关闭所有的设备
if (!error) {
sysdev_resume();
}
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
Enable_cpus:
enable_nonboot_cpus();
Power_up_devices:
dpm_resume_noirq(PMSG_RESUME);
三,被管理的设备注册相应的suspend和resume函数:
各设备注册:
void register_early_suspend(struct early_suspend *handler);
enum {
EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
};
设备休眠优先级,低--à高
struct early_suspend {
struct list_head link;
int level;
void (*suspend)(struct early_suspend *h);
void (*resume)(struct early_suspend *h);
};
四,power on/off流程:
在第一步的时候,注册模块到内核中会生成一个设备文件/sys /power/state
用户可以通过读写sys文件/sys /power/state 是实现控制系统休眠唤酲
echo on/mem > /sys/power/state

void request_suspend_state(suspend_state_t new_state)
{…….
If (new_state == PM_SUSPEND_MEM)
queue_work(suspend_work_queue, &early_suspend_work);
else If (new_state == PM_SUSPEND_ON)
queue_work(suspend_work_queue, &late_resume_work);
}
根据不同的命令queue_work相应的队列,每个队列都是各模块在启动时注册组成的,
list_for_each_entry(pos, &early_suspend_handlers, link)
{
if (pos->suspend != NULL)
pos->suspend(pos);
}
list_for_each_entry_reverse(pos, &early_suspend_handlers, link)
{
if (pos->resume != NULL)
pos->resume(pos);
}
Linux 系统休眠几种状态
•echo ??? > /dev/power/state
•1,on ------ 唤醒系统resume
•2,standy ---- 休眠(进程状态保存到memory,相对mem耗电,但恢复更快)
•3,mem ----- 休眠(进程状态保存到memory)
•4,disk ----- 休眠 (进程状态保存到磁盘,一般为swap分区)
在SMDKC11平台下,内核在调试的时候,如果进入到内核层的休眠,怎么会同时关闭console,导致无法打印,给调试带来了难度,可修改代码以达到在内核层休眠时,console仍然能够打印信息,具体方法是:在/drivers/serial/samsung.c中,将s3c24xx_serial_init()中的suspend 和resume赋值语句注销掉。