跟面試官講Binder(零)

原文地址 http://blog.csdn.net/linmiansheng/article/details/37918333

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


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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章