面試的時候,面試官問你說,簡單說一下Android的Binder機制,你會怎麼回答?
我想,我會這麼說。
在Android啓動的時候,Zygote進程孵化出第一個子進程叫SystemServer,而在這個進程中,很多系統提供的服務,比如ActivityManagerSerivce, PowerManagerService等,都在此進程中的某一條線程上運行。
而很多用戶開發的應用程序,也就是我們常說的APP,基於安全的考慮,在安裝到系統中的時候,都會被分配一個獨特的UID,運行在一個單獨的進程中。
基於Linux的用戶安全機制,它們是沒有辦法直接訪問到上述系統服務的。
但是在實際中,App很多時候是需要跟這些系統服務進行通信的,也就是要進行進程間通信。
而Binder機制則是Android系統實現進程間通信(IPC)的機制之一。
面試官可能還會問,那麼Binder機制是怎麼實現的呢?
我覺得,要想知道Binder機制是怎麼實現的,首先要知道下面四個概念:
1)Server
2)Client
3)ServiceManager
4)Binder驅動
Server和Client無需多說,我們只需要知道一個是用來提供服務的,一個是獲取服務的,並且分別運行在不同的進程當中。
那麼ServiceManager是用來幹什麼的呢?
在前面一篇文章,關於Android的啓動過程中,我們有提到,ServiceManager是和Zygote進程一樣,是Android系統的守護進程之一。
它的主要作用,就是幫助系統去維護衆多的Service列表,所以叫Service Manager。
它就像個電話本,你要打電話給某個人問點事情(想要獲取某種Service),但是你沒有這個人的電話(不知道Service的訪問點,只知道名字),於是你先找到電話本(ServiceManager),在電話本上找到這個人的電話(通過名字找到電話),然後你才能打電話給他(開始獲取Service)。
我們知道,ServiceManager,也是一個單獨的進程。要找到ServiceManager,本身也要涉及到進程間通信啊,不是嗎?
是的。
從Client和Server的角度來看,ServiceManager永遠都是一個Server,任何訪問ServiceManager的都是Client。
我們先想一想,ServiceManager本身就是一個進程,那爲什麼這個進程就能夠成爲ServiceManager呢?
回想一下,在Android啓動ServiceManager進程的時候,都做了什麼事?
1)首先打開“/dev/binder”設備文件,映射內存
2)利用BINDER_SET_CONTEXT_MGR命令,令自己成爲上下文管理者,其實也就是成爲ServiceManager。
3)進入一個無限循環,等待Client的請求到來。
所以,是通過Binder驅動,通過命令”BINDER_SET_CONTEXT_MGR”,讓某個進程成爲ServiceManager的。
面試官這時候,又適當的提了一個問題,Binder驅動是用來幹什麼的?它跟網卡驅動,聲卡驅動一樣的嗎?
不一樣。
Binder驅動其實跟硬件沒有什麼關係,它就是一段運行在內核空間的代碼,通過一個叫”/dev/binder”的文件在內核空間和用戶空間來回搬數據。
它是整個Binder機制的核心。正是通過它,Binder才能實現進程間的通信。
因爲Server, Client和ServiceManager是運行在用戶空間,不同的進程,彼此是不能互相訪問的。
那麼在不同的進程間,要進行數據的傳遞,只有通過內核空間來傳遞數據,
而只有Binder驅動是工作在內核空間中的。
它負責Binder節點的建立,負責Binder在進程間的傳遞,負責對Binder引用的計數管理,在不同進程間傳輸數據包等底層操作。
整個進程間通信的大部分工作都是通過 open(), mmap()和 ioctl()等文件操作,在內核空間與用戶空間中進進出出,來實現的。
面試官接着問,那”BINDER_SET_CONTEXT_MGR”又是怎麼回事?
是這樣的。
ServiceManager進程啓動的時候,此時它還不是ServiceManager呢。
不過,當它打開了/dev/binder文件,將BINDER_SET_CONTEXT_MGR命令傳給Binder驅動的時候,Binder驅動就會爲其在內核空間中創建一個節點(binder_node),
這個節點就是binder_context_mgr_node,也就是ServiceManager的Binder實體。
而這個節點所在的進程,就是ServiceManager。
面試官突然間插了一句話,“那麼,你怎麼知道ServiceManager在哪的?“
在整個系統中,只會有一個binder_context_mgr_node,所以也只會有一個ServiceManager的進程,那麼對ServiceManager的訪問,驅動就可以在系統中定義好其句柄,也就是 0。
所以只要是想找ServiceManager的進程,只要告訴Binder驅動,想訪問的是句柄爲 0的進程,Binder驅動就知道,它們是在找ServiceManager。
它就會喚醒ServiceManager,讓它接受訪問。
所以,”BINDER_SET_CONTEXT_MGR” 就是某某進程告訴驅動,它想成爲ServiceManager。而只要它是第一個發送這個命令的,它就是ServiceManager了。
面試官好像稍微明白了一點,不過他想了一想,又問:”什麼是句柄?“
呃,這個我也不知道怎麼說,句柄理解上應該就是一個區分不同服務的標識吧。不過在Android系統中,只有跨進程的通信,纔會用到句柄這個術語,因爲沒有辦法直接訪問。
而如果服務運行在本地進程中,由於共享同一個進程空間,是可以直接訪問的,這個時候就不用句柄來描述了,而稱之爲引用。
所以,句柄與引用的區別,就在於一個是遠程訪問(跨進程),一個是本地訪問(相同進程)。
面試官若有所思地看着面前這個人,目光中好像有那麼點懷疑的樣子,不過也就沒再繼續問下去。
但是他突然間又說了一句話,”你還沒有回答我,Binder的機制是怎麼實現的呢,先簡單地說一下?“
通過上面的簡單描述,我們可以這樣認爲,每一個提供服務的Server都會通過Binder驅動,將自身給註冊到ServiceManager中,方便衆多想獲取服務的Client可以去ServiceManager找到自己。
那麼,這些Service都會經過內核空間的Binder驅動,其實這個”經過”的說法,本質上,就是Server們會將自身作爲一個對象,封裝在數據包中,將這些數據複製到內核空間中,由Binder驅動訪問。
而Binder驅動讀取數據包的時候,如果發現其中有Binder實體,類似ServiceManager那樣的服務提供商,那麼也會爲對應的Binder實體創建對應的Binder節點(BinderNode)。
這些節點位於Server所屬的進程內。
Binder驅動也會爲這些服務分配句柄(大於0),同時會將這些句柄也記錄在Binder驅動中,然後再將這些句柄和名字發送給ServiceManager,由ServiceManager來維護。
也就是說,Server們通過和Binder驅動通信,Binder驅動做了如下兩件事:
1)在內核空間中創建了一個Binder節點,隸屬於Server們的進程。
2)驅動爲這些Binder節點分配一個大於0的包柄,將這些句柄和名字發送給ServiceManager。
那麼Client們是怎麼和Binder驅動通信的呢?
很顯然,無論如何,Client得知道要跟誰通信,它們知道一個名字。
所以,
1)它們就會把想要獲取服務的名字,加上一個句柄爲 0 的值,封裝爲一個數據包,打開Binder設備文件,將這個數據發送給Binder驅動。
2)Binder驅動接收到句柄爲0,就會將這數據包扔給ServiceManager。
3)ServiceManager接收到這個數據包,就會分析,發現是要找某個名字的服務,於是就找找找,然後將對應服務的句柄發送回來(某大於0的句柄)。
4)驅動再將這個句柄發回給Client。
5)Client獲取句柄之後,就會再加上想要的服務,還有這個句柄,再發送給Binder驅動,Binder驅動就會找到對應的句柄,然後。。。。
面試官靜靜地坐在那裏,面無表情,也不知道在想什麼,最後說了一句,”先這樣,先回去吧,有結果會通知你的。“