前言
Android中有一個(gè)常用的類(lèi)Context污抬,它貫穿著Android生命周期的始終骡尽。通常來(lái)講己沛,中文翻譯為“上下文”,字面理解就是Android運(yùn)行期間前后環(huán)境之間的聯(lián)系厚脉,例如可以通過(guò)它整個(gè)系統(tǒng)運(yùn)行時(shí)必要類(lèi)、常量值等胶惰,在需要的時(shí)候就可以通過(guò)context獲得傻工。
這樣說(shuō)或許還是不太容易理解。我們知道孵滞,Android的底層實(shí)現(xiàn)是Linux的中捆,所以這個(gè)“上下文”跟Linux中的上下文之間,有沒(méi)有什么聯(lián)系呢坊饶?或者說(shuō)理解Linux中的上下文對(duì)理解Android的Context有沒(méi)有幫助呢泄伪?我個(gè)人認(rèn)為還是有的。所以本文先從Linux的角度學(xué)習(xí)一個(gè)它的“上下文”的原理匿级,隨后再通過(guò)源碼的方式了解Android中的Context蟋滴。
Linux的上下文切換
為什么要進(jìn)行上下文切換
Linux是一個(gè)多任務(wù)系統(tǒng),可以支持大于cpu數(shù)量的任務(wù)同時(shí)運(yùn)行根蟹。不過(guò)這并不是真正的“同時(shí)”脓杉,只不過(guò)cpu會(huì)在極短的時(shí)間片上切換任務(wù),使得用戶(hù)看起來(lái)這些任務(wù)是在同時(shí)運(yùn)行简逮。
既然cpu要不斷地進(jìn)行任務(wù)切換球散,那么每個(gè)任務(wù)都有必要的信息需要保存和恢復(fù),否則下次切換回運(yùn)行時(shí)就不知道之前切換出去時(shí)的狀態(tài)了散庶。對(duì)于這種這些需要記錄蕉堰、保存的環(huán)境信息,我們稱(chēng)他們?yōu)椤吧舷挛摹薄?/p>
上下文的內(nèi)容
- cpu寄存器:容量小悲龟、速度快
- 程序計(jì)數(shù)器sp:指令位置
有了這兩個(gè)屋讶,就可以知道一個(gè)任務(wù)從哪里加載、運(yùn)行须教,從而使得原來(lái)的任務(wù)的狀態(tài)不受影響皿渗,可以繼續(xù)運(yùn)行斩芭。
因此,cpu對(duì)任務(wù)的切換乐疆,就是不斷地保存划乖、恢復(fù)上下文的過(guò)程,也叫做上下文切換挤土。我們知道琴庵,Linux中存在進(jìn)程和線程兩個(gè)概念,在程序運(yùn)行時(shí)也會(huì)不斷地進(jìn)行進(jìn)程仰美、線程的切換迷殿,每次切換都會(huì)涉及到上下文。除此之外咖杂,計(jì)算機(jī)中還存在硬件中斷庆寺,用以執(zhí)行一些重要但耗時(shí)少的任務(wù),因此上下文切換存在三種類(lèi)別:程上下文切換
翰苫、線程上下文切換
和中斷上下文切換
止邮。
進(jìn)程上下文切換
Linux進(jìn)程分為用戶(hù)空間和內(nèi)核空間,內(nèi)核空間權(quán)限最高奏窑,可以訪問(wèn)所有資源导披,而用戶(hù)空間訪問(wèn)權(quán)限受限,必須通過(guò)系統(tǒng)調(diào)用
才能通過(guò)內(nèi)核訪問(wèn)受限資源埃唯。
當(dāng)進(jìn)程在用戶(hù)態(tài)通過(guò)系統(tǒng)調(diào)用調(diào)用內(nèi)核態(tài)資源時(shí)撩匕,cpu會(huì)發(fā)生上下文切換。但這種切換是一個(gè)進(jìn)程內(nèi)部的切換墨叛,所以不會(huì)涉及到虛擬內(nèi)存等問(wèn)題:
- 保存cpu寄存器里原來(lái)用戶(hù)態(tài)的指令位置止毕,將cpu寄存器更新為內(nèi)核態(tài)指令位置,隨后執(zhí)行內(nèi)核態(tài)任務(wù)漠趁;
- 系統(tǒng)調(diào)用結(jié)束后扁凛,cpu恢復(fù)之前保存的用戶(hù)態(tài)指令,切換回用戶(hù)空間闯传,繼續(xù)執(zhí)行谨朝。
而進(jìn)程的上下文切換就不太一樣了,它要比用戶(hù)態(tài)和內(nèi)核態(tài)切換更多了保存進(jìn)程的棧
甥绿、全局變量
字币、虛擬空間
以及內(nèi)核堆棧
和寄存器
等。同理共缕,在加載新進(jìn)程時(shí)洗出,也要把新進(jìn)程的這些信息更新過(guò)來(lái)。注意图谷,進(jìn)程的切換只能發(fā)生在內(nèi)核態(tài)翩活,因?yàn)檫M(jìn)程是由內(nèi)核管理的阱洪,這也是與用戶(hù)、內(nèi)核態(tài)切換不一樣的地方菠镇。
頻繁切換進(jìn)程會(huì)耗費(fèi)大量資源澄峰、浪費(fèi)時(shí)間、效率低下辟犀,導(dǎo)致負(fù)載率升高。
還有一點(diǎn)需要知道绸硕,Linux通過(guò)TLB管理虛擬內(nèi)存與物理內(nèi)存之間的映射關(guān)系堂竟,它是由多個(gè)處理器共享的。因此玻佩,當(dāng)切換進(jìn)程任務(wù)出嘹,導(dǎo)致虛擬內(nèi)存更新,Linux需要及時(shí)更新TLB咬崔,而此時(shí)税稼,不但當(dāng)前cpu不能正常工作,其它c(diǎn)pu也是無(wú)法正常工作的垮斯,需要更新完畢后才可以繼續(xù)郎仆。
上下文切換時(shí)機(jī)
- 用戶(hù)態(tài)和內(nèi)核態(tài)切換:用戶(hù)態(tài)使用系統(tǒng)調(diào)用時(shí),例如需要讀取文件時(shí)兜蠕,使用read()扰肌。
- 進(jìn)程切換:
- 進(jìn)程執(zhí)行完畢:釋放cpu資源,以便就緒隊(duì)列里其他進(jìn)程繼續(xù)執(zhí)行熊杨;
- cpu的時(shí)間片調(diào)度:當(dāng)前進(jìn)程的時(shí)間片執(zhí)行完曙旭,切換其他進(jìn)程,以便使所有進(jìn)程實(shí)現(xiàn)并行執(zhí)行晶府;
- 進(jìn)程系統(tǒng)資源不足:系統(tǒng)掛起進(jìn)程桂躏;
- 當(dāng)前進(jìn)程調(diào)用sleep:進(jìn)程主動(dòng)掛起;
- 執(zhí)行優(yōu)先級(jí)更高的進(jìn)程:
- 硬件中斷:
線程上下文切換
進(jìn)程是資源擁有的基本單位川陆,而線程是系統(tǒng)調(diào)度的基本單位剂习。進(jìn)程為線程提供了虛擬內(nèi)存、全局變量等資源书劝,一個(gè)進(jìn)程內(nèi)的線程共享這些資源进倍。
因此,線程的上下文切換:
- 同一個(gè)進(jìn)程內(nèi)的線程切換:僅僅需要切換線程的私有數(shù)據(jù)购对、寄存器等線程私有資源即可猾昆;
- 不同進(jìn)程內(nèi)的線程切換:由于是不同進(jìn)程,因此跟進(jìn)程切換一樣骡苞,需要更新虛擬內(nèi)存垂蜗、棧楷扬、全局變量、內(nèi)核堆棧贴见、寄存器等資源烘苹。
因此,線程的切換(同進(jìn)程內(nèi))比進(jìn)程切換更高效片部。
中斷上下文切換
為了響應(yīng)外部設(shè)備或硬件的某些問(wèn)題镣衡,cpu需要及時(shí)處理硬件中斷。中斷的上下文切換同樣不涉及用戶(hù)態(tài)档悠,因此不會(huì)影響到當(dāng)前用戶(hù)態(tài)的虛擬內(nèi)存廊鸥、全局變量,只需要保存當(dāng)前進(jìn)程的用戶(hù)態(tài)狀態(tài)辖所,隨后更新至中斷響應(yīng)程序
的寄存器狀態(tài)惰说、內(nèi)核堆棧、硬件中斷參數(shù)等信息缘回。
中斷處理比進(jìn)程的優(yōu)先級(jí)更高吆视,而且處理時(shí)間相對(duì)較短(打字、鼠標(biāo)點(diǎn)擊)酥宴。
Android中的Context
了解了Linux中的上下文概念后啦吧,我們一起來(lái)看一看Android中的Context“上下文”。