https://blog.csdn.net/gjsisi/article/details/8613754
Android休眠喚醒機制簡介(一)
1、背景介紹:
睡眠/喚醒是嵌入式Linux非常重要的組成部分,因為優秀的睡眠喚醒機制可以是嵌入式設備盡可能的進入休眠狀態,來延長電池的續航時間(這在移動終端消費類電子設備中是非常重要和有意義的!!)。但標準的Linux睡眠喚醒機制有其自身的一些缺陷(所有模塊必須同時睡下或者喚醒),在某些情況下,這會導致能耗的白白浪費。因此Android在標準Linux睡眠喚醒的機制上作了新的改動(wake_lock喚醒、early_suspend和late_resume機制),從而很好的解決上面的問題。本文將以Android2.3.1版本為例,詳細介紹標準Linux睡眠/喚醒是如何工作的,并且Android中是如何把其自身特有的機制和Linux中標準的聯系起來的。
2、標準Linux睡眠喚醒機制簡介:
在標準Linux中,休眠主要分三個主要的步驟:(1)凍結用戶態進程和內核態任務;(2)調用注冊的設備的suspend的回調函數,其調用順序是按照驅動加載時的注冊順序。(3)休眠核心設備和使CPU進入休眠態凍結進程是內核把進程列表中所有的進程的狀態都設置為停止,并且保存下所有進程的上下文。當這些進程被解凍的時候,它們是不知道自己被凍結過的,只是簡單的繼續執行。
那么是如何讓Linux進入休眠的呢?其實很簡單,因為Android和kernel已經做了很多復雜的工作,所以用戶只需可以通過讀寫sys文件/sys/power/state 就可以實現控制系統進入休眠。
比如:#echomem> /sys/power/state/////// 使系統進行睡眠
#echoon> /sys/power/state////////////使系統從睡眠中喚醒過來
當然還有其它的狀態操作,在下面的內容中將有介紹。
3、Android睡眠喚醒機制簡介:
Android在Linux內核原有的睡眠喚醒模塊上基礎上,主要增加了下面三個機制:
Wake _Lock 喚醒鎖機制;
Early _Suspend 預掛起機制;
Late _Resume 遲喚醒機制;
其基本原理如下:當啟動一個應用程序的時候,它都可以申請一個wake_lock喚醒鎖,每當申請成功之后都會在內核中注冊一下(通知系統內核,現在已經有鎖被申請),當應用程序在某種情況下釋放wake_lock的時候,會注銷之前所申請的wake_lock。特別要注意的是:只要是系統中有一個wake_lock的時候,系統此時都不能進行睡眠。但此時各個模塊可以進行early_suspend。當系統中所有的wake_lock都被釋放之后,系統就會進入真正的kernel的睡眠狀態。在系統啟動的時候會創建一個主喚醒鎖main_wake_lock,該鎖是內核初始化并持有的一個WAKE_LOCK_SUSPEND屬性的非限時喚醒鎖。因此,系統正常工作時,將始終因為該鎖被內核持有而無法進入睡眠狀態。也就是說在不添加新鎖的情況下,只需將main_wake_lock解鎖,系統即可進入睡眠狀態。
下面是Android睡眠喚醒模塊框架
接下來我們將以上圖的框架結構為主線,將進行非常非常詳細地從最上層到最底層的跟蹤!!!本文的主旨主要就是讀者從Android最上層(Java寫的應用程序)一步一步的往下跟進,經過Java、C++和C語言寫的Framework層、JNI層、HAL層最后到達android的最底層(Kernel層)。通過本文的閱讀,您將對android的整體有更加深入、宏觀的理解和把握!
主要涉及到的目錄文件:
android/frameworks/base/core/java/android/os/PowerManager.java
android/frameworks/base/services/java/com/android/server/PowerManagerService.java
android/frameworks/base/core/java/android/os/ Power.java
android/frameworks/base/core/jni/android_os_Power.cpp
android/hardware/libhardware_legacy/power/power.c
android/kernel/kernel/power/main.c
android/kernel/kernel/power/earlysuspend.c
android/kernel/kernel/power/suspend.c
android/kernel/kernel/power/wakelock.c
android/kernel/kernel/power/userwakelock.c
在應用程序框架層中,PowerManager類是面向上層應用程序的接口類,提供了WakeLock機制(同時也是睡眠喚醒子系統)的基本接口(喚醒鎖的獲取和釋放)。上層應用程序通過調用這些接口,實現對系統電源狀態的監控。PowerManager類通過IBinder這種Android中特有的通信模式,與PowerManagerService類進行通信。PowerManagerService 是PowerManager 類中定義的接口的具體實現,并進一步調用Power類來與下一層進行通信。PowerManagerService 類是WakeLock機制在應用程序框架層的核心,他們對應用程調用PowerManager類接口時所傳遞的參數進行初步的分析和對應的設置,并管理一個喚醒鎖隊列,然后配合其他模塊(例如WatchDog、BatteryService、ShutdownThread等)的狀態信息,做出決策,調用Power類的對應接口,最終通過JNI 接口,調用到硬件抽象層中的函數,對sysfs的用戶接口進行操作,從而觸發內核態實現的用。
PowerManager.java:提供上層應用程序的接口;
PowerManagerService.java:具體實現PowerManager類中的接口;
Power.java:被PowerManagerService類調用;
android_os_Power.cpp:實現Power類中的JNI接口;
power.c:進行sysfs用戶接口的操作。
其余涉及到的都是內核kernel中的文件,它們的作用將在后續給予介紹。
具體流程
下面我將分別以兩條路線(第一:獲得wakelock喚醒鎖。第二:系統進入睡眠。)來分別說明各自的流程,讓讀者對android睡眠喚醒機制有更深入的理解!
第一部分:獲得wakelock喚醒鎖
比如在應用程序中,當獲得wakelock喚醒鎖的時候,它首先是調用/android/frameworks/base/core/java/
android/os/PowerManager類中的public voidacquire()方法,而該方法通過android特有的通訊機制,會接著調用到PowerManagerService類中的publicvoid acquireWakeLock。
public void acquireWakeLock(int flags, IBinder lock, Stringtag, WorkSource ws) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
if (uid != Process.myUid()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK,null);
}
if (ws != null) {
enforceWakeSourcePermission(uid, pid);
}
long ident =Binder.clearCallingIdentity();
try {
synchronized (mLocks) {
acquireWakeLockLocked(flags,lock, uid, pid, tag, ws);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
而 public voidacquireWakeLock方法又調用了acquireWakeLockLocked。
public void acquireWakeLockLocked(int flags, IBinder lock, intuid, int pid, String tag,
WorkSourcews)
{
if (mSpew) {
Slog.d(TAG, "acquireWakeLock flags=0x" +Integer.toHexString(flags) + " tag=" + tag); }
if (ws != null&& ws.size() == 0) {ws =null;}
int index = mLocks.getIndex(lock);
WakeLock wl;
boolean newlock;
boolean diffsource;
WorkSource oldsource;
.
中間代碼省略
.
Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);
}
if (diffsource) {
// If thelock sources have changed, need to first release the
// oldones.
noteStopWakeLocked(wl, oldsource);
}
if (newlock || diffsource) {
noteStartWakeLocked(wl, ws);
}
}
我們可以看到在acquireWakeLockLocked 方法調用Power類中的public static native voidacquireWakeLock(int lock, Stringid)方法。而該方法是調用android_os_Power.cpp中的static voidacquireWakeLock()函數。
static void acquireWakeLock(JNIEnv *env, jobject clazz, jintlock, jstring idObj)
{
if (idObj == NULL){
throw_NullPointerException(env, "id isnull");
return ;
}
const char *id =env->GetStringUTFChars(idObj, NULL);
acquire_wake_lock(lock,id);
env->ReleaseStringUTFChars(idObj, id);
}
函數acquire_wake_lock()的實現在 power.c中,其定義如下:
intacquire_wake_lock(int lock, const char*id)
{
initialize_fds();
//LOGI("acquire_wake_lock lock=%d id='%s'\n", lock,id);
if (g_error) returng_error;
int fd;
if (lock ==PARTIAL_WAKE_LOCK) {
fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
}
else {
return EINVAL;
}
return write(fd, id,strlen(id));
}
到現在為止,我們的代碼流程已經走了一大半了,我們一開始介紹的android的上面幾層Framework層、JNI層、HAL層都已經介紹了就剩下Kernel層了。下面就應該是和kernel層進行交互了。
但是在android/hardware/libhardware_legacy/power/power.c中的acquire_wake_lock()函數似乎沒法和kernel層進行通信啊??不急要淡定!!在這個函數的最后不是還有一個返回語句return write(fd, id,strlen(id))嘛!!有人會說這句話看不出什么啊,我一開始用SourceInsight代碼閱讀器跟蹤的時候也沒有找到它的原型,那個叫急啊!!呵呵最后經過我的繼續不斷的努力查找(其實方法很簡單,既然我從上往下的路斷了,那我就換個方向,我最后又從下往上順著代碼走了一遍),終于被我發現了。
我們先看一下android/kernel/kernel/power/main.c中的一段代碼,我將會做簡單的分析,之后你就會明白剛才上面所產生的疑問了。
#ifdef CONFIG_USER_WAKELOCK
power_attr(wake_lock);
power_attr(wake_unlock);
#endif
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr,
#endif
#ifdef CONFIG_USER_WAKELOCK
&wake_lock_attr.attr,
&wake_unlock_attr.attr,
#endif
#endif
NULL,
};
static structattribute_groupattr_group = {
.attrs = g,
};
#ifdef CONFIG_PM_RUNTIME
struct workqueue_struct *pm_wq;
EXPORT_SYMBOL_GPL(pm_wq);
static int __init pm_start_workqueue(void)
{
pm_wq = create_freezeable_workqueue("pm");
return pm_wq ? 0 : -ENOMEM;
}
#else
static inline int pm_start_workqueue(void) { return 0; }
#endif
static int __init pm_init(void)
{
int error = pm_start_workqueue();
if (error)
return error;
power_kobj = kobject_create_and_add("power", NULL);
if (!power_kobj)
return -ENOMEM;
return sysfs_create_group(power_kobj,&attr_group);
}
core_initcall(pm_init);
這段代碼雖然簡短,但看起來是不是還是比較費勁,沒關系,我們倒過來看就比較清楚了。上面代碼中的sysfs_create_group(power_kobj,&attr_group);的意思就是當我們在對sysfs/下相對的節點進行操作的時候會調用與attr_group里的相關函數,再往上面看其實就是指&wake_lock_attr.attr(對不同情況的操作會調用不同的attr_group,在第二條路的里面我們還會再次接觸到這里)。power_attr(wake_lock)就是使具體的操作函數與其掛鉤。我們現在來看一看這個掛鉤過程是怎么實現的。
#define power_attr(_name) \
static struct kobj_attribute _name##_attr = {\
.attr = { \
.name = __stringify(_name),\
.mode = 0644, \
}, \
.show = _name##_show,\
.store = _name##_store,\
}
在該函數中##的作用通俗點講就是“連接”的意思,比如power_attr(wake_lock),
static structkobj_attributewake_lock_attr = {\
.attr = { \
.name = __stringify(wake_lock),\
.mode = 0644, \
}, \
.show = wake_lock_show, \
.store = wake_lock_store, \
}
函數wake_lock_store和wake_lock_show就定義在android/kernel/kernel/power/userwakelock.c
中。因此當我們對/sys/power/wake_lock進行操作的時候就會調用到userwakelock.c中定義的
wake_lock_store()函數。
好了,我們該回到原來我們產生疑問的地方了,在power.c中我們將重新研究一下這這段代碼,這時我們還得關注其中的另一個函數initialize_fds()。
intacquire_wake_lock(int lock, const char*id)
{
initialize_fds();
//LOGI("acquire_wake_lock lock=%d id='%s'\n", lock,id);
if (g_error) returng_error;
int fd;
if (lock ==PARTIAL_WAKE_LOCK) {
fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
}
else {
return EINVAL;
}
return write(fd, id, strlen(id));
}
initialize_fds(void)
{
// XXX: should bethis:
//pthread_once(&g_initialized,open_file_descriptors);
// XXX: not this:
if (g_initialized == 0){
if(open_file_descriptors(NEW_PATHS)< 0) {
open_file_descriptors(OLD_PATHS);
on_state ="wake";
off_state= "standby";
}
g_initialized = 1;
}
}
其實這個函數中最和新的步驟就是open_file_descriptors(NEW_PATHS) ;而
const char * const NEW_PATHS[] = {
"/sys/power/wake_lock",
"/sys/power/wake_unlock",
"/sys/power/state"
};
總之經過著一些列的步驟后,最終我們將在return write(fd, id,strlen(id));時調用android/kernel/kernel/power/userwakelock.c 中的wake_lock_store()函數。
ssize_twake_lock_store(
struct kobject *kobj, struct kobj_attribute*attr,
const char *buf, size_t n)
{
long timeout;
struct user_wake_lock*l;
mutex_lock(&tree_lock);
l = lookup_wake_lock_name(buf, 1,&timeout);
if (IS_ERR(l)) {
n = PTR_ERR(l);
goto bad_name;
}
if (debug_mask & DEBUG_ACCESS)
pr_info("wake_lock_store: %s,timeout %ld\n", l->name, timeout);
if (timeout)
wake_lock_timeout(&l->wake_lock,timeout);
else
wake_lock(&l->wake_lock);
bad_name:
mutex_unlock(&tree_lock);
return n;
}
該函數執行的基本流程為:首先調用lookup_wake_lock_name()來獲得指定的喚醒鎖,若延遲參數timeout為零的話,就調用wake_lock()否則就調用wake_lock_timeout(),但不管調用哪個最后都會調用到android/kernel/kernel/power/wakelock.c中的函數staticvoid wake_lock_internal()。
static void wake_lock_internal(struct wake_lock *lock, longtimeout, int has_timeout)
{
int type;
unsigned long irqflags;
long expire_in;
spin_lock_irqsave(&list_lock, irqflags);
type = lock->flags &WAKE_LOCK_TYPE_MASK;
BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
BUG_ON(!(lock->flags &WAKE_LOCK_INITIALIZED));
#ifdef CONFIG_WAKELOCK_STAT
if (type == WAKE_LOCK_SUSPEND&& wait_for_wakeup) {
if (debug_mask & DEBUG_WAKEUP)
pr_info("wakeup wake lock: %s\n",lock->name);
wait_for_wakeup = 0;
lock->stat.wakeup_count++;
}
if ((lock->flags &WAKE_LOCK_AUTO_EXPIRE) &&
(long)(lock->expires - jiffies) <= 0){
wake_unlock_stat_locked(lock, 0);
lock->stat.last_time = ktime_get();
}
#endif
if (!(lock->flags &WAKE_LOCK_ACTIVE)) {
lock->flags |= WAKE_LOCK_ACTIVE;
#ifdef CONFIG_WAKELOCK_STAT
lock->stat.last_time = ktime_get();
#endif
}
list_del(&lock->link);
if (has_timeout) {
if (debug_mask & DEBUG_WAKE_LOCK)
pr_info("wake_lock: %s, type %d, timeout %ld.lu\n",
lock->name, type, timeout / HZ,
(timeout % HZ) * MSEC_PER_SEC / HZ);
lock->expires = jiffies + timeout;
lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
list_add_tail(&lock->link,&active_wake_locks[type]);
} else {
if (debug_mask & DEBUG_WAKE_LOCK)
pr_info("wake_lock: %s, type %d\n", lock->name,type);
lock->expires = LONG_MAX;
lock->flags &=~WAKE_LOCK_AUTO_EXPIRE;
list_add(&lock->link,&active_wake_locks[type]);
}
if (type == WAKE_LOCK_SUSPEND) {
current_event_num++;
#ifdef CONFIG_WAKELOCK_STAT
if (lock == &main_wake_lock)
update_sleep_wait_stats_locked(1);
else if(!wake_lock_active(&main_wake_lock))
update_sleep_wait_stats_locked(0);
#endif
if (has_timeout)
expire_in = has_wake_lock_locked(type);
else
expire_in = -1;
if (expire_in > 0) {
if (debug_mask & DEBUG_EXPIRE)
pr_info("wake_lock: %s, start expire timer, "
"%ld\n", lock->name, expire_in);
mod_timer(&expire_timer, jiffies +expire_in);
} else {
if (del_timer(&expire_timer))
if (debug_mask & DEBUG_EXPIRE)
pr_info("wake_lock: %s, stop expire timer\n",
lock->name);
if (expire_in == 0)
queue_work(suspend_work_queue,&suspend_work);
}
}
spin_unlock_irqrestore(&list_lock,irqflags);
}
到這里為止,我們走的第一條路就到目的地了,這個函數具體做了什么,在這里就不仔細分析了,大家可以自己再跟下或者上網查相關資料,理解這個函數不難。
第二部分:系統進入睡眠
有了上面第一部分的學習,再看第二部分的話,會容易很多。假如現在我們按了PAD上的power睡眠鍵,經過一些列的事件處理后,它會調用到PowerManager類中的
public void goToSleep(longtime)
{
try {
mService.goToSleep(time);
} catch (RemoteException e) {
}
}
而該函數會調用到PowerManagerService類中的public void goToSleep()方法;
public void goToSleep(long time)
{
goToSleepWithReason(time,WindowManagerPolicy.OFF_BECAUSE_OF_USER);
}
goToSleepWithReason()會調用goToSleepLocked()方法,接著會調用setPowerState();而setPowerState()方法里會調用setScreenStateLocked(),setScreenStateLocked()又會調用到Power類中的JNI接口setScreenState(),其具體實現是在android_os_Power.cpp文件中;
static int setScreenState(JNIEnv *env, jobject clazz, jbooleanon)
{
returnset_screen_state(on);
}
函數中returnset_screen_state()的實現是android/hardware/libhardware_legacy/power/power.c
set_screen_state(inton)
{
QEMU_FALLBACK(set_screen_state(on));
LOGI("***set_screen_state %d", on);
initialize_fds();
//LOGI("go_to_sleepeventTime=%lld now=%lld g_error=%s\n", eventTime,
// systemTime(),strerror(g_error));
if (g_error) returng_error;
char buf[32];
int len;
if(on)
len = snprintf(buf, sizeof(buf), "%s",on_state);
else
len = snprintf(buf, sizeof(buf), "%s",off_state);
buf[sizeof(buf) - 1] ='\0';
len =write(g_fds[REQUEST_STATE], buf, len);
if(len <0) {
LOGE("Failed setting last user activity:g_error=%d\n", g_error);
}
return 0;
看!!代碼到這里是不是跟第一部分很相似?不錯,如果接著往下分析的話,可以套用上面第一部分的分析思路,最終len =write(g_fds[REQUEST_STATE], buf,len);語句調用的是android//kernel/kernel/power/main.c中的set_screen_state();
當我們在sys/power/state(android/hardware/libhardware_legacy/power/power.c)進行讀寫操作的時候,(linux/kernel/power/main.c)中的state_store()函數會被調用,在該函數中會分成兩個分支:
static ssize_t state_store(struct kobject *kobj, structkobj_attribute *attr, const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPEND
suspend_state_t state = PM_SUSPEND_ON;
#else
suspend_state_t state =PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
if (len == 4 &&!strncmp(buf, "disk", len)) {
error = hibernate();
goto Exit;
}
#ifdef CONFIG_SUSPEND
for (s = &pm_states[state];state < PM_SUSPEND_MAX; s++, state++) {
if (*s&& len == strlen(*s)&& !strncmp(buf, *s, len))
break;
}
if (state < PM_SUSPEND_MAX&& *s)
#ifdef CONFIG_EARLYSUSPEND
if (state == PM_SUSPEND_ON ||valid_state(state)) {
error =0;
request_suspend_state(state);
}
#else
error =enter_state(state);
#endif
#endif
Exit:
return error ? error : n;
}
Android特有的earlysuspend: request_suspend_state(state)
Linux標準的suspend:enter_state(state)
注意:如果CONFIG_EARLYSUSPEND宏開的話,kernel會先走earlysuspend,反之則直接走suspend;從這里開始就要分兩個分支了,如果支持earlysuspend的話就進入request_suspend_state(state)函數,如果不支持的話就進入標準Linux的enter_state(state)函數。、
這兩個函數分別在兩個文件中kernel/kernel/power/earlysuspend.c和suspend.c。現在再回過頭來看的話,感覺整個android中睡眠喚醒機制還是很清晰的。這兩個函數體里又做了什么,在這里就不再做具體分析,大家可以自己對照代碼或者上網查資料,因為本文的主旨是帶讀者從最上層應用層一直到最底層kernel層,把整個android的睡眠喚醒機制給走通。
PowerManager.javagoToSleep( )
PowerManagerService.javagoToSleep()
PowerManagerService.javagoToSleepWithReason()
PowerManagerService.javasetPowerState()
PowerManagerService.javaSetScreenStateLocked ()
Power.javasetScreenState()
android_os_Power.cppsetScreenState()
power.cset_screen_state( )
main.cstate_store( )
標準linux休眠過程:
lpowermanagementnotifiersareexecutedwithPM_SUSPEND_PREPARE
ltasksarefrozen
ltargetsystemsleepstateisannouncedtotheplatform-handlingcode
ldevicesaresuspended
lplatform-specificglobalsuspendpreparationmethodsareexecuted
lnon-bootCPUsaretakenoff-line
linterruptsaredisabledontheremaining(main)CPU
llatesuspendofdevicesiscarriedout(一般有一些BUSdriver的動作進行)?
lplatform-specificglobalmethodsareinvokedtoputthesystemtosleep
標準linux喚醒過程:
lthemainCPUisswitchedtotheappropriatemode,ifnecessary
learlyresumeofdevicesiscarriedout(一般有
lplatform-specificglobalresumepreparationmethodsareinvoked
ldevicesarewokenup
ltasksarethawed
lpowermanagementnotifiersareexecutedwithPM_POST_SUSPEND
用戶可以通過sys文件系統控制系統進入休眠一些BUSdriver的動作進行)?
linterruptsareenabledonthemainCPU
lnon-bootCPUsareenabled:
查看系統支持的休眠方式:
#cat/sys/power/state
常見有standby(suspendtoRAM)、mem(suspendtoRAM)和disk(suspendtodisk),只是standby耗電更多,返回到正常工作狀態的時間更短。
通過#echomem>/sys/power/state讓系統進入休眠。
#echo on>/sys/power/state使系統喚醒。
Android休眠與喚醒
android是在傳統的linux內核電源管理設計的基礎上,結合手機設計的實際需求而進化出的一套電源管理系統,其核心內容有:wakelock、early_suspend與late_resume。
wakelock在Android的電源管理系統中扮演一個核心的角色。wakelock是一種鎖的機制,只要有人拿著這個鎖,系統就無法進入休眠,可以被用戶態程序和內核獲得。這個鎖可以是有超時的或者是沒有超時的,超時的鎖會在時間過去以后自動解鎖。如果沒有鎖了或者超時了,內核就會啟動休眠的那套機制來進入休眠。
當系統在啟動完畢后,會自己去加一把名為“main“的鎖,而當系統有意愿去睡眠時則會先去釋放這把“main”鎖,在android中,在early_suspend的最后一步會去釋放“main”鎖(wake_unlock:main)。釋放完后則會去檢查是否還有其他存在的鎖,如果沒有則直接進入睡眠過程。
它的缺點是,如果有某一應用獲鎖而不釋放或者因一直在執行某種操作而沒時間來釋放的話,則會導致系統一直進入不了睡眠狀態,功耗過大。
early_suspend:先與linux內核的睡眠過程被調用。一般在手機系統的設計中對背光的操作等采用此類方法,因為背光需要的能耗過大。當然此操作與late_resume是配套使用的。一些在內核中要預先進行處理的事件可以先注冊上early_suspend函數,當系統要進入睡眠之前會首先調用這些注冊的函數。
本文中,linuxkernel版本為linux-2.6.29,android版本為android2.1
與android休眠喚醒主要相關的文件主要有:
llinux_source/kernel/power/main.c
llinux_source/kernel/power/earlysuspend.c
llinux_source/kernel/power/wakelock.c
llinux_source/kernel/power/process.c
llinux_source/driver/base/power/main.c
llinux_source/arch/xxx/mach-xxx/pm.c或linux_source/arch/xxx/plat-xxx/pm.c
Android休眠過程如下:
當用戶讀寫/sys/power/state時,linux_source/kernel/power/main.c中的state_store()函數會被調用。其中,android的early_suspend會執行request_suspend_state(state);而標準的linux休眠則執行error=enter_state(state);
staticssize_tstate_store(structkobject*kobj,structkobj_attribute*attr,
constchar*buf,size_tn)
{
#ifdefCONFIG_SUSPEND
#ifdefCONFIG_EARLYSUSPEND
suspend_state_tstate=PM_SUSPEND_ON;
#else
suspend_state_tstate=PM_SUSPEND_STANDBY;
#endif
constchar*const*s;
#endif
char*p;
intlen;
interror=-EINVAL;
p=memchr(buf,'\n',n);
len=p?p-buf:n;
if(len==4&&!strncmp(buf,"disk",len)){
error=hibernate();
gotoExit;
}
#ifdefCONFIG_SUSPEND
for(s=&pm_states[state];state
if(*s&&len==strlen(*s)&&!strncmp(buf,*s,len))
break;
}
if(state
#ifdefCONFIG_EARLYSUSPEND
if(state==PM_SUSPEND_ON||valid_state(state)){
error=0;
request_suspend_state(state);
}
#else
#endif
#endif
Exit:
returnerror?error:n;
}
在request_suspend_state(state)函數中,會調用early_suspend_work的工作隊列,從而進入early_suspend()函數中。
staticDECLARE_WORK(early_suspend_work,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-d-dd:d:d.luUTC)\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);
}
if(!old_sleep&&new_state!=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);
queue_work(suspend_work_queue,&late_resume_work);
}
requested_suspend_state=new_state;
spin_unlock_irqrestore(&state_lock,irqflags);
}
在early_suspend()函數中,首先要判斷當前請求的狀態是否還是suspend,若不是,則直接退出了;若是,函數會調用已經注冊的early_suspend的函數。然后同步文件系統,最后釋放main_wake_lock。
staticvoidearly_suspend(structwork_struct*work)
{
structearly_suspend*pos;
unsignedlongirqflags;
intabort=0;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock,irqflags);
if(state==SUSPEND_REQUESTED)
state|=SUSPENDED;
else
abort=1;
spin_unlock_irqrestore(&state_lock,irqflags);
if(abort){
if(debug_mask&DEBUG_SUSPEND)
pr_info("early_suspend:abort,state%d\n",state);
mutex_unlock(&early_suspend_lock);
gotoabort;
}
if(debug_mask&DEBUG_SUSPEND)
pr_info("early_suspend:callhandlers\n");
list_for_each_entry(pos,&early_suspend_handlers,link){
if(pos->suspend!=NULL)
pos->suspend(pos);
}
mutex_unlock(&early_suspend_lock);
if(debug_mask&DEBUG_SUSPEND)
pr_info("early_suspend:sync\n");
sys_sync();
abort:
spin_lock_irqsave(&state_lock,irqflags);
if(state==SUSPEND_REQUESTED_AND_SUSPENDED)
wake_unlock(&main_wake_lock);
spin_unlock_irqrestore(&state_lock,irqflags);
}
在wake_unlock()中,刪除鏈表中wake_lock節點,判斷當前是否存在wake_lock,若wake_lock的數目為0,則調用工作隊列suspend_work,進入suspend狀態。
staticDECLARE_WORK(suspend_work,suspend);
voidwake_unlock(structwake_lock*lock)
{
inttype;
unsignedlongirqflags;
spin_lock_irqsave(&list_lock,irqflags);
type=lock->flags&WAKE_LOCK_TYPE_MASK;
#ifdefCONFIG_WAKELOCK_STAT
wake_unlock_stat_locked(lock,0);
#endif
if(debug_mask&DEBUG_WAKE_LOCK)
pr_info("wake_unlock:%s\n",lock->name);
lock->flags&=~(WAKE_LOCK_ACTIVE|WAKE_LOCK_AUTO_EXPIRE);
list_del(&lock->link);
list_add(&lock->link,&inactive_locks);
if(type==WAKE_LOCK_SUSPEND){
longhas_lock=has_wake_lock_locked(type);
if(has_lock>0){
if(debug_mask&DEBUG_EXPIRE)
pr_info("wake_unlock:%s,startexpiretimer,"
"%ld\n",lock->name,has_lock);
mod_timer(&expire_timer,jiffies+has_lock);
}else{
if(del_timer(&expire_timer))
if(debug_mask&DEBUG_EXPIRE)
pr_info("wake_unlock:%s,stopexpire"
"timer\n",lock->name);
if(has_lock==0)
queue_work(suspend_work_queue,&suspend_work);
}
if(lock==&main_wake_lock){
if(debug_mask&DEBUG_SUSPEND)
print_active_locks(WAKE_LOCK_SUSPEND);
#ifdefCONFIG_WAKELOCK_STAT
update_sleep_wait_stats_locked(0);
#endif
}
}
spin_unlock_irqrestore(&list_lock,irqflags);
}
在suspend()函數中,先判斷當前是否有wake_lock,若有,則退出;然后同步文件系統,最后調用pm_suspend()函數。
staticvoidsuspend(structwork_struct*work)
{
intret;
intentry_event_num;
if(has_wake_lock(WAKE_LOCK_SUSPEND)){
if(debug_mask&DEBUG_SUSPEND)
pr_info("suspend:abortsuspend\n");
return;
}
entry_event_num=current_event_num;
sys_sync();
if(debug_mask&DEBUG_SUSPEND)
pr_info("suspend:entersuspend\n");
ret=pm_suspend(requested_suspend_state);
if(debug_mask&DEBUG_EXIT_SUSPEND){
structtimespects;
structrtc_timetm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec,&tm);
pr_info("suspend:exitsuspend,ret=%d"
"(%d-d-dd:d:d.luUTC)\n",ret,
tm.tm_year+1900,tm.tm_mon+1,tm.tm_mday,
tm.tm_hour,tm.tm_min,tm.tm_sec,ts.tv_nsec);
}
if(current_event_num==entry_event_num){
if(debug_mask&DEBUG_SUSPEND)
pr_info("suspend:pm_suspendreturnedwithnoevent\n");
wake_lock_timeout(&unknown_wakeup,HZ/2);
}
}
在pm_suspend()函數中,enter_state()函數被調用,從而進入標準linux休眠過程。
intpm_suspend(suspend_state_tstate)
{
if(state>PM_SUSPEND_ON&&state<=PM_SUSPEND_MAX)
returnenter_state(state);
return-EINVAL;
}
在enter_state()函數中,首先檢查一些狀態參數,再同步文件系統,然后調用suspend_prepare()來凍結進程,最后調用suspend_devices_and_enter()讓外設進入休眠。
staticintenter_state(suspend_state_tstate)
{
interror;
if(!valid_state(state))
return-ENODEV;
if(!mutex_trylock(&pm_mutex))
return-EBUSY;
printk(KERN_INFO"PM:Syncingfilesystems...");
sys_sync();
printk("done.\n");
pr_debug("PM:Preparingsystemfor%ssleep\n",pm_states[state]);
error=suspend_prepare();
if(error)
gotoUnlock;
if(suspend_test(TEST_FREEZER))
gotoFinish;
pr_debug("PM:Entering%ssleep\n",pm_states[state]);
error=suspend_devices_and_enter(state);
Finish:
pr_debug("PM:Finishingwakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
returnerror;
}
在suspend_prepare()函數中,先通過pm_prepare_console();給suspend分配一個虛擬終端來輸出信息,再廣播一個系統進入suspend的通報,關閉用戶態的helper進程,然后調用suspend_freeze_processes()來凍結進程,最后會嘗試釋放一些內存。
staticintsuspend_prepare(void)
{
interror;
unsignedintfree_pages;
if(!suspend_ops||!suspend_ops->enter)
return-EPERM;
pm_prepare_console();
error=pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if(error)
gotoFinish;
error=usermodehelper_disable();
if(error)
gotoFinish;
if(suspend_freeze_processes()){
error=-EAGAIN;
gotoThaw;
}
free_pages=global_page_state(NR_FREE_PAGES);
if(free_pages
pr_debug("PM:freesomememory\n");
shrink_all_memory(FREE_PAGE_NUMBER-free_pages);
if(nr_free_pages()
error=-ENOMEM;
printk(KERN_ERR"PM:Noenoughmemory\n");
}
}
if(!error)
return0;
Thaw:
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
returnerror;
}
在suspend_freeze_processes()函數中調用了freeze_processes()函數,而freeze_processes()函數中又調用了try_to_freeze_tasks()來完成凍結任務。在凍結過程中,會判斷當前進程是否有wake_lock,若有,則凍結失敗,函數會放棄凍結。
staticinttry_to_freeze_tasks(boolsig_only)
{
structtask_struct*g,*p;
unsignedlongend_time;
unsignedinttodo;
structtimevalstart,end;
u64elapsed_csecs64;
unsignedintelapsed_csecs;
unsignedintwakeup=0;
do_gettimeofday(&start);
end_time=jiffies+TIMEOUT;
do{
todo=0;
read_lock(&tasklist_lock);
do_each_thread(g,p){
if(frozen(p)||!freezeable(p))
continue;
if(!freeze_task(p,sig_only))
continue;
if(!task_is_stopped_or_traced(p)&&
!freezer_should_skip(p))
todo++;
}while_each_thread(g,p);
read_unlock(&tasklist_lock);
yield();
if(todo&&has_wake_lock(WAKE_LOCK_SUSPEND)){
wakeup=1;
break;
}
if(time_after(jiffies,end_time))
break;
}while(todo);
do_gettimeofday(&end);
elapsed_csecs64=timeval_to_ns(&end)-timeval_to_ns(&start);
do_div(elapsed_csecs64,NSEC_PER_SEC/100);
elapsed_csecs=elapsed_csecs64;
if(todo){
if(wakeup){
printk("\n");
printk(KERN_ERR"Freezingof%saborted\n",
sig_only?"userspace":"tasks");
}
else{
printk("\n");
printk(KERN_ERR"Freezingoftasksfailedafter%d.dseconds"
"(%dtasksrefusingtofreeze):\n",
elapsed_csecs/100,elapsed_csecs%100,todo);
show_state();
}
read_lock(&tasklist_lock);
do_each_thread(g,p){
task_lock(p);
if(freezing(p)&&!freezer_should_skip(p))
printk(KERN_ERR"%s\n",p->comm);
cancel_freezing(p);
task_unlock(p);
}while_each_thread(g,p);
read_unlock(&tasklist_lock);
}else{
printk("(elapsed%d.dseconds)",elapsed_csecs/100,
elapsed_csecs%100);
}
returntodo?-EBUSY:0;
}
到現在,所有的進程(也包括workqueue/kthread)都已經停止了,內核態進程有可能在停止的時候握有一些信號量,所以如果這時候在外設里面去解鎖這個信號量有可能會發生死鎖,所以在外設suspend()函數里面作lock/unlock鎖要非常小心,建議不要在外設的suspend()里面等待鎖。而且suspend的過程中,有一些log是無法輸出的,所以一旦出現問題,非常難調試。
回到enter_state()函數中,再凍結進程完成后,調用suspend_devices_and_enter()函數讓外設進入休眠。該函數中,首先休眠串口(之后不能再顯示log,解決方法為在kernel配置選項的cmd_line中,添加”no_console_suspend”選項),再通過device_suspend()函數調用各驅動的suspend函數。
當外設進入休眠后,suspend_ops->prepare()被調用,suspend_ops是板級的PM操作(本文中粉紅色的函數,依賴于具體的平臺),以s3c6410為例,其注冊在linux_source/arch/arm/plat-s3c64xx/pm.c中,只定義了suspend_ops->enter()函數。
staticstructplatform_suspend_opss3c6410_pm_ops={
.enter=s3c6410_pm_enter,
.valid=suspend_valid_only_mem,
};
接下來,多CPU中的非啟動CPU被關閉。
intsuspend_devices_and_enter(suspend_state_tstate)
{
interror;
if(!suspend_ops)
return-ENOSYS;
if(suspend_ops->begin){
error=suspend_ops->begin(state);
if(error)
gotoClose;
}
suspend_console();
suspend_test_start();
error=device_suspend(PMSG_SUSPEND);
if(error){
printk(KERN_ERR"PM:Somedevicesfailedtosuspend\n");
gotoRecover_platform;
}
suspend_test_finish("suspenddevices");
if(suspend_test(TEST_DEVICES))
gotoRecover_platform;
if(suspend_ops->prepare){
error=suspend_ops->prepare();
if(error)
gotoResume_devices;
}
if(suspend_test(TEST_PLATFORM))
gotoFinish;
error=disable_nonboot_cpus();
if(!error&&!suspend_test(TEST_CPUS))
suspend_enter(state);
enable_nonboot_cpus();
Finish:
if(suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
suspend_test_start();
device_resume(PMSG_RESUME);
suspend_test_finish("resumedevices");
resume_console();
Close:
if(suspend_ops->end)
suspend_ops->end();
returnerror;
Recover_platform:
if(suspend_ops->recover)
suspend_ops->recover();
gotoResume_devices;
}
接下來suspend_enter()被調用,該函數首先關閉IRQ,然后調用device_power_down(),它會調用suspend_late()函數,這個函數是系統真正進入休眠最后調用的函數,通常會在這個函數中作最后的檢查,接下來休眠所有的系統設備和總線。最后調用suspend_pos->enter()來使CPU進入省電狀態。這時候,整個休眠過程完成,代碼的執行也就停在這里了。
staticintsuspend_enter(suspend_state_tstate)
{
interror=0;
device_pm_lock();
#ifdefCONFIG_CPU_FREQ
cpufreq_get_cpufreq_name(0);
strcpy(governor_name,cpufreq_governor_name);
if(strnicmp(governor_name,userspace_governor,CPUFREQ_NAME_LEN)){
cpufreq_set_policy(0,"performance");
}
#endif
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
if((error=device_power_down(PMSG_SUSPEND))){
printk(KERN_ERR"PM:Somedevicesfailedtopowerdown\n");
gotoDone;
}
error=sysdev_suspend(PMSG_SUSPEND);
if(!error){
if(!suspend_test(TEST_CORE))
error=suspend_ops->enter(state);
sysdev_resume();
}
device_power_up(PMSG_RESUME);
Done:
arch_suspend_enable_irqs();
#ifdefCONFIG_CPU_FREQ
if(strnicmp(governor_name,userspace_governor,CPUFREQ_NAME_LEN)){
cpufreq_set_policy(0,governor_name);
}
#endif
BUG_ON(irqs_disabled());
device_pm_unlock();
returnerror;
}
在suspend_pos->enter()所對應的函數中,代碼最終停止在pm_cpu_sleep();處。
staticints3c6410_pm_enter(suspend_state_tstate)
{
……
s3c6410_pm_do_save(gpio_save,ARRAY_SIZE(gpio_save));
s3c6410_pm_do_save(irq_save,ARRAY_SIZE(irq_save));
s3c6410_pm_do_save(core_save,ARRAY_SIZE(core_save));
s3c6410_pm_do_save(sromc_save,ARRAY_SIZE(sromc_save));
__raw_writel(__raw_readl(S3C_WAKEUP_STAT),S3C_WAKEUP_STAT);
#ifdefCONFIG_MACH_SMDK6410
__raw_writel(0xffffff00,S3C_NORMAL_CFG);
__raw_writel(0xffffffff,S3C_HCLK_GATE);
__raw_writel(0xffffffff,S3C_PCLK_GATE);
__raw_writel(0xffffffff,S3C_SCLK_GATE);
……
if(s3c6410_cpu_save(regs_save)==0){
flush_cache_all();
pm_cpu_sleep();
}
cpu_init();
__raw_writel(s3c_eint_mask_val,S3C_EINT_MASK);
s3c6410_pm_do_restore_core(core_save,ARRAY_SIZE(core_save));
s3c6410_pm_do_restore(sromc_save,ARRAY_SIZE(sromc_save));
……
}
staticintsuspend_enter(suspend_state_tstate)
{
interror=0;
device_pm_lock();
#ifdefCONFIG_CPU_FREQ
cpufreq_get_cpufreq_name(0);
strcpy(governor_name,cpufreq_governor_name);
if(strnicmp(governor_name,userspace_governor,CPUFREQ_NAME_LEN)){
cpufreq_set_policy(0,"performance");
}
#endif
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
if((error=device_power_down(PMSG_SUSPEND))){
printk(KERN_ERR"PM:Somedevicesfailedtopowerdown\n");
gotoDone;
}
error=sysdev_suspend(PMSG_SUSPEND);
if(!error){
if(!suspend_test(TEST_CORE))
error=suspend_ops->enter(state);//suspend過程完成處
sysdev_resume();
}
device_power_up(PMSG_RESUME);
Done:
arch_suspend_enable_irqs();
#ifdefCONFIG_CPU_FREQ
if(strnicmp(governor_name,userspace_governor,CPUFREQ_NAME_LEN)){
cpufreq_set_policy(0,governor_name);
}
#endif
BUG_ON(irqs_disabled());
device_pm_unlock();
returnerror;
}
然后回到suspend_devices_and_enter()函數中,使能休眠時候停止掉的非啟動CPU,繼續喚醒每個設備,使能終端。
intsuspend_devices_and_enter(suspend_state_tstate)
{
interror;
if(!suspend_ops)
return-ENOSYS;
if(suspend_ops->begin){
error=suspend_ops->begin(state);
if(error)
gotoClose;
}
suspend_console();
suspend_test_start();
error=device_suspend(PMSG_SUSPEND);
if(error){
printk(KERN_ERR"PM:Somedevicesfailedtosuspend\n");
gotoRecover_platform;
}
suspend_test_finish("suspenddevices");
if(suspend_test(TEST_DEVICES))
gotoRecover_platform;
if(suspend_ops->prepare){
error=suspend_ops->prepare();
if(error)
gotoResume_devices;
}
if(suspend_test(TEST_PLATFORM))
gotoFinish;
error=disable_nonboot_cpus();
if(!error&&!suspend_test(TEST_CPUS))
suspend_enter(state);//suspend過程完成處
enable_nonboot_cpus();
Finish:
if(suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
suspend_test_start();
device_resume(PMSG_RESUME);
suspend_test_finish("resumedevices");
resume_console();
Close:
if(suspend_ops->end)
suspend_ops->end();
returnerror;
Recover_platform:
if(suspend_ops->recover)
suspend_ops->recover();
gotoResume_devices;
}
當suspend_devices_and_enter()執行完成后,系統外設已經喚醒,但進程依然是凍結的狀態,返回到enter_state函數中,調用suspend_finish()函數。
staticintenter_state(suspend_state_tstate)
{
interror;
if(!valid_state(state))
return-ENODEV;
if(!mutex_trylock(&pm_mutex))
return-EBUSY;
printk(KERN_INFO"PM:Syncingfilesystems...");
sys_sync();
printk("done.\n");
pr_debug("PM:Preparingsystemfor%ssleep\n",pm_states[state]);
error=suspend_prepare();
if(error)
gotoUnlock;
if(suspend_test(TEST_FREEZER))
gotoFinish;
pr_debug("PM:Entering%ssleep\n",pm_states[state]);
error=suspend_devices_and_enter(state);//suspend過程完成處
Finish:
pr_debug("PM:Finishingwakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
returnerror;
}
在suspend_finish()函數中,解凍進程和任務,使能用戶空間helper進程,廣播一個系統從suspend狀態退出的notify,喚醒終端。
staticvoidsuspend_finish(void)
{
suspend_thaw_processes();
usermodehelper_enable();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
當所有的喚醒已經結束以后,用戶進程都已經開始運行了,但沒點亮屏幕,喚醒通常會是以下的幾種原因:
如果是來電,那么Modem會通過發送命令給rild來讓rild通知WindowManager有來電響應,這樣就會遠程調用PowerManagerService來寫”on”到/sys/power/state來調用lateresume(),執行點亮屏幕等操作。
用戶按鍵事件會送到WindowManager中,WindowManager會處理這些按鍵事件,按鍵分為幾種情況,如果按鍵不是喚醒鍵,那么WindowManager會主動放棄wakeLock來使系統進入再次休眠;如果按鍵是喚醒鍵,那么WindowManger就會調用PowerManagerService中的接口來執行lateResume。
當”on”被寫入到/sys/power/state之后,同early_suspend過程,request_suspend_state()被調用,只是執行的工作隊列變為late_resume_work。在late_resume函數中,喚醒調用了early_suspend的設備。
staticDECLARE_WORK(late_resume_work,late_resume);
staticvoidlate_resume(structwork_struct*work)
{
structearly_suspend*pos;
unsignedlongirqflags;
intabort=0;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock,irqflags);
if(state==SUSPENDED)
state&=~SUSPENDED;
else
abort=1;
spin_unlock_irqrestore(&state_lock,irqflags);
if(abort){
if(debug_mask&DEBUG_SUSPEND)
pr_info("late_resume:abort,state%d\n",state);
gotoabort;
}
if(debug_mask&DEBUG_SUSPEND)
pr_info("late_resume:callhandlers\n");
list_for_each_entry_reverse(pos,&early_suspend_handlers,link)
if(pos->resume!=NULL)
pos->resume(pos);
if(debug_mask&DEBUG_SUSPEND)
pr_info("late_resume:done\n");
abort:
mutex_unlock(&early_suspend_lock);
}
關于wake_lock
在上文中,已經介紹了wakelock機制,下面從代碼的角度進行介紹。
wakelock有2種類型,常用為WAKE_LOCK_SUSPEND,作用是防止系統進入睡眠。WAKE_LOCK_IDLE
這種鎖不會影響到系統進入休眠,但是如果這種鎖被持有,那么系統將無法進入idle空閑模式。
enum{
WAKE_LOCK_SUSPEND,
WAKE_LOCK_IDLE,
WAKE_LOCK_TYPE_COUNT
};
Wakelock有加鎖和解鎖2種操作,加鎖有2種方式,第一種是永久加鎖(wake_lock),這種鎖必須手動的解鎖;另一種是超時鎖(wake_lock_timeout),這種鎖在過去指定時間后,會自動解鎖。
voidwake_lock(structwake_lock*lock)
{
wake_lock_internal(lock,0,0);
}
voidwake_lock_timeout(structwake_lock*lock,longtimeout)
{
wake_lock_internal(lock,timeout,1);
}
對于wakelock,timeout=has_timeout=0;直接加鎖后,然后退出;
staticvoidwake_lock_internal(
structwake_lock*lock,longtimeout,inthas_timeout)
{
inttype;
unsignedlongirqflags;
longexpire_in;
spin_lock_irqsave(&list_lock,irqflags);
type=lock->flags&WAKE_LOCK_TYPE_MASK;
BUG_ON(type>=WAKE_LOCK_TYPE_COUNT);
BUG_ON(!(lock->flags&WAKE_LOCK_INITIALIZED));
#ifdefCONFIG_WAKELOCK_STAT
if(type==WAKE_LOCK_SUSPEND&&wait_for_wakeup){
if(debug_mask&DEBUG_WAKEUP)
pr_info("wakeupwakelock:%s\n",lock->name);
wait_for_wakeup=0;
lock->stat.wakeup_count++;
}
if((lock->flags&WAKE_LOCK_AUTO_EXPIRE)&&
(long)(lock->expires-jiffies)<=0){
wake_unlock_stat_locked(lock,0);
lock->stat.last_time=ktime_get();
}
#endif
if(!(lock->flags&WAKE_LOCK_ACTIVE)){
lock->flags|=WAKE_LOCK_ACTIVE;
#ifdefCONFIG_WAKELOCK_STAT
lock->stat.last_time=ktime_get();
#endif
}
list_del(&lock->link);
if(has_timeout){
if(debug_mask&DEBUG_WAKE_LOCK)
pr_info("wake_lock:%s,type%d,timeout%ld.lu\n",
lock->name,type,timeout/HZ,
(timeout%HZ)*MSEC_PER_SEC/HZ);
lock->expires=jiffies+timeout;
lock->flags|=WAKE_LOCK_AUTO_EXPIRE;
list_add_tail(&lock->link,&active_wake_locks[type]);
}else{
if(debug_mask&DEBUG_WAKE_LOCK)
pr_info("wake_lock:%s,type%d\n",lock->name,type);
lock->expires=LONG_MAX;
lock->flags&=~WAKE_LOCK_AUTO_EXPIRE;
list_add(&lock->link,&active_wake_locks[type]);
}
if(type==WAKE_LOCK_SUSPEND){
current_event_num++;
#ifdefCONFIG_WAKELOCK_STAT
if(lock==&main_wake_lock)
update_sleep_wait_stats_locked(1);
elseif(!wake_lock_active(&main_wake_lock))
update_sleep_wait_stats_locked(0);
#endif
if(has_timeout)
expire_in=has_wake_lock_locked(type);
else
expire_in=-1;
if(expire_in>0){
if(debug_mask&DEBUG_EXPIRE)
pr_info("wake_lock:%s,startexpiretimer,"
"%ld\n",lock->name,expire_in);
mod_timer(&expire_timer,jiffies+expire_in);
}else{
if(del_timer(&expire_timer))
if(debug_mask&DEBUG_EXPIRE)
pr_info("wake_lock:%s,stopexpiretimer\n",
lock->name);
if(expire_in==0)
queue_work(suspend_work_queue,&suspend_work);
}
}
spin_unlock_irqrestore(&list_lock,irqflags);
}
而對于wake_lock_timeout,在經過timeout時間后,才加鎖。再判斷當前持有wakelock時,啟動另一個定時器,在expire_timer的回調函數中再次判斷是否持有wakelock。
staticvoidexpire_wake_locks(unsignedlongdata)
{
longhas_lock;
unsignedlongirqflags;
if(debug_mask&DEBUG_EXPIRE)
pr_info("expire_wake_locks:start\n");
spin_lock_irqsave(&list_lock,irqflags);
if(debug_mask&DEBUG_SUSPEND)
print_active_locks(WAKE_LOCK_SUSPEND);
has_lock=has_wake_lock_locked(WAKE_LOCK_SUSPEND);
if(debug_mask&DEBUG_EXPIRE)
pr_info("expire_wake_locks:done,has_lock%ld\n",has_lock);
if(has_lock==0)
queue_work(suspend_work_queue,&suspend_work);
spin_unlock_irqrestore(&list_lock,irqflags);
}
staticDEFINE_TIMER(expire_timer,expire_wake_locks,0,0);
在wakelock中,有2個地方可以讓系統從early_suspend進入suspend狀態。分別是:
l在wake_unlock中,解鎖之后,若沒有其他的wakelock,則進入suspend。
l在超時鎖的定時器超時后,定時器的回調函數,會判斷有沒有其他的wakelock,若沒有,則進入suspend。