Framework和Binder的內(nèi)容挺深的,本文還是站在應(yīng)用層開(kāi)發(fā)者的角度來(lái)建立基本認(rèn)知,能在遇到問(wèn)題的時(shí)候有思路和方向即可。(本文將帶著關(guān)鍵問(wèn)題和核心流程展開(kāi),不會(huì)面面俱到)
大綱:
- 背景
- 為什么要多進(jìn)程
- 為什么要Binder
- Binder簡(jiǎn)單架構(gòu)
- 簡(jiǎn)單示例
- 源碼分析
- 客戶端與驅(qū)動(dòng)交互
- 服務(wù)端與驅(qū)動(dòng)交互
- 總結(jié)
- 細(xì)節(jié)補(bǔ)充
- Binder為什么高效
- Binder為什么不用shm
- 提問(wèn)
- 參考資料
本文約4.0k字,閱讀大約17分鐘。
Android源碼基于8.0。
背景
為什么要多進(jìn)程
Binder是Android系統(tǒng)的一種跨進(jìn)程通信(IPC)機(jī)制。
在Android系統(tǒng)中,單個(gè)進(jìn)程被分配了有限的內(nèi)存,多進(jìn)程可以使用更多內(nèi)存、隔離崩潰風(fēng)險(xiǎn)等。
多進(jìn)程在Android中常見(jiàn)的使用場(chǎng)景有獨(dú)立進(jìn)程的WebView、推送、?;睢⑾到y(tǒng)服務(wù)等,既然是多進(jìn)程場(chǎng)景,那么就需要跨進(jìn)程通信了。
為什么要Binder
Linux自帶了一些跨進(jìn)程通信方式:
管道(pipe):管道描述符是半雙工,單向的,數(shù)據(jù)只能往一個(gè)方向流,想要讀寫(xiě)需要兩個(gè)管道描述符。Linux提供了pipe(fds)來(lái)獲取一對(duì)描述符,一個(gè)讀一個(gè)寫(xiě)。匿名管道只能用在具有親緣關(guān)系的父子進(jìn)程間的通信,有名管道無(wú)此限制。
Socket:全雙工,可讀可寫(xiě)。如Zygote進(jìn)程等待AMS系統(tǒng)服務(wù)發(fā)起socket請(qǐng)求來(lái)創(chuàng)建應(yīng)用進(jìn)程。
共享內(nèi)存(shm,Shared Memory):會(huì)映射一段能被多個(gè)進(jìn)程訪問(wèn)的內(nèi)存,是最高效的IPC方式,他通常需要結(jié)合其他跨進(jìn)程方式如信號(hào)量來(lái)同步信息。Android基于shm改進(jìn)得到匿名共享內(nèi)存Ashmem(Anonymous Shared Memory),因高效而適合處理較大的數(shù)據(jù),如應(yīng)用進(jìn)程通過(guò)共享內(nèi)存來(lái)讀取SurfaceFlinger進(jìn)程合成的視圖數(shù)據(jù),進(jìn)行展示。
內(nèi)存映射(mmap):Linux通過(guò)將一個(gè)虛擬內(nèi)存區(qū)域與一個(gè)磁盤(pán)上的文件關(guān)聯(lián)起來(lái),以初始化這個(gè)虛擬內(nèi)存區(qū)域的內(nèi)容。通過(guò)指針的方式讀寫(xiě)內(nèi)存,系統(tǒng)會(huì)同步進(jìn)對(duì)應(yīng)的磁盤(pán)文件。Binder用到了mmap。
信號(hào)(signal):?jiǎn)蜗虻模l(fā)個(gè)信號(hào)就完事,無(wú)返回結(jié)果。只能發(fā)信號(hào),帶不了參數(shù)。如子進(jìn)程被殺掉后系統(tǒng)會(huì)發(fā)出SIGCHLD信號(hào),父進(jìn)程會(huì)清理子進(jìn)程在進(jìn)程表的描述信息防止
僵尸進(jìn)程的發(fā)生。
另外還有文件共享、消息隊(duì)列(Message)等跨進(jìn)程通信方式...
這些跨進(jìn)程通信方式都各有優(yōu)劣,Android最終選擇了自建一套兼顧好用、高效、安全的Binder。
- 好用:易用的C/S架構(gòu)(借助AIDL后只需編寫(xiě)業(yè)務(wù)邏輯)
- 高效:用mmap進(jìn)行內(nèi)存映射,只需一次拷貝
- 安全:內(nèi)核態(tài)管理身份標(biāo)記,每個(gè)App有UID來(lái)校驗(yàn)權(quán)限,同時(shí)支持實(shí)名(系統(tǒng)服務(wù))和匿名(自己創(chuàng)建的服務(wù))
Binder簡(jiǎn)單架構(gòu)
Linux內(nèi)存被分為用戶空間和內(nèi)核空間,用戶空間需要經(jīng)過(guò)系統(tǒng)調(diào)用才能訪問(wèn)到內(nèi)核空間。

(圖片來(lái)源:「寫(xiě)給Android應(yīng)用工程師的Binder原理剖析」)
Binder整體基于C/S架構(gòu)。運(yùn)行在內(nèi)核空間的Binder驅(qū)動(dòng)程序,會(huì)為用戶空間暴露出一個(gè)設(shè)備文件/dev/binder,進(jìn)程間通過(guò)該文件來(lái)建立通信通道。

Binder的啟動(dòng)過(guò)程:
- 打開(kāi)binder驅(qū)動(dòng)(open)
- 將驅(qū)動(dòng)文件的描述符(mDriverFD)進(jìn)行內(nèi)存映射(mmap),分配緩沖區(qū)
- 服務(wù)端運(yùn)行binder線程,把線程注冊(cè)到binder驅(qū)動(dòng),進(jìn)入循環(huán)等待客戶端的指令(兩端通過(guò)ioctl與驅(qū)動(dòng)交互)
簡(jiǎn)單示例
AIDL(Android接口定義語(yǔ)言)可以輔助生成Binder的Java類(lèi),減少重復(fù)工作,使用姿勢(shì)網(wǎng)上有很多,這里就直接手寫(xiě)吧,方便理解。
示例調(diào)用流程如下:

代碼不多,大部分是log,重點(diǎn)看注釋就行。
客戶端Activity:
//NoAidlActivity.java
protected void onCreate(Bundle savedInstanceState) {
Intent intent = new Intent(this, MyService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//1. 從對(duì)象池拿到可復(fù)用的對(duì)象(享元模式)
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
Log.e("哈利迪", "--- 我是客戶端 NoAidlActivity , pid = "
+ Process.myPid() + ", thread = "
+ Thread.currentThread().getName());
String str = "666";
Log.e("哈利迪", "客戶端向服務(wù)端發(fā)送:" + str);
//2. 往data寫(xiě)數(shù)據(jù),作為請(qǐng)求參數(shù)
data.writeString(str);
//3. 拿到服務(wù)端的IBinder句柄,調(diào)用transact
//約定行為碼是1;需要服務(wù)端的返回值,所以flags傳0表示同步調(diào)用
service.transact(1, data, reply, 0);
Log.e("哈利迪", "--- 我是客戶端 NoAidlActivity , pid = "
+ Process.myPid() + ", thread = "
+ Thread.currentThread().getName());
//4. 從reply讀取服務(wù)端的返回值
Log.e("哈利迪", "客戶端接收服務(wù)端返回:" + reply.readString());
}
}, Context.BIND_AUTO_CREATE);
}
service.transact傳入了flags為0,表示同步調(diào)用,會(huì)阻塞等待服務(wù)端的返回值。如果服務(wù)端進(jìn)行了耗時(shí)操作,此時(shí)用戶操作UI則會(huì)引起ANR。
flags的另一個(gè)值是1,表示異步調(diào)用的one way,不需要等待服務(wù)端的返回結(jié)果,先忽略。
來(lái)看服務(wù)端運(yùn)行的Service,
class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
//返回服務(wù)端的IBinder句柄
return new MyBinder();
}
}
注冊(cè)服務(wù),讓服務(wù)端Service運(yùn)行在:remote進(jìn)程,來(lái)實(shí)現(xiàn)跨進(jìn)程,
<service
android:name=".binder.no_aidl.MyService"
android:process=":remote" />
運(yùn)行在服務(wù)端的Binder對(duì)象,
class MyBinder extends Binder {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags){
if (code == 1) {//如果是約定好的行為碼1
Log.e("哈利迪", "--- 我是服務(wù)端 MyBinder , pid = "
+ Process.myPid() + ", thread = "
+ Thread.currentThread().getName());
//1. 從data讀取客戶端參數(shù)
Log.e("哈利迪", "服務(wù)端收到:" + data.readString());
String str = "777";
Log.e("哈利迪", "服務(wù)端返回:" + str);
//2. 從reply向客戶端寫(xiě)返回值
reply.writeString(str);
//3. 處理完成
return true;
}
return super.onTransact(code, data, reply, flags);
}
}
運(yùn)行如下,7行日志:

由于我們的flags傳入的是0同步調(diào)用,可以試著在服務(wù)端onTransact里sleep幾秒,會(huì)發(fā)現(xiàn)客戶端需要幾秒后才能打印出返回值。所以如果服務(wù)端需要進(jìn)行耗時(shí)操作,客戶端則需要在子線程里進(jìn)行binder調(diào)用。
延伸:從
IT互聯(lián)網(wǎng)大叔的「android獲取進(jìn)程名函數(shù),如何優(yōu)化到極致」一文可見(jiàn),在使用系統(tǒng)API時(shí),如果有更好的方案,還是建議將跨進(jìn)程方案getSystemService放到最后作為兜底,因?yàn)樗枰腷inder調(diào)用本身有開(kāi)銷(xiāo),而且作為應(yīng)用層開(kāi)發(fā)者也很少會(huì)去關(guān)注遠(yuǎn)方進(jìn)程的內(nèi)部實(shí)現(xiàn),萬(wàn)一對(duì)方有潛在的耗時(shí)操作呢?
通過(guò)這個(gè)例子,我們可以看出,Binder機(jī)制使用了Parcel來(lái)序列化數(shù)據(jù),客戶端在主線程調(diào)用了transact來(lái)請(qǐng)求(Parcel data傳參),服務(wù)端在Binder線程調(diào)用onTransact來(lái)響應(yīng)(Parcel reply回傳結(jié)果)。
源碼分析
Binder的調(diào)用流程大致如下,native層BpBinder的Bp指的是Binder proxy,

可見(jiàn),需要經(jīng)過(guò)如下調(diào)用才能完成一次通信:
- 請(qǐng)求:客戶端Java層->客戶端native層->Binder驅(qū)動(dòng)層->服務(wù)端native層->服務(wù)端Java層
- 響應(yīng):服務(wù)端Java層->服務(wù)端native層->Binder驅(qū)動(dòng)層->客戶端native層->客戶端Java層
即Binder驅(qū)動(dòng)層充當(dāng)著一個(gè)中轉(zhuǎn)站的作用,有點(diǎn)像網(wǎng)絡(luò)分層模型。
客戶端與驅(qū)動(dòng)交互
先來(lái)看客戶端與驅(qū)動(dòng)的交互。因?yàn)槭强邕M(jìn)程調(diào)用(指定了:remote),示例里onServiceConnected回調(diào)回來(lái)的service對(duì)象是個(gè)BinderProxy代理實(shí)例(不跨進(jìn)程的話會(huì)發(fā)生遠(yuǎn)程轉(zhuǎn)本地,后面講),我們以service.transact(1, data, reply, 0)這行調(diào)用作為入口跟進(jìn)。
BinderProxy類(lèi)寫(xiě)在Binder類(lèi)文件里面:
//BinderProxy.java
public boolean transact(int code, Parcel data, Parcel reply, int flags){
//調(diào)用了native方法
return transactNative(code, data, reply, flags);
}
這個(gè)native方法在android_util_Binder.cpp里注冊(cè),
//android_util_Binder.cpp
//JNI注冊(cè)
static const JNINativeMethod gBinderProxyMethods[] = {
{ "transactNative",
"(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z",
(void*)android_os_BinderProxy_transact},
};
//native方法具體實(shí)現(xiàn)
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags){
//轉(zhuǎn)成native層的Parcel
Parcel* data = parcelForJavaObject(env, dataObj);
Parcel* reply = parcelForJavaObject(env, replyObj);
//拿到native層的句柄BpBinder
IBinder* target = (IBinder*)
env->GetLongField(obj, gBinderProxyOffsets.mObject);
//調(diào)用BpBinder的transact
status_t err = target->transact(code, *data, reply, flags);
}
繼續(xù)跟BpBinder.cpp,
//BpBinder.cpp
status_t BpBinder::transact(...){
//交給線程單例處理,驅(qū)動(dòng)會(huì)根據(jù)mHandle值來(lái)找到對(duì)應(yīng)的binder句柄
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
}
IPCThreadState是一個(gè)線程單例,負(fù)責(zé)與binder驅(qū)動(dòng)進(jìn)行具體的指令通信,跟進(jìn)IPCThreadState.cpp,
//IPCThreadState.cpp
status_t IPCThreadState::transact(...){
//將數(shù)據(jù)寫(xiě)入mOut,見(jiàn)1.1
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
//...先忽略one way異步調(diào)用的代碼,只看有返回值的同步調(diào)用
//跟binder驅(qū)動(dòng)交互,傳入reply接收返回?cái)?shù)據(jù),見(jiàn)1.2
err = waitForResponse(reply);
}
//1.1 將數(shù)據(jù)寫(xiě)入mOut
status_t IPCThreadState::writeTransactionData(...)
{
binder_transaction_data tr;
//...打包各種數(shù)據(jù)(data size、buffer、offsets)
tr.sender_euid = 0;
//將BC_TRANSACTION指令寫(xiě)入mOut
mOut.writeInt32(cmd);
//將打包好的binder_transaction_data寫(xiě)入mOut
mOut.write(&tr, sizeof(tr));
}
//1.2 跟binder驅(qū)動(dòng)交互,傳入reply接收返回?cái)?shù)據(jù)
status_t IPCThreadState::waitForResponse(...){
//這個(gè)循環(huán)很重要,客戶端就是在這里休眠等待服務(wù)端返回結(jié)果的
while (1) {
//跟驅(qū)動(dòng)進(jìn)行數(shù)據(jù)交互,往驅(qū)動(dòng)寫(xiě)mOut,從驅(qū)動(dòng)讀mIn,見(jiàn)1.3
talkWithDriver();
//讀取驅(qū)動(dòng)回復(fù)的指令
cmd = (uint32_t)mIn.readInt32();
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
//表示驅(qū)動(dòng)已經(jīng)收到客戶端的transact請(qǐng)求
//如果是one way異步調(diào)用,到這就可以結(jié)束了
if (!reply && !acquireResult) goto finish;
break;
case BR_REPLY:
//表示客戶端收到服務(wù)端的返回結(jié)果
binder_transaction_data tr;
//把服務(wù)端的數(shù)據(jù)讀出來(lái),打包進(jìn)tr
err = mIn.read(&tr, sizeof(tr));
//再把tr的數(shù)據(jù)透?jìng)鬟M(jìn)reply
reply->ipcSetDataReference(...);
//結(jié)束
goto finish;
}
}
}
//1.3 跟驅(qū)動(dòng)進(jìn)行數(shù)據(jù)交互,往驅(qū)動(dòng)寫(xiě)mOut,從驅(qū)動(dòng)讀mIn
status_t IPCThreadState::talkWithDriver(bool doReceive){
binder_write_read bwr;
//指定寫(xiě)數(shù)據(jù)大小和寫(xiě)緩沖區(qū)
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
//指定讀數(shù)據(jù)大小和讀緩沖區(qū)
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
//ioctl的調(diào)用進(jìn)入了binder驅(qū)動(dòng)層的binder_ioctl
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr);
if (bwr.write_consumed > 0) {
//數(shù)據(jù)已經(jīng)寫(xiě)入驅(qū)動(dòng),從mOut移除
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) {
//從驅(qū)動(dòng)讀出數(shù)據(jù)存入mIn
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
}
ioctl的調(diào)用進(jìn)入了binder驅(qū)動(dòng)層的binder_ioctl,驅(qū)動(dòng)層的代碼先不跟。
服務(wù)端與驅(qū)動(dòng)交互
從「一圖摸清Android應(yīng)用進(jìn)程的啟動(dòng)」一文可知,服務(wù)端創(chuàng)建了一個(gè)線程注冊(cè)進(jìn)binder驅(qū)動(dòng),即binder線程,在ProcessState.cpp,
//ProcessState.cpp
virtual bool threadLoop()
{ //把binder線程注冊(cè)進(jìn)binder驅(qū)動(dòng)程序的線程池中
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
跟進(jìn)IPCThreadState.cpp,
//IPCThreadState.cpp
void IPCThreadState::joinThreadPool(bool isMain){
//向binder驅(qū)動(dòng)寫(xiě)數(shù)據(jù),表示當(dāng)前線程需要注冊(cè)進(jìn)binder驅(qū)動(dòng)
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
status_t result;
do {
//進(jìn)入死循環(huán),等待指令的到來(lái),見(jiàn)1.1
result = getAndExecuteCommand();
} while (result != -ECONNREFUSED && result != -EBADF);
//向binder驅(qū)動(dòng)寫(xiě)數(shù)據(jù)(退出循環(huán),線程結(jié)束)
mOut.writeInt32(BC_EXIT_LOOPER);
}
//1.1 等待指令的到來(lái)
status_t IPCThreadState::getAndExecuteCommand(){
//跟驅(qū)動(dòng)進(jìn)行數(shù)據(jù)交互,驅(qū)動(dòng)會(huì)把指令寫(xiě)進(jìn)mIn
talkWithDriver();
//從mIn讀出指令
cmd = mIn.readInt32();
//執(zhí)行指令,見(jiàn)1.2
result = executeCommand(cmd);
return result;
}
//1.2 執(zhí)行指令
status_t IPCThreadState::executeCommand(int32_t cmd){
//客戶端發(fā)請(qǐng)求到驅(qū)動(dòng),驅(qū)動(dòng)轉(zhuǎn)發(fā)到服務(wù)端
switch ((uint32_t)cmd) {
case BR_TRANSACTION:{
//服務(wù)端收到BR_TRANSACTION指令
binder_transaction_data tr;
//讀出客戶端請(qǐng)求的參數(shù)
result = mIn.read(&tr, sizeof(tr));
//準(zhǔn)備數(shù)據(jù),向上傳給Java層
Parcel buffer; Parcel reply;
buffer.ipcSetDataReference(...);
//cookie保存的是binder實(shí)體,對(duì)應(yīng)服務(wù)端的native層對(duì)象就是BBinder
reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
//服務(wù)端向驅(qū)動(dòng)寫(xiě)返回值,讓驅(qū)動(dòng)轉(zhuǎn)發(fā)給客戶端
sendReply(reply, 0);
}
}
}
//1.3 服務(wù)端向驅(qū)動(dòng)寫(xiě)返回值,讓驅(qū)動(dòng)轉(zhuǎn)發(fā)給客戶端
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags){
err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
//服務(wù)端返回結(jié)果給客戶端就行,不用等待客戶端,所以傳NULL
return waitForResponse(NULL, NULL);
}
然后看下BBinder的transact是怎么向上傳遞到Java層的,在Binder.cpp中,
//Binder.cpp
status_t BBinder::transact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags){
switch (code) {
//ping指令用來(lái)判斷連通性,即binder句柄是否還活著
case PING_TRANSACTION:
reply->writeInt32(pingBinder());
break;
default:
//看這,通過(guò)JNI調(diào)用到Java層的execTransact,見(jiàn)1.1
err = onTransact(code, data, reply, flags);
break;
}
return err;
}
//android_util_Binder.cpp
//1.1 通過(guò)JNI調(diào)用到Java層的execTransact
virtual status_t onTransact(...){
JNIEnv* env = javavm_to_jnienv(mVM);
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, ...);
}
回到Java層,execTransact如下:
//android.os.Binder.java
private boolean execTransact(...) {
res = onTransact(code, data, reply, flags);
}
至此就回調(diào)到了示例代碼中服務(wù)端MyBinder的onTransact了,我們?cè)谑纠刑幚碚?qǐng)求參數(shù)data和返回值reply,最后由native層的sendReply(reply, 0)真正向驅(qū)動(dòng)寫(xiě)返回值,讓驅(qū)動(dòng)轉(zhuǎn)發(fā)給客戶端。
將調(diào)用代碼和流程圖結(jié)合起來(lái):

然后是指令交互圖(非one way模式):

binder同步調(diào)用等到服務(wù)端的BR_REPLY指令后就真正結(jié)束,服務(wù)端則繼續(xù)循環(huán),等待下一次請(qǐng)求。
總結(jié)
本文主要介紹了Binder的背景和調(diào)用流程,將留下3個(gè)疑問(wèn)繼續(xù)探討。
- binder句柄是怎么傳輸和管理的(binder驅(qū)動(dòng)和ServiceManager進(jìn)程)
- binder句柄的
遠(yuǎn)程轉(zhuǎn)本地 - one way異步模式和他的串行調(diào)用(async_todo)、同步模式的并行調(diào)用
系列文章:
- 圖解 | Android系統(tǒng)的啟動(dòng)
- 圖解 | 一圖摸清Android系統(tǒng)服務(wù)
- 圖解 | 一圖摸清Android應(yīng)用進(jìn)程的啟動(dòng)
細(xì)節(jié)補(bǔ)充
Binder為什么高效
Linux用戶空間是無(wú)法直接讀寫(xiě)磁盤(pán)的,系統(tǒng)所有的資源管理(讀寫(xiě)磁盤(pán)文件、分配回收內(nèi)存、從網(wǎng)絡(luò)接口讀寫(xiě)數(shù)據(jù))都是在內(nèi)核空間完成的,用戶空間需要通過(guò)系統(tǒng)調(diào)用讓內(nèi)核空間完成這些功能。
傳統(tǒng)IPC傳輸數(shù)據(jù):發(fā)送進(jìn)程需要copy_from_user從用戶到內(nèi)核,接收進(jìn)程再copy_to_uer從內(nèi)核到用戶,兩次拷貝。
而B(niǎo)inder傳輸數(shù)據(jù):用mmap將binder內(nèi)核空間的虛擬內(nèi)存和用戶空間的虛擬內(nèi)存映射到同一塊物理內(nèi)存。copy_from_user將數(shù)據(jù)從發(fā)送進(jìn)程的用戶空間拷貝到接收進(jìn)程的內(nèi)核空間(一次拷貝),接收進(jìn)程通過(guò)映射關(guān)系能直接在用戶空間讀取內(nèi)核空間的數(shù)據(jù)。

(圖片來(lái)源:「寫(xiě)給Android應(yīng)用工程師的Binder原理剖析」)
Binder為什么不用shm
shm通常需要結(jié)合其他跨進(jìn)程方式如信號(hào)量來(lái)同步信息,使用沒(méi)有mmap方便。
提問(wèn)
- 上期提問(wèn): SurfaceFlinger進(jìn)程為什么不是通過(guò)Zygote進(jìn)程的fork創(chuàng)建,而是由init進(jìn)程創(chuàng)建?
參考資料
- 書(shū)籍 - Android系統(tǒng)源代碼情景分析
- 博客 - 王小二的Android站
- 博客 - 寫(xiě)給Android應(yīng)用工程師的Binder原理剖析
- 博客 - Binder傳輸機(jī)制篇_中
- 博客 - 共享內(nèi)存和文件內(nèi)存映射的區(qū)別