Binder和IPC機(jī)制

進(jìn)程一般指一個(gè)執(zhí)行單元,在電腦和移動(dòng)設(shè)備上指一個(gè)程序或者一個(gè)應(yīng)用。

我們通過系統(tǒng)提供的ContentProvider去查詢數(shù)據(jù)的時(shí)候,其實(shí)也是一種進(jìn)程間通信,只不過通信細(xì)節(jié)被系統(tǒng)內(nèi)部屏蔽了。

給一個(gè)應(yīng)用開啟多進(jìn)程模式——android:process,除此之外沒有其他辦法,也就是說我們無法給一個(gè)線程或者一個(gè)實(shí)體類指定其運(yùn)行時(shí)所在的進(jìn)程。

一個(gè)應(yīng)用默認(rèn)進(jìn)程的進(jìn)程名為包名。

進(jìn)程名為“:”開頭的進(jìn)程屬于當(dāng)前應(yīng)用的私有進(jìn)程,其他應(yīng)用的組件不可以和它跑在同一個(gè)進(jìn)程里,而進(jìn)程名不以“:”開頭的進(jìn)程屬于全局進(jìn)程,其他應(yīng)用通過ShareUID方式可以和它跑在一個(gè)進(jìn)程里。

Android系統(tǒng)為每個(gè)應(yīng)用分配一個(gè)唯一的UID,具有相同UID的應(yīng)用才能共享數(shù)據(jù)。兩個(gè)應(yīng)用通過ShareUID跑在同一個(gè)進(jìn)程中是有要求的,需要這兩個(gè)應(yīng)用有相同的ShareUID并且簽名相同才可以。在這種情況下,他們可以互相訪問對(duì)方的私有數(shù)據(jù),比如data目錄、組件信息等,不管它們是否跑在同一個(gè)進(jìn)程里。如果跑在同一個(gè)進(jìn)程里,除了能共享data數(shù)據(jù)目錄,組件信息,還可以共享內(nèi)存數(shù)據(jù),看上去就像同一個(gè)應(yīng)用的兩個(gè)部分。

ShareUID

android:sharedUserId 即上文中提到的UID和ShareUID是同一個(gè)概念。
Android給每個(gè)app進(jìn)程分配一個(gè)單獨(dú)的用戶空間,其manifest中的userid就是對(duì)應(yīng)一個(gè)Linux用戶Shared User id
通過Shared User id,擁有同一個(gè)User id的多個(gè)app可以配置成運(yùn)行在同一個(gè)進(jìn)程中.所以默認(rèn)就是可以互相訪問任意數(shù)據(jù). 也可以配置成運(yùn)行成不同的進(jìn)程, 同時(shí)可以訪問其他APK的數(shù)據(jù)目錄下的數(shù)據(jù)庫(kù)和文件.就像訪問本程序的數(shù)據(jù)一樣.

多進(jìn)程模式的運(yùn)行機(jī)制和需要注意的問題

靜態(tài)變量不能在多進(jìn)程間同步。
Android為每個(gè)應(yīng)用分配了一個(gè)獨(dú)立的虛擬機(jī),或者說為每個(gè)進(jìn)程都分配了一個(gè)獨(dú)立的虛擬機(jī),不同的虛擬機(jī)在內(nèi)存分配上有不同的地址空間,這就導(dǎo)致了不同的虛擬機(jī)中訪問同一個(gè)類的對(duì)象會(huì)產(chǎn)生多個(gè)副本。
所有運(yùn)行在不同進(jìn)程中的四大組件,只要它們之間需要通過內(nèi)存來共享數(shù)據(jù),都會(huì)共享失敗,這也是多進(jìn)程帶來的主要影響。所以在運(yùn)用IPC時(shí)要考慮共享的是不是內(nèi)存數(shù)據(jù)。
一般來說,使用多進(jìn)程會(huì)造成以下幾方面的問題

  • 靜態(tài)成員和單例模式完全失敗
  • 線程同步機(jī)制完全失敗
  • SharedPreferences的可靠性下降。因?yàn)镾haredPreferences不支持- 兩個(gè)進(jìn)程同時(shí)執(zhí)行寫操作,否則會(huì)有一定幾率的數(shù)據(jù)丟失。
  • Application會(huì)多次創(chuàng)建。運(yùn)行在同一個(gè)進(jìn)程中的組件是屬于同一個(gè)虛擬機(jī)和同一個(gè)Application的,同理,運(yùn)行在不同進(jìn)程里的組件就屬于兩個(gè)不同的虛擬機(jī)和Application。

我們可以這么理解同一個(gè)應(yīng)用間的多進(jìn)程:它就相當(dāng)于兩個(gè)不同的應(yīng)用采用了ShareUID的模式,這樣能夠更加直接的理解多進(jìn)程模式的本質(zhì)。

Serializable和Parcelable

Serializable是Java中的序列化接口,其使用起來簡(jiǎn)單但是開銷很大,序列化和反序列化過程需要大量的I/O操作。
Parcelable是android中的序列化方式,在android平臺(tái)上,效率很高。
Parcelable主要用在內(nèi)存序列化上,通過Parcelable將對(duì)象序列化到存儲(chǔ)設(shè)備中或者將對(duì)象序列化后通過網(wǎng)絡(luò)傳輸也都是可以做到,但是這個(gè)過程會(huì)稍顯復(fù)雜,因此在這兩種情況下建議大家使用Serializable。

關(guān)于Binder

需要額外注意的兩點(diǎn)是:首先,當(dāng)客戶端發(fā)起遠(yuǎn)程請(qǐng)求時(shí),由于當(dāng)前線程會(huì)被掛起,直到服務(wù)端進(jìn)程返回?cái)?shù)據(jù),所以如果一個(gè)遠(yuǎn)程方法是很耗時(shí)的,那么不能在UI線程中發(fā)起此遠(yuǎn)程請(qǐng)求。其次,由于服務(wù)端的Binder方法運(yùn)行在Binder的線程池中,所以Binder方法不管是否耗時(shí)都應(yīng)該采用同步的方式實(shí)現(xiàn),因?yàn)樗呀?jīng)運(yùn)行在一個(gè)線程中了。

Messenger是一種輕量級(jí)的IPC方案,底層實(shí)現(xiàn)是AIDL,由于它一次處理一個(gè)請(qǐng)求,因此在服務(wù)端,我們不用考慮線程同步的問題,因?yàn)榉?wù)端不存在并發(fā)執(zhí)行的情形。Messenger是用串行的方式處理客戶端發(fā)來的請(qǐng)求,如果有大量并發(fā)請(qǐng)求,那么使用Messenger就不太合適了。Messenger的作用主要是為了傳遞消息,很多時(shí)候我們可能需要跨進(jìn)程調(diào)用服務(wù)端的方法,這種情形用Messenger就無法做到了。但是我們可以用AIDL來實(shí)現(xiàn)跨進(jìn)程的方法調(diào)用。

Binder連接池的作用和工作機(jī)制

當(dāng)有多個(gè)業(yè)務(wù)模塊都用到AIDL進(jìn)行跨進(jìn)程通信時(shí),每個(gè)業(yè)務(wù)模塊創(chuàng)建自己的AIDL接口并實(shí)現(xiàn)此接口。這個(gè)時(shí)候不同業(yè)務(wù)模塊之間是不能有耦合的,所有實(shí)現(xiàn)細(xì)節(jié)我們要單獨(dú)開來,然后向服務(wù)端提供自己的唯一標(biāo)識(shí)和其對(duì)應(yīng)的Binder對(duì)象;對(duì)于服務(wù)端而言,只需要一個(gè)Service就可以了,服務(wù)端提供一個(gè)queryBinder接口,這個(gè)接口能夠根據(jù)業(yè)務(wù)模塊的特征來返回相應(yīng)的Binder對(duì)象給它們,不同的業(yè)務(wù)模塊拿到所需的Binder對(duì)象后就可以進(jìn)行遠(yuǎn)程方法調(diào)用了,由此可見,BInder連接池的主要作用就是將每個(gè)業(yè)務(wù)模塊的Binder請(qǐng)求統(tǒng)一轉(zhuǎn)發(fā)給遠(yuǎn)程Service中去執(zhí)行,從而避免了重復(fù)創(chuàng)建Service的過程。

AIDL的oneway

oneway可以用來修飾在interface之前,這樣會(huì)造成interface內(nèi)所有的方法都隱式地帶上oneway;
oneway也可以修飾在interface里的各個(gè)方法之前。
被oneway修飾的方法不可以有返回值。

oneway關(guān)鍵字用于修改遠(yuǎn)程調(diào)用的行為

  • 本地調(diào)用(同步調(diào)用)——如果用于本地調(diào)用,則不會(huì)有任何影響,調(diào)用仍然是同步調(diào)用
  • 遠(yuǎn)程調(diào)用(異步調(diào)用)——使用oneway修飾的方法,遠(yuǎn)程調(diào)用時(shí)不會(huì)阻塞;Client端調(diào)用該方法時(shí),只是發(fā)送數(shù)據(jù)并立即返回。接口的實(shí)現(xiàn)類最終接收此調(diào)用時(shí),是以正常的遠(yuǎn)程調(diào)用形式將其作為常規(guī)的binder調(diào)用來接收。

oneway主要有兩個(gè)特性:異步調(diào)用和串行化處理。

  • 異步調(diào)用是指Client端向binder驅(qū)動(dòng)發(fā)送調(diào)用請(qǐng)求后不需要掛起線程等待binder驅(qū)動(dòng)的回復(fù),而是直接結(jié)束。向一些系統(tǒng)服務(wù)調(diào)用應(yīng)用進(jìn)程的時(shí)候就會(huì)使用oneway,比如AMS調(diào)用應(yīng)用進(jìn)程啟動(dòng)Activity,這樣就算應(yīng)用進(jìn)程中做了耗時(shí)的任務(wù),也不會(huì)阻塞系統(tǒng)服務(wù)的運(yùn)行。
  • 串行化處理是指對(duì)于一個(gè)Server的AIDL接口而言,所有的oneway方法不會(huì)同時(shí)執(zhí)行,binder驅(qū)動(dòng)會(huì)將它們串行化處理,排隊(duì)一個(gè)一個(gè)調(diào)用。
binder的相關(guān)協(xié)議
非oneway方法調(diào)用

如果是 oneway 的話,客戶端就不需要掛起線程等待:
oneway方法調(diào)用

涉及到的 binder 命令也有規(guī)律,由外部發(fā)送給 binder 驅(qū)動(dòng)的都是 BC_ 開頭,由 binder 驅(qū)動(dòng)發(fā)往外部的都是 BR_開頭。

waiteventinterruptible方法

Client端調(diào)用Server端的非oneway方法時(shí),會(huì)造成Client端線程掛起等待遠(yuǎn)程方法返回,這個(gè)等待操作,底層調(diào)用的是Linux系統(tǒng)函數(shù)waiteventinterruptible()方法。相當(dāng)于Thread的sleep方法,調(diào)用后,線程就不會(huì)占用CPU資源。
而用到waiteventinterruptible()方法的地方,還有Handler機(jī)制中,Looper的阻塞,其實(shí)質(zhì)是MessageQueue的next()方法中,調(diào)用了nativePollOnce(),它的底層實(shí)現(xiàn)也調(diào)用了waiteventinterruptible()方法。

Binder機(jī)制的原理

BInder通信機(jī)制采用是的C/S架構(gòu),其中包括4個(gè)主要的角色:Client、Server、ServiceManager和Binder驅(qū)動(dòng)。其中Client、Server和SerivceManager都是在用戶空間,他們都在各自的進(jìn)程,Binder驅(qū)動(dòng)是屬于內(nèi)核空間。關(guān)于內(nèi)核空間和用戶空間,是Linux中的概念,每個(gè)Linux(Android)進(jìn)程默認(rèn)都對(duì)應(yīng)4G的虛擬地址空間,其中0~1G是內(nèi)核空間,1G到4G是用戶空間,每個(gè)進(jìn)程的用戶空間都是相互獨(dú)立的,內(nèi)核空間是可共享的。
Binder機(jī)制的流程可以這樣理解:

  • (1)注冊(cè)服務(wù) Server端注冊(cè)service對(duì)象到ServiceManager,在ServiceManager中生成映射關(guān)系表。這個(gè)過程中,Server相當(dāng)于客戶端,ServiceManager相當(dāng)于服務(wù)端。
  • (2)獲取服務(wù) Client向ServiceManager發(fā)送請(qǐng)求,獲取service對(duì)象,而ServiceManager會(huì)返回一個(gè)service的代理對(duì)象給Client。這個(gè)過程中,Client相當(dāng)于客戶端,ServiceManager相對(duì)于服務(wù)端。
  • (3)Client獲取到service的代理對(duì)象,調(diào)用service代理對(duì)象的對(duì)應(yīng)方法并獲取執(zhí)行結(jié)果,實(shí)現(xiàn)Client和Server的跨進(jìn)程通信。這個(gè)過程中,Client就是客戶端,Server就是服務(wù)端。


    Binder機(jī)制流程

但是以上三個(gè)步驟中的,雙方都不是直接交互的,都是通過Binder驅(qū)動(dòng)實(shí)現(xiàn)IPC通信的過程。比如步驟(2)中,ServiceManager是通過Binder驅(qū)動(dòng)生成service的代理對(duì)象,然后將代理對(duì)象轉(zhuǎn)發(fā)給Client。而在步驟(3)中,Client調(diào)用service代理對(duì)象對(duì)應(yīng)方法時(shí),會(huì)向Binder驅(qū)動(dòng)發(fā)送消息,然后由Binder驅(qū)動(dòng)通知Server端,執(zhí)行service對(duì)象的同名方法,并將執(zhí)行結(jié)果發(fā)送給BInder驅(qū)動(dòng),再將其轉(zhuǎn)發(fā)給Client。從Client端的視角來看,就相當(dāng)于,它直接調(diào)用了service對(duì)象的方法。

Binder機(jī)制實(shí)現(xiàn)IPC的優(yōu)勢(shì):
  • 性能相比于socket、管道和消息隊(duì)列要高。因?yàn)閟ocket、管道和消息隊(duì)列都是采用存儲(chǔ)-轉(zhuǎn)發(fā)方式,即數(shù)據(jù)先從發(fā)送方拷貝到內(nèi)核緩沖區(qū),再?gòu)膬?nèi)核緩沖區(qū)拷貝到接收方。需要兩次拷貝過程。而Binder機(jī)制只需要一次數(shù)據(jù)拷貝。
  • 穩(wěn)定性 Binder基于C/S架構(gòu),架構(gòu)清晰,職責(zé)明確又相對(duì)獨(dú)立。
  • 安全性 傳統(tǒng)的IPC方法,接收端無法獲得發(fā)送端的UID/PID,也就無法鑒別發(fā)送方的身份。而Android系統(tǒng)中給每個(gè)進(jìn)程都分配了UID,可以當(dāng)做鑒別進(jìn)程身份的重要標(biāo)識(shí)。傳統(tǒng)的IPC只能由用戶將UID填寫在數(shù)據(jù)包里,容易被截獲利用。其次,傳統(tǒng)的IPC訪問接入點(diǎn)是開放的,只要知道接入點(diǎn),就可以與對(duì)端建立連接,這樣就容易被惡意程序猜測(cè)到接入點(diǎn)而與接收方建立連接。同時(shí),Binder既支持實(shí)名Binder又支持匿名BInder,安全性高。
binder機(jī)制基于mmap一次拷貝的實(shí)現(xiàn)原理
mmap

Client端和Server端處于不同的進(jìn)程有些不同的虛擬地址規(guī)則,所以無法直接通信,而一個(gè)頁(yè)框可以映射給多個(gè)頁(yè),那么就可以將一塊物理內(nèi)存分別與Client和Server的虛擬內(nèi)存塊進(jìn)行映射,如圖所示,Client向Server發(fā)消息,只需調(diào)用copy_from_user進(jìn)行一次數(shù)據(jù)拷貝,Server進(jìn)程就能讀取到數(shù)據(jù)了,另外映射的虛擬內(nèi)存塊大小將近1M(1M~8K),所以IPC通信的數(shù)據(jù)量也被限制為這個(gè)值。通過Intent向Acivity或者Service等組件傳參時(shí),也因?yàn)檫@個(gè)映射內(nèi)存的大小問題,也會(huì)影響到Intent傳遞數(shù)據(jù)的大小。

通過Intent傳遞數(shù)據(jù)的大小具體限制
1.1傳512K以下的數(shù)據(jù)的數(shù)據(jù)可以正常傳遞。
1.2傳512K~1024K的數(shù)據(jù)會(huì)出錯(cuò),閃退。
1.3傳1024K以上的數(shù)據(jù)會(huì)報(bào)錯(cuò):TransactionTooLargeException。
1.4考慮到Intent還包括要啟動(dòng)的Activity等信息,實(shí)際可以傳的數(shù)據(jù)略小于512K
原文鏈接:https://blog.csdn.net/pingfangx/article/details/52093225

怎么理解頁(yè)和頁(yè)框
頁(yè)框是指一塊實(shí)際的物理內(nèi)存,頁(yè)是指程序的一塊內(nèi)存數(shù)據(jù)單元,內(nèi)存數(shù)據(jù)一定是存儲(chǔ)在實(shí)際的物理內(nèi)存上,即頁(yè)必然對(duì)應(yīng)于一個(gè)頁(yè)框,頁(yè)數(shù)據(jù)實(shí)際上是存儲(chǔ)在頁(yè)框上的。
頁(yè)和頁(yè)框一樣大,都是內(nèi)核對(duì)內(nèi)存的分塊單位。一個(gè)頁(yè)框可以映射給多個(gè)頁(yè),也就是說一塊實(shí)際的物理存儲(chǔ)空間可以映射給多個(gè)進(jìn)程的多個(gè)虛擬內(nèi)存空間,這也是mmap機(jī)制依賴的基礎(chǔ)規(guī)則。

Binder的整體架構(gòu)
Binder的整體架構(gòu)

Client通過ServiceManager或者AMS獲取到的遠(yuǎn)程binder實(shí)體,一般會(huì)用Proxy做一層封裝,比如ServiceManagerProxy、AIDL生成的Proxy類。而被封裝的遠(yuǎn)程binder實(shí)體是一個(gè)BInderProxy。
BpBinder和BinderProxy其實(shí)是一個(gè)東西,遠(yuǎn)程binder實(shí)體,只不過一個(gè)Native層,一個(gè)Java層。BpBinder內(nèi)部持有一個(gè)binder句柄值handle。
ProcessState是進(jìn)程單例,負(fù)責(zé)啟動(dòng)binder驅(qū)動(dòng)以及mmap;IPCThreadState是線程單例,負(fù)責(zé)與binder驅(qū)動(dòng)進(jìn)行具體的命令通信。
由Client端通過Proxy發(fā)起遠(yuǎn)程方法調(diào)用,會(huì)將數(shù)據(jù)打包到Parcel中,層層向下調(diào)用到BpBinder,在BpBinder中調(diào)用IPCThreadState的transact()方法并傳入handle句柄值,IPCThreadState再去執(zhí)行具體的binder命令。
由binder驅(qū)動(dòng)到Server端的大概流程是:Server端通過IPCThreadState接收到Client端的請(qǐng)求后,層層向上,最后回調(diào)Stub的onTransact() 方法。
以上不代表所有IPC的流程,比如Service Manager作為Server端時(shí),便沒有上層的封裝,也沒有借助IPCThreadState,而是初始化后通過binder_loop()方法直接和binder驅(qū)動(dòng)通信的。

本文參考:
https://blog.csdn.net/qq_42300133/article/details/103402635
https://blog.csdn.net/zhwadezh/article/details/79301909?depth_1-utm_source=distribute.pc_relevant_right.none-task&utm_source=distribute.pc_relevant_right.none-task
https://mp.weixin.qq.com/s/Mp8A1VreFsuj2USbyHRZjw
https://blog.csdn.net/u011033906/article/details/89316543
https://mp.weixin.qq.com/s/Jc2mrxeMVTJXudoPx5K4-w
https://blog.csdn.net/anlian523/article/details/98476033
https://blog.csdn.net/u010164190/article/details/73292012

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容