本文參考:
http://www.wowotech.net/pm_subsystem/wakelocks.html
http://www.wowotech.net/pm_subsystem/wakelocks.html
http://www.wowotech.net/pm_subsystem/wakelocks.html
文章中代碼來源于:Mtk8173竭缝,android 6.0
Linux 3.4內核開始的變化
(1)linux 3.4內核PM使用了新的wakeup_source機制媳叨,摒棄了繁雜的wake_lock機制
之前android一直是基于Linux加入了wake_lock機制來阻止系統(tǒng)休眠缭受,后來Linux 3.4內核加入了wakeup_source來管理缓熟,安卓4.4跟著升級內核也就摒棄了自己的繁雜的wake_lock機制,在對上層接口并不改變,在內核wake_lock實現(xiàn)直接基于wakeup_source來實現(xiàn)的。當然也會帶來debug上的一些問題,比如以前的wake_lock自身帶有強大的debug信息减拭,那么我們在調試的時候可以自己看見dmesg中默認打印active wake lock XXX,很直觀來辨別需要休眠的時候那個wake lock有問題阻止了休眠区丑。這個需要我們自己來完善拧粪。個人認為改進很大修陡,現(xiàn)在使用了autosleep機制,只要不存在任何active wakeup_source了可霎,系統(tǒng)自動休眠魄鸦,當有active wake_source自動block住,個人認為休眠更及時癣朗,非休眠時間在減少拾因,同時不會消耗額外的資源。使用基于queue work與進程block來管理suspend旷余。還有這里的wakeup_source個人覺得應該叫keepawake_source或者stayawake_souce绢记,畢竟系統(tǒng)的喚醒也就是cpu的再次運行是由中斷喚醒的而不是wakeup_source。
(2)安卓4.4還有一個重大改變就是去除了early suspend機制改為fb event通知機制正卧。那么現(xiàn)在就只有suspend與resume,runtime suspend與runtime resume了蠢熄。
(1) wakelock
說起android的休眠機制,就不得不說說wakelock,下面我們詳細介紹下waklock機制的歷史炉旷。
Android和Linux的恩恩怨怨
wakelocks最初出現(xiàn)在Android為linux kernel打的一個補丁集上签孔,該補丁集實現(xiàn)了一個名稱為“wakelocks”的系統(tǒng)調用,該系統(tǒng)調用允許調用者阻止系統(tǒng)進入低功耗模式(如idle窘行、suspend等)饥追。同時,該補丁集更改了Linux kernel原生的電源管理執(zhí)行過程(kernel/power/main.c中的state_show和state_store)罐盔,轉而執(zhí)行自定義的state_show判耕、state_store。
這種做法是相當不規(guī)范的翘骂,它是典型的只求實現(xiàn)功能,不擇手段帚豪。就像國內很多的Linux開發(fā)團隊碳竟,要實現(xiàn)某個功能,都不去弄清楚kernel現(xiàn)有的機制狸臣、框架莹桅,牛逼哄哄的猛干一番。最后功能是實現(xiàn)了烛亦,可都不知道重復造了多少輪子诈泼,浪費了多少資源。到此打住煤禽,Android的開發(fā)者不會這么草率铐达,他們推出wakelocks機制一定有一些苦衷,我們就不評論了檬果。
但是瓮孙,雖然有苦衷唐断,kernel的開發(fā)者可是有原則的,死活不讓這種機制合并到kernel分支(換誰也不讓昂伎佟)脸甘,直到kernel自身的wakeup events framework成熟后,這種僵局才被打破偏灿。因為Android開發(fā)者想到了一個壞點子:不讓合并就不讓合并唄丹诀,我用你的機制(wakeup source),再實現(xiàn)一個就是了翁垂。至此铆遭,全新的wakelocks出現(xiàn)了。
所以wakelocks有兩個沮峡,早期Android版本的wakelocks幾乎已經銷聲匿跡了疚脐,不仔細找還真找不到它的source code(這里有一個鏈接,但愿讀者看到時還有效邢疙,drivers/android/power.c)棍弄。本文不打算翻那本舊黃歷,所以就focus在新的wakelocks上(drivers/power/wakelock.c疟游,較新的kernel都支持)呼畸。
Android wakelocks
雖說不翻舊黃歷了,還是要提一下Android wakelocks的功能颁虐,這樣才能知道kernel wakelocks要做什么蛮原。總的來說另绩,Android wakelocks提供的功能包括:
1)一個sysfs文件:/sys/power/wake_lock儒陨,用戶程序向文件寫入一個字符串,即可創(chuàng)建一個wakelock笋籽,該字符串就是wakelock的名字蹦漠。該wakelock可以阻止系統(tǒng)進入低功耗模式。
2)一個sysfs文件::/sys/power/wake_unlock车海,用戶程序向文件寫入相同的字符串笛园,即可注銷一個wakelock。
3)當系統(tǒng)中所有的wakelock都注銷后侍芝,系統(tǒng)可以自動進入低功耗狀態(tài)研铆。
4)向內核其它driver也提供了wakelock的創(chuàng)建和注銷接口,允許driver創(chuàng)建wakelock以阻止睡眠州叠、注銷wakelock以允許睡眠棵红。
Kernel wakelocks的功能
對比Android wakelocks要實現(xiàn)的功能,Linux kernel的方案是:
1)允許driver創(chuàng)建wakelock以阻止睡眠留量、注銷wakelock以允許睡眠:已經由新的wakeup source機制取代窄赋。
2)當系統(tǒng)中所有的wakelock都注銷后哟冬,系統(tǒng)可以自動進入低功耗狀態(tài):由autosleep實現(xiàn)。
3)wake_lock和wake_unlock功能:由本文所描述的kernel wakelocks實現(xiàn)忆绰,其本質就是將wakeup source開發(fā)到用戶空間訪問浩峡。
Kernel wakelocks在電源管理中的位置
相比Android wakelocks,Kernel wakelocks的實現(xiàn)非常簡單(簡單的才是最好的)错敢,就是在PM core中增加一個wakelock模塊(kernel/power/wakelock.c)翰灾,該模塊依賴wakeup events framework提供的wakeup source機制,實現(xiàn)用戶空間的wakeup source(就是wakelocks)稚茅,并通過PM core main模塊纸淮,向用戶空間提供兩個同名的sysfs文件,wake_lock和wake_unlock亚享。
/sys/power/wake_lock & /sys/power/wake_unlock
從字面意思上咽块,新版的wake_lock和wake_unlock和舊版的一樣,都是用于創(chuàng)建和注銷wakelock侈沪。從應用開發(fā)者的角度,確實可以這樣理解晚凿。但從底層實現(xiàn)的角度,卻完全不是一回事歼秽。
Android的wakelock应役,真是一個lock燥筷,用戶程序創(chuàng)建一個wakelock,就是在系統(tǒng)suspend的路徑上加了一把鎖肆氓,注銷就是解開這把鎖滥比。直到suspend路徑上所有的鎖都解開時做院,系統(tǒng)才可以suspend。
而Kernel的wakelock濒持,是基于wakeup source實現(xiàn)的键耕,因此創(chuàng)建wakelock的本質是在指定的wakeup source上activate一個wakeup event,注銷wakelock的本質是deactivate wakeup event柑营。因此屈雄,/sys/power/wake_lock和/sys/power/wake_unlock兩個sysfs文件的的功能就是:
寫wake_lock(以wakelock name和timeout時間<可選>為參數(shù)),相當于以wakeup source為參數(shù)調用__pm_stay_awake(或者__pm_wakeup_event)官套,即activate wakeup event酒奶;
寫wake_unlock(以wakelock name為參數(shù))蚁孔,相當于以wakeup source為參數(shù),調用__pm_relax惋嚎;
讀wake_lock杠氢,獲取系統(tǒng)中所有的處于active狀態(tài)的wakelock列表(也即wakeup source列表)
讀wake_unlock,返回系統(tǒng)中所有的處于非active狀態(tài)的wakelock信息(也即wakeup source列表)另伍。
這兩個sysfs文件在kernel/power/main.c中實現(xiàn)鼻百,如下:
static ssize_t wake_lock_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return pm_show_wakelocks(buf, true);
}
static ssize_t wake_lock_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = pm_wake_lock(buf);
return error ? error : n;
}
power_attr(wake_lock);
static ssize_t wake_unlock_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return pm_show_wakelocks(buf, false);
}
static ssize_t wake_unlock_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t n)
{
int error = pm_wake_unlock(buf);
return error ? error : n;
}
power_attr(wake_unlock);
#endif /* CONFIG_PM_WAKELOCKS */
1)wakelocks功能不是linux kernel的必選功能,可以通過CONFIG_PM_WAKELOCKS開關摆尝。
2)wake_lock的寫接口温艇,直接調用pm_wake_lock;wake_unlock的寫接口堕汞,直接調用pm_wake_unlock勺爱;它們的讀接口,直接調用pm_show_wakelocks接口(參數(shù)不同)讯检。這三個接口均在kernel/power/wakelock.c中實現(xiàn)琐鲁。
pm_wake_lock
pm_wake_lock位于kernel\power\wakelock.c中,用于上報一個wakeup event(從另一個角度视哑,就是阻止系統(tǒng)suspend)绣否,代碼如下:
int pm_wake_lock(const char *buf)
{
const char *str = buf;
struct wakelock *wl;
u64 timeout_ns = 0;
size_t len;
int ret = 0;
if (!capable(CAP_BLOCK_SUSPEND))
return -EPERM;
while (*str && !isspace(*str))
str++;
len = str - buf;
if (!len)
return -EINVAL;
if (*str && *str != '\n') {
/* Find out if there's a valid timeout string appended. */
ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
if (ret)
return -EINVAL;
}
mutex_lock(&wakelocks_lock);
wl = wakelock_lookup_add(buf, len, true);
if (IS_ERR(wl)) {
ret = PTR_ERR(wl);
goto out;
}
if (timeout_ns) {
u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
do_div(timeout_ms, NSEC_PER_MSEC);
__pm_wakeup_event(&wl->ws, timeout_ms);
} else {
__pm_stay_awake(&wl->ws);
}
wakelocks_lru_most_recent(wl);
out:
mutex_unlock(&wakelocks_lock);
return ret;
}
a)輸入?yún)?shù)為一個字符串,如"wake_lock_test 1000”挡毅,該字符串指定上報wakeup event的wakelock name蒜撮,可以在name后用空格隔開,添加一個時間值(單位為ns)跪呈,表示該event的timeout值段磨。
b)調用capable,檢查當前進程是否具備阻止系統(tǒng)suspend的權限耗绿。
capable是Linux security子系統(tǒng)提供的一個接口苹支,用于權限判斷。我們說過误阻,power是系統(tǒng)的核心資源债蜜,理應由OS全權管理,但wakelock違反了這一原則究反,將阻止系統(tǒng)睡眠的權利給了用戶空間。這樣一來精耐,用戶空間程序將可以隨心所欲的占用power資源,特別是用戶態(tài)的程序員卦停,天生對資源占用不敏感(這是對的)恼蓬,就導致該接口有被濫用的風險。不過還好处硬,通過系統(tǒng)的權限管理機制,可以改善這種狀態(tài)(其實不是改善郁油,而是矛盾轉移攀痊,很有可能把最終的裁決權交給用戶,太糟糕了9毒丁)。</font>
c)解析字符串棘街,將timeout值(有的話)保存在timeout_ns中,解析name長度(len)遭殉,并將name保存在原來的buf中。
d)調用wakelock_lookup_add接口痹愚,查找是否有相同name的wakelock。如果有拯腮,直接返回wakelock的指針蚁飒;如果沒有,分配一個wakelock淮逻,同時調用wakeup events framework提供的接口,創(chuàng)建該wakelock對應的wakeup source結構爬早。
e)如果指定timeout值,以wakelock的wakeup source指針為參數(shù)凸椿,調用__pm_wakeup_event接口翅溺,上報一個具有時限的wakeup events髓抑;否則优幸,調用__pm_stay_awake,上報一個沒有時限的wakeup event羹饰。