天天看點

跟面試官講Binder(零)

面試的時候,面試官問你說,簡單說一下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驅動就會找到對應的句柄,然後。。。。

面試官靜靜地坐在那裡,面無表情,也不知道在想什麼,最後說了一句,”先這樣,先回去吧,有結果會通知你的。“