Linux Kernel and Android 休眠与唤醒Word下载.docx
《Linux Kernel and Android 休眠与唤醒Word下载.docx》由会员分享,可在线阅读,更多相关《Linux Kernel and Android 休眠与唤醒Word下载.docx(13页珍藏版)》请在冰豆网上搜索。
![Linux Kernel and Android 休眠与唤醒Word下载.docx](https://file1.bdocx.com/fileroot1/2023-1/22/60bccf29-b30f-47d5-9b54-5a5a9347b4ec/60bccf29-b30f-47d5-9b54-5a5a9347b4ec1.gif)
link
∙中文版:
作者:
zhangjiejing<
kzjeef#>
Date:
2010-04-07,
版本信息
∙LinuxKernel:
v2.6.28
∙Android:
v2.0
对于休眠(suspend)的简单介绍
在Linux中,休眠主要分三个主要的步骤:
1.冻结用户态进程和内核态任务
2.调用注册的设备的suspend的回调函数,顺序是按照注册顺序
3.休眠核心设备和使CPU进入休眠态冻结进程是内核把进程列表中所有的进程的状态都设置为停止,并且保存下所有进程的上下文.当这些进程被解冻的时候,他们是不知道自己被冻结过的,只是简单的继续执行.如何让Linux进入休眠呢?
用户可以通过读写sys文件/sys/power/state是实现控制系统进入休眠.比如
#echostandby>
/sys/power/state
命令系统进入休眠.也可以使用
#cat/sys/power/state
来得到内核支持哪几种休眠方式.
LinuxSuspend的流程
相关的文件:
你可以通过访问
Linux内核网站
来得到源代码,下面是文件的路径:
∙linux_soruce/kernel/power/main.c
∙linux_source/kernel/arch/xxx/mach-xxx/pm.c
∙linux_source/driver/base/power/main.c
接下来让我们详细的看一下Linux是怎么休眠/唤醒的.Let'
sgoingtoseehowthesehappens.
用户对于/sys/power/state的读写会调用到main.c中的state_store(),用户可以写入constchar*constpm_state[]中定义的字符串,比如"
mem"
"
standby"
.
然后state_store()会调用enter_state(),它首先会检查一些状态参数,然后同步文件系统.下面是代码:
/**
*enter_state-Docommonworkofenteringlow-powerstate.
*@state:
pm_statestructureforstatewe'
reentering.
*
*Makesurewe'
retheonlyonestryingtoenterasleepstate.Fail
*ifsomeonehasbeatustoit,sincewedon'
twantanythingweirdto
*happenwhenwewakeup.
*Then,dothesetupforsuspend,enterthestate,andcleaup(after
*we'
vewokenup).
*/
staticintenter_state(suspend_state_tstate)
{
interror;
if(!
valid_state(state))
return-ENODEV;
mutex_trylock(&
pm_mutex))
return-EBUSY;
printk(KERN_INFO"
PM:
Syncingfilesystems..."
);
sys_sync();
printk("
done.\n"
pr_debug("
Preparingsystemfor%ssleep\n"
pm_states[state]);
error=suspend_prepare();
if(error)
gotoUnlock;
if(suspend_test(TEST_FREEZER))
gotoFinish;
Entering%ssleep\n"
error=suspend_devices_and_enter(state);
Finish:
Finishingwakeup.\n"
suspend_finish();
Unlock:
mutex_unlock(&
pm_mutex);
returnerror;
}
准备,冻结进程
当进入到suspend_prepare()中以后,它会给suspend分配一个虚拟终端来输出信息,然后广播一个系统要进入suspend的Notify,关闭掉用户态的helper进程,然后一次调用suspend_freeze_processes()冻结所有的进程,这里会保存所有进程当前的状态,也许有一些进程会拒绝进入冻结状态,当有这样的进程存在的时候,会导致冻结失败,此函数就会放弃冻结进程,并且解冻刚才冻结的所有进程.
*suspend_prepare-Doprepworkbeforeenteringlow-powerstate.
*Thisiscommoncodethatiscalledforeachstatethatwe'
*Runsuspendnotifiers,allocateaconsoleandstopallprocesses.
staticintsuspend_prepare(void)
unsignedintfree_pages;
suspend_ops||!
suspend_ops->
enter)
return-EPERM;
pm_prepare_console();
error=pm_notifier_call_chain(PM_SUSPEND_PREPARE);
error=usermodehelper_disable();
if(suspend_freeze_processes()){
error=-EAGAIN;
gotoThaw;
}
free_pages=global_page_state(NR_FREE_PAGES);
if(free_pages<
FREE_PAGE_NUMBER){
freesomememory\n"
shrink_all_memory(FREE_PAGE_NUMBER-free_pages);
if(nr_free_pages()<
error=-ENOMEM;
printk(KERN_ERR"
Noenoughmemory\n"
error)
return0;
Thaw:
suspend_thaw_processes();
usermodehelper_enable();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
让外设进入休眠
现在,所有的进程(也包括workqueue/kthread)都已经停止了,内核态人物有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁,所以在外设的suspend()函数里面作lock/unlock锁要非常小心,这里建议设计的时候就不要在suspend()里面等待锁.而且因为suspend的时候,有一些Log是无法输出的,所以一旦出现问题,非常难调试.
然后kernel在这里会尝试释放一些内存.
最后会调用suspend_devices_and_enter()来把所有的外设休眠,在这个函数中,如果平台注册了suspend_pos(通常是在板级定义中定义和注册),这里就会调用suspend_ops->
begin(),然后driver/base/power/main.c中的device_suspend()->
dpm_suspend()会被调用,他们会依次调用驱动的suspend()回调来休眠掉所有的设备.
当所有的设备休眠以后,suspend_ops->
prepare()会被调用,这个函数通常会作一些准备工作来让板机进入休眠.接下来Linux,在多核的CPU中的非启动CPU会被关掉,通过注释看到是避免这些其他的CPU造成racecondion,接下来的以后只有一个CPU在运行了.
suspend_ops是板级的电源管理操作,通常注册在文件arch/xxx/mach-xxx/pm.c中.
接下来,suspend_enter()会被调用,这个函数会关闭archirq,调用device_power_down(),它会调用suspend_late()函数,这个函数是系统真正进入休眠最后调用的函数,通常会在这个函数中作最后的检查.如果检查没问题,接下来休眠所有的系统设备和总线,并且调用suspend_pos->
enter()来使CPU进入省电状态.这时候,就已经休眠了.代码的执行也就停在这里了.
*suspend_devices_and_enter-suspenddevicesandenterthedesiredsystem
*sleepstate.
statetoenter
intsuspend_devices_and_enter(suspend_state_tstate)
interror,ftrace_save;
suspend_ops)
return-ENOSYS;
if(suspend_ops->
begin){
error=suspend_ops->
begin(state);
gotoClose;
suspend_console();
ftrace_save=__ftrace_enabled_save();
suspend_test_start();
error=device_suspend(PMSG_SUSPEND);
if(error){
Somedevicesfailedtosuspend\n"
gotoRecover_platform;
suspend_test_finish("
suspenddevices"
if(suspend_test(TEST_DEVICES))
prepare){
prepare();
gotoResume_devices;
if(suspend_test(TEST_PLATFORM))
error=disable_nonboot_cpus();
error&
&
!
suspend_test(TEST_CPUS))
suspend_enter(state);
enable_nonboot_cpus();
finish)
suspend_ops->
finish();
Resume_devices:
device_resume(PMSG_RESUME);
resumedevices"
__ftrace_enabled_restore(ftrace_save);
resume_console();
Close:
end)
end();
Recover_platform:
recover)
recover();
Resume
如果在休眠中系统被中断或者其他事件唤醒,接下来的代码就会开始执行,这个唤醒的顺序是和休眠的循序相反的,所以系统设备和总线会首先唤醒,使能系统中断,使能休眠时候停止掉的非启动CPU,以及调用suspend_ops->
finish(),而且在suspend_devices_and_enter()函数中也会继续唤醒每个设备,使能虚拟终端,最后调用suspend_ops->
end().
在返回到enter_state()函数中的,当suspend_devices_and_enter()返回以后,外设已经唤醒了,但是进程和任务都还是冻结状态,这里会调用suspend_finish()来解冻这些进程和任务,而且发出Notify来表示系统已经从suspend状态退出,唤醒终端.
到这里,所有的休眠和唤醒就已经完毕了,系统继续运行了.
Android休眠(suspend)
在一个打过android补丁的内核中,state_store()函数会走另外一条路,会进入到request_suspend_state()中,这个文件在earlysuspend.c中.这些功能都是android系统加的,后面会对earlysuspend和lateresume进行介绍.
涉及到的文件:
∙linux_source/kernel/power/main.c
∙linux_source/kernel/power/earlysuspend.c
∙linux_source/kernel/power/wakelock.c
特性介绍
EarlySuspend
Earlysuspend是android引进的一种机制,这种机制在上游备受争议,这里不做评论.这个机制作用在关闭显示的时候,在这个时候,一些和显示有关的设备,比如LCD背光,比如重力感应器,触摸屏,这些设备都会关掉,但是系统可能还是在运行状态(这时候还有wakelock)进行任务的处理,例如在扫描SD卡上的文件等.在嵌入式设备中,背光是一个很大的电源消耗,所以android会加入这样一种机制.
LateResume
LateResume是和suspend配套的一种机制,是在内核唤醒完毕开始执行的.主要就是唤醒在EarlySuspend的时候休眠的设备.
WakeLock
WakeLock在Android的电源管理系统中扮演一个核心的角色.WakeLock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得.这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁.如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠.
AndroidSuspend
当用户写入mem或者standby到/sys/power/state中的时候,state_store()会被调用,然后Android会在这里调用request_suspend_state()而标准的Linux会在这里进入enter_state()这个函数.如果请求的是休眠,那么early_suspend这个workqueue就会被调用,并且进入early_suspend状态.
voidrequest_suspend_state(suspend_state_tnew_state)
unsignedlongirqflags;
intold_sleep;
spin_lock_irqsave(&
state_lock,irqflags);
old_sleep=state&
SUSPEND_REQUESTED;
if(debug_mask&
DEBUG_USER_STATE){
structtimespects;
structrtc_timetm;
getnstimeofday(&
ts);
rtc_time_to_tm(ts.tv_sec,&
tm);
pr_info("
request_suspend_state:
%s(%d->
%d)at%lld"
"
(%d-%02d-%02d%02d:
%02d:
%02d.%09luUTC)\n"
new_state!
=PM_SUSPEND_ON?
sleep"
:
wakeup"
requested_suspend_state,new_state,
ktime_to_ns(ktime_get()),
tm.tm_year+1900,tm.tm_mon+1,tm.tm_mday,
tm.tm_hour,tm.tm_min,tm.tm_sec,ts.tv_nsec);
old_sleep&
=PM_SUSPEND_ON){
state|=SUSPEND_REQUESTED;
queue_work(suspend_work_queue,&
early_suspend_work);
}elseif(old_sleep&
new_state==PM_SUSPEND_ON){
state&
=~SUSPEND_REQUESTED;
wake_lock(&
main_wake_lock);
late_resume_work);
requested_suspend_state=new_state;
spin_unlock_irqrestore(&
在early_suspend()函数中,首先会检查现在请求的状态还是否是suspend,来防止suspend的请求会在这个时候取消掉(因为这个时候用户进程还在运行),如果需要退出,就简单的退出了.如果没有,这个函数就会把earlysuspend中注册的一系列的回调都调用一次,然后同步文件系统,然后放弃掉main_wake_lock,这个wakelock是一个没有超时的锁,如果这个锁不释放,那么系统就无法进入休眠.
staticvoidearly_suspend(structwork_struct*work)
structearly_suspend*pos;
intabort=0;
mutex_lock(&
early_suspend_lock);
if(state==SUSPEND_REQUESTED)
state|=SUSPENDED;
else
abort=1;
if(abort){
DEBUG_SUSPEND)
early_suspend:
abort,state%d\n"
state);
gotoabort;
callhandlers\n"
list_for_each_entry(pos,&
early_suspend_handlers,link){
if(pos->
suspend!
=NULL)
pos->
suspend(pos);
sync\n"
abort:
if(state==SUSPEND_REQUESTED_AND_SUSPENDED)
wake_unlock(&
当所有的唤醒已经结束以后