三、 kernel層源碼解析- wakelock的重要地位
wakelock在android的休眠喚醒機制中扮演著及其重要的角色,主要源碼位於文件:
kernel/kernel/power/wakelock.c
kernel/include/linux/wakelock.h中。
wakelocks_init()函數所做的工作是整個wakelock可以工作起來的基礎,所有這裡先說說這個函數。
static int __init wakelocks_init (void)
{
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
INIT_LIST_HEAD(&active_wake_locks[i]);
//初始化active_wake_locks數組中的兩個類型鎖鍊錶: WAKE_LOCK_SUSPEND,WAKE_LOCK_IDLE
#ifdef CONFIG_WAKELOCK_STAT // defined
wake_lock_init(& deleted_wake_locks , WAKE_LOCK_SUSPEND,
"deleted_wake_locks");
//初始化wakelock deleted_wake_locks,同時將其加入到非活動鎖鍊錶中
#endif
wake_lock_init(& main_wake_lock , WAKE_LOCK_SUSPEND, "main");
wake_lock_init(& sys_sync_wake_lock , WAKE_LOCK_SUSPEND, "sys_sync");
wake_lock(&main_wake_lock);
wake_lock_init(& unknown_wakeup , WAKE_LOCK_SUSPEND, "unknown_wakeups");
//初始化wakelock: main, sys_sync, unknown_wakeups,同時將其加入到非活動鎖鍊錶中
//給main_wake_lock加鎖
ret = platform_device_register(&power_device) ;
if (ret) {
pr_err("[wakelocks_init]: platform_device_register failed/n");
goto err_platform_device_register;
}
ret = platform_driver_register(&power_driver) ;
if (ret) {
pr_err("[wakelocks_init]: platform_driver_register failed/n");
goto err_platform_driver_register;
}
//新建工作隊列和工作者內核線程: sys_sync_work_queue, fs_sync
// suspend_work_queue, suspend
sys_sync_work_queue = create_singlethread_workqueue("fs_sync");
if (sys_sync_work_queue == NULL) {
pr_err("[wakelocks_init] fs_sync workqueue create failed/n");
}
suspend_work_queue = create_singlethread_workqueue("suspend");
if (suspend_work_queue == NULL) {
ret = -ENOMEM;
goto err_suspend_work_queue;
}
#ifdef CONFIG_WAKELOCK_STAT
proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);
//創建proc接口
#endif
return 0;
err_suspend_work_queue:
platform_driver_unregister(&power_driver);
err_platform_driver_register:
platform_device_unregister(&power_device);
err_platform_device_register:
wake_lock_destroy(&unknown_wakeup);
wake_lock_destroy(&main_wake_lock);
#ifdef CONFIG_WAKELOCK_STAT
wake_lock_destroy(&deleted_wake_locks);
#endif
return ret;
}
可以看到該初始化函數中新建了幾個wakelock: deleted_wake_locks、main_wake_lock、sys_sync_wake_lock、unknown_wakeup,他們全部都是WAKE_LOCK_SUSPEND類型的wakelock,說到這裡不得不提到wakelock的兩種類型了:
1. WAKE_LOCK_SUSPEND –這種鎖如果被某個task持有,那麼系統將無法進入休眠。
2. WAKE_LOCK_IDLE –這種鎖不會影響到系統進入休眠,但是如果這種鎖被持有,那麼系統將無法進入idle空閒模式。
不過常用的所類型還是WAKE_LOCK_SUSPEND,包括userwakelock.c提供給用戶空間的新建wakelock的接口,都是建立的第一種鎖。另外系統為了分開管理這兩種不同類型的鎖,建立了兩個鍊錶來統一鏈接不同類型的鎖:active_wake_locks[],這個是具有兩個鍊錶頭的數組,元素0是掛接WAKE_LOCK_SUSPEND類型的鎖,而元素1就是掛接WAKE_LOCK_IDLE類型的wakelock了。
接著上面說,這個初始化函數新建這些鎖之後,直接將主鎖(main_wake_lock)給上鎖了,其餘都是非鎖狀態。新建wakelock使用函數wake_lock_init(),該函數設置鎖的名字,類型,最後將新建的鎖掛接到一個專門鏈接這些非鎖狀態的鍊錶inactive_locks上(新建的wakelock初期都是出於非鎖狀態的,除非顯示調用函數wake_lock來上鎖)。接著如果使用函數wake_lock()來給特定的wakelock上鎖的話,會將該鎖從鍊錶inactive_locks上移動到對應類型的專用鍊錶上active_wake_locks[type]上。
wakelock有兩種形式的鎖:超時鎖和非超時鎖,這兩種形式的鎖都是使用函數wake_lock_init()來初始化,只是在上鎖的時候會有一點點差別,超時鎖使用函數wake_lock_timeout(),而非超時鎖使用函數wake_lock(),這個兩個函數會最終調用到同一個函數wake_lock_internal(),該函數依靠傳入的 不同參數來選擇不同的路徑來工作。值得注意的是,非超時鎖必須手工解鎖,否則係統永遠不能進入睡眠。下面是wake_lock_internal()函數的片段:
if (!(lock->flags & WAKE_LOCK_ACTIVE))
lock->flags |= WAKE_LOCK_ACTIVE; // wakelock狀態為inactive,則更改為active
…
if ( has_timeout ) { // wake_lock_timeout() 會傳入1
if (wakelock_debug_mask & DEBUG_WAKE_LOCK)
pr_info("[wake_lock_internal]: %s, type %d, timeout %ld.%03lu/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]) ;
}
// acquire a non-timeout wakelock添加一個非超時鎖
else { // wake_lock () 會傳入0
if (wakelock_debug_mask & DEBUG_WAKE_LOCK)
pr_info("[wake_lock_internal]: %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]);
//將剛剛設置的非超時鎖加到對應類型的活動鎖鍊錶中
}
解鎖的時候,這兩種形式的鎖所使用函數都是一樣了:wake_unlock(),該函數中會首先作如下操作:
lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
//清除鎖活動標誌和自動超時標誌
list_del(&lock->link); // 從鎖對應的活動鍊錶上摘除
list_add(&lock->link, &inactive_locks);
//將unlock的鎖掛接到非活動鍊錶inactive_locks上
前面已經說了只有類型為WAKE_LOCK_SUSPEND的wakelock被上鎖才會阻止系統進入suspend,那麼也就是說只要鍊錶active_wake_locks[WAKE_LOCK_SUSPEND]為NULL,那麼系統就可以執行suspend的流程了。Android對linux的改造,讓其可以在三種情況下進入linux的標準suspend的流程:
1. wake_unlock(),這個應該是最容易想到的,只要係統有對WAKE_LOCK_SUSPEND類型的wakelock解鎖的動作,都有可能會進入suspend流程開始休眠,為什麼是有可能呢?因為可能還有超時鎖沒有被超時解鎖。下面看一下代碼片段:
void wake_unlock(struct wake_lock *lock)
{
…
if ( type == WAKE_LOCK_SUSPEND ) //貌似只在處理這個類型的wakelock
{
long has_lock = has_wake_lock_locked(type) ;
//這個函數蠻重要,它來檢查type類型的鍊錶上是否還有鎖被上鎖了。
//其返回值如果是0,說明沒有該類型的鎖被持有了;返回非0表明就是這個類型的活動鍊錶上還存在超時鎖但是沒有非超時鎖了,這個返回值就是當前時間距離最後超時的鎖超時時間的jiffies值;如果返回-1,那表明還有該類型的非超時鎖被持有。
if (wakelock_debug_mask & DEBUG_WAKE_LOCK)
pr_info("[wake_unlock]: has_lock = 0x%x/n" , has_lock);
if ( has_lock > 0 ) {
if (wakelock_debug_mask & DEBUG_EXPIRE)
pr_info("[wake_unlock]: %s, start expire timer, "
"%ld/n", lock->name, has_lock);
mod_timer(&expire_timer, jiffies + has_lock);
//修改定時器的超時值並add該定時器
}
else //已經沒有超時鎖了
{
if ( del_timer(&expire_timer) ) // 刪除定時器
if (wakelock_debug_mask & DEBUG_EXPIRE)
pr_info("[wake_unlock]: %s, stop expire "
"timer/n", lock->name);
if ( has_lock == 0 )
// !=0,表明還有該類型的非超時鎖被持有,現在還不能進入suspend
{
pr_info("[wake_unlock]: (%s) suspend_work_queue suspend_work/n" , lock->name);
queue_work(suspend_work_queue, &suspend_work);
//提交suspend的工作項,開始執行標準linux的suspend流程
}
}
…
}
spin_unlock_irqrestore(&list_lock, irqflags);
}
2. 超時鎖超時之後,定時器的回調函數會執行會查看是否有其他的wakelock,如果沒有,就在這裡讓系統進入睡眠。
static void expire_wake_locks (unsigned long data)
{
long has_lock;
unsigned long irqflags;
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 )
//如果沒有SUSPEND類型的wakelock處於active,那麼將調用suspend
queue_work(suspend_work_queue, &suspend_work);
spin_unlock_irqrestore(&list_lock, irqflags);
}
static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
列出以下一個重要的函數源碼:
static long has_wake_lock_locked (int type)
{
struct wake_lock *lock, *n;
long max_timeout = 0;
BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
if ( lock->flags & WAKE_LOCK_AUTO_EXPIRE ) {
long timeout = lock->expires - jiffies;
if (timeout <= 0)
expire_wake_lock(lock);
else if (timeout > max_timeout)
max_timeout = timeout;
} else
return -1;
}
return max_timeout;
}
3. 這個可能有人覺得匪夷所思,就是在wake_lock{_ _timeout}()函數中,調用了內部函數wake_lock_internal()。這裡只有在對超時鎖上鎖的時候才有可能進入休眠,如果對一個費超時鎖上鎖的話,那麼就沒有必要去檢查活動鍊錶了。
static void wake_lock_internal(
struct wake_lock *lock, long timeout, int has_timeout)
{
…
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) // 超時鎖的時候傳進來的是1
expire_in = has_wake_lock_locked(type);
//檢查當前鎖類型鍊錶上是否還有鎖處於active的狀態,無返回0
else
expire_in = -1;
//如果是非超時鎖的話,這裡直接賦值-1,省去了活動鍊錶檢查步驟了
if ( expire_in > 0 ) {
if (debug_mask & DEBUG_EXPIRE)
pr_info("wake_lock: %s, start expire timer, "
"%ld/n", lock->name, expire_in);
// modify the time wakelock is expired
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 ) //沒有鎖處於active狀態後,準備調用suspend了
{
pr_info("[wake_lock]: suspend_work_queue suspend_work/n ");
queue_work(suspend_work_queue, &suspend_work) ;
}
}
}
spin_unlock_irqrestore(&list_lock, irqflags);
}
下面是suspend的工作項,經過上面三種情況的檢查,ok之後將會提交該工作項給工作隊列suspend_work_queue,如下:
static void suspend(struct work_struct *work)
{
int ret;
int entry_event_num;
// there are still some wakelock
if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
if (wakelock_debug_mask & DEBUG_SUSPEND)
pr_info("[suspend]: abort suspend/n");
return;
}
entry_event_num = current_event_num;
sys_sync();
if (debug_mask & DEBUG_SUSPEND)
pr_info("suspend: enter suspend/n");
ret = pm_suspend(requested_suspend_state) ;
// requested_suspend_state這個全局變量在函數request_suspend_state()中被設置,也就是執行了eraly suspend或者late resume之後,主要是為suspend保留請求的省電狀態。
if (debug_mask & DEBUG_EXIT_SUSPEND) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec, &tm);
pr_info("suspend: exit suspend, ret = %d "
"(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)/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_suspend returned with no event/n");
wake_lock_timeout(&unknown_wakeup, HZ / 2);
}
}
static DECLARE_WORK(suspend_work, suspend);
@kernel/kernel/power/suspend.c
int pm_suspend (suspend_state_t state)
{
if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
return enter_state(state);
//標準linux的suspend流程函數
return -EINVAL;
}
EXPORT_SYMBOL(pm_suspend);
Wakelock的機制被文件userwakelock.c中的code封裝成了sys的接口sys/power/wake_lock和sys/power/wake_unlock文件,那麼上層如果需要新建wakelock或者註銷wakelock,或者是解鎖wakelock,都是操作這兩個sys接口文件。