Andorid性能優(yōu)化之功耗-Linux內核

本文參考:
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羹饰。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末碳却,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子昼浦,更是在濱河造成了極大的恐慌,老刑警劉巖关噪,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異建钥,居然都是意外死亡,警方通過查閱死者的電腦和手機熊经,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門置蜀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盯荤,你說我怎么就攤上這事∏锍樱” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵绍哎,是天一觀的道長鞋真。 經常有香客問我,道長,這世上最難降的妖魔是什么繁莹? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任特幔,我火速辦了婚禮,結果婚禮上蚯斯,老公的妹妹穿的比我還像新娘。我一直安慰自己拍嵌,他們只是感情好,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布嵌牺。 她就那樣靜靜地躺著龄糊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪炫惩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天他嚷,我揣著相機與錄音,去河邊找鬼卸耘。 笑死,一個胖子當著我的面吹牛蚣抗,可吹牛的內容都是我干的。 我是一名探鬼主播瓮下,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼讽坏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了路呜?” 一聲冷哼從身側響起织咧,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤漠秋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后膛堤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肥荔,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡朝群,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了姜胖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚜锨,死狀恐怖慢蜓,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情晨抡,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布耘柱,位于F島的核電站,受9級特大地震影響调煎,放射性物質發(fā)生泄漏。R本人自食惡果不足惜汛蝙,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坚洽。 院中可真熱鬧,春花似錦讶舰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹅颊。三九已至,卻和暖如春堪伍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帝雇。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留彻亲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓苞尝,卻偏偏與公主長得像茧痕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子踪旷,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容